diff --git a/securityquiz/settings.py b/securityquiz/settings.py index 8cf23fd..e33f548 100644 --- a/securityquiz/settings.py +++ b/securityquiz/settings.py @@ -41,7 +41,9 @@ INSTALLED_APPS = ( 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'quiz' + 'quiz', + 'oauth2_provider', + 'corsheaders', ) MIDDLEWARE_CLASSES = ( @@ -51,8 +53,11 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'corsheaders.middleware.CorsMiddleware', ) +CORS_ORIGIN_ALLOW_ALL = True + ROOT_URLCONF = 'securityquiz.urls' WSGI_APPLICATION = 'securityquiz.wsgi.application' diff --git a/securityquiz/urls.py b/securityquiz/urls.py index ffd7aa4..f53c33b 100644 --- a/securityquiz/urls.py +++ b/securityquiz/urls.py @@ -1,10 +1,13 @@ from django.conf.urls import patterns, include, url - from django.contrib import admin admin.autodiscover() +from views import SecurityApi + urlpatterns = patterns('', # Examples: + url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')), + url(r'^api/hello', SecurityApi.as_view()), url(r'^callback$', 'views.avans_callback'), url(r'^logout$', 'views.avans_logout'), url(r'^pull$', 'views.git_pull'), diff --git a/static/img/auth_basic.png b/static/img/auth_basic.png new file mode 100644 index 0000000..044736a Binary files /dev/null and b/static/img/auth_basic.png differ diff --git a/static/img/auth_basic_app.png b/static/img/auth_basic_app.png new file mode 100644 index 0000000..232ddc0 Binary files /dev/null and b/static/img/auth_basic_app.png differ diff --git a/static/img/oauth_get_token.png b/static/img/oauth_get_token.png new file mode 100644 index 0000000..6ace05f Binary files /dev/null and b/static/img/oauth_get_token.png differ diff --git a/static/img/oauth_register.png b/static/img/oauth_register.png new file mode 100644 index 0000000..e9d66ae Binary files /dev/null and b/static/img/oauth_register.png differ diff --git a/templates/oauth.html b/templates/oauth.html new file mode 100644 index 0000000..b40fae7 --- /dev/null +++ b/templates/oauth.html @@ -0,0 +1,170 @@ +{% extends "base.html" %} + +{% block content %} + +

OAuth

+ +

Heb je wel eens gebruik gemaakt van een API van Twitter, Facebook of Google? Dan heb je waarschijnlijk te maken gehad OAuth, de technologie die je toestemming verleent om gebruik te maken van die API's.

+ +

Er zijn veel misverstanden over OAuth waardoor het vaak verkeerd begrepen wordt en er veel frictie ontstaat ontstaat tussen de API en de ontwikkelaar. Het is bijvoorbeeld niet ontworpen om enkel mee in te loggen, hoewel het daar wel veel voor gebruikt wordt. Deze week gaan we kijken naar het probleem van authoriseren en hoe OAuth dat oplost.

+ +

We nemen als voorbeeld de Poespas bank. Een simpele website waarmee gebruikers kunnen inloggen met een gebruikersnaam en wachtwoord. Als ze ingelogd zijn zien ze hun huidige saldo. Ze hebben ook een simpele API waarmee gebruikers hun saldo kunnen checken.

+ +

Simpele authenticatie

+ +

Een simpele manier om de saldo API te implementeren is om rechtstreeks je gebruikersnaam en wachtwoord mee te geven in de API request. De server checkt of de inloggegevens correct zijn en geeft de saldo informatie terug van die gebruiker terug. Uiteraard moet dit alles over een beveiligde verbinding gebeuren, maar als dat zo is is het in theorie een veilige oplossing.

+ +
+ +
+ +

Veel echte API's werkte vroeger op deze manier (vaak op basis van HTTP Authentication). En zolang alles over een beveiligde verbinding tussen de gebruiker en de server loopt is alles veilig. De problemen beginnen echter als een derde partij betrokken raakt bij onze API.

+ +

Een startup heeft de BudgetApp ontwikkelt, een nieuwe app die laat zien hoeveel saldo je per maand op je rekening hebt staan. Ze maken gebruik van de Poespas API om het saldo van de gebruiker op te halen. Maar om die API te kunnen gebruiken hebben ze de gebruikersnaam en het wachtwoord van de gebruiker nodig. Op hun inlogscherm vragen ze daarom om de inloggegevens zodat de app goed kan werken.

+ +
+ +
+ +

Het probleem met deze manier van authenticeren wordt nu snel duidelijk: de app krijgt de gebruikersnaam en wachtwoord van de gebruiker in handen. Daarmee kunnen ze niet alleen het saldo kunnen ophalen met de API. Maar ook alle andere dingen die de gebruiker kan zoals bijvoorbeeld inloggen, geld overschrijven en wachtwoord veranderen.

+ + +

Om deze situatie te voorkomen willen we een autorisatiesysteem waarbij de app wel een sleutel krijgt om de API te gebruiken, maar niet het wachtwoord van de gebruiker zelf. Dat moet strict geheim blijven.

+ +

Enter OAuth

+ + ...Access token verhaal... + +

autorisatie

+ +

Deze site heeft ook een API die te vinden is op: /api/hallo. Die mag je uiteraard alleen gebruiken als je een access token hebt, als je gewoon op de link klikt krijg je een lege 403 pagina (Forbidden). We gaan alle stappen doorlopen om te komen tot de almachtige access token waarmee we de API kunnen aanroepen.

+ +

De eerste stap bij een OAuth provider is om een client aan te maken. Daarmee weet de site altijd wie het is die de API aanroep doet. Voor deze opdracht kan je die aanmaken op deze pagina. Maak je eigen applicatie met de volgende gegevens:

+ + + +
+ +
+ +
+ Wat is de client_id die je hebt geregistreerd? +
5 punten
+ +
+ +

Elke OAuth server heeft twee endpoints (lees: URL's): de autorisatie endpoint en de token endpoint. Hieronder zie je de endpoints van een aantal bekende servers:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
ServiceAuthorization endpointToken endpoint
Dit vakhttp://sec1.aii.avans.nl/o/authorize/http://sec1.aii.avans.nl/o/token/
GitHubhttps://github.com/login/oauth/authorizehttps://github.com/login/oauth/access_token
Googlehttps://accounts.google.com/o/oauth2/authhttps://accounts.google.com/o/oauth2/token
Facebookhttps://www.facebook.com/dialog/oauthhttps://graph.facebook.com/oauth/access_token
+ +

De autorisatie endpoint is de URL waar je de gebruiker heenstuurt om hem te laten inloggen op de website en om op 'Toestaan' te laten klikken. Deze site redirect daarna weer terug naar je eigen site / app met een speciale autorisatie code. De token endpoint is de OAuth API waar we al onze access tokens uit kunnen halen, maar dan hebben we wel eerst die autorisatie code nodig.

+ +

Om te authoriseren hebben we de volgende URL parameters nodig: (documentatie)

+ + + +

Een voorbeeld van een autorisatie URL is: http://sec.aii.avans.nl/o/authorize/?client_id=(CLIENT_ID)&response_type=code

+ +
+ Autoriseer met je eigen client id. Welke autorisatie code heb je gekregen? +
5 punten
+ +
+ +

Access tokens in zicht

+ +

Ok, we hebben nu een autorisatie code. Met die code heb je nu een bewijsje dat de gebruiker recentelijk jouw client heeft geautoriseerd. Maar na dat alles hebben we nog steeds niet die albelangrijke access tokens die ons toegang geven tot de API. Om daar aan te komen moeten we onze autorisatie code inwisselen bij de token endpoint voor een refresh token en een access token.

+ +

Dit lijkt allemaal nodeloos complex, waarom geeft OAuth nadat de gebruiker zijn zege heeft gegevens ons niet meteen een refresh_token en een access_token? Waarom moet dat nou weer via een aparte 'autorisatie code'? De reden hiervoor is is dat OAuth ontworpen is rond het principe dat webapplicatie's access_token moeten kunnen krijgen, zonder dat de gebruiker die te zien krijgt. De gebruiker kan niet zelf nieuwe access tokens ophalen omdat hij niet de client_secret heeft.

+ +

Om de access_token en de refresh_token te krijgen moeten we POST requests versturen naar de token endpoint. Testen met POST requests is het handigst om met een tool te doen. In de screenshots maken we gebruik van Postman. + + We moeten de volgende parameters meesturen: (documentatie) + +