Protect Your API with OAuth 2 Slides: http://akrab.at/2nin0yI Rob Allen ~ April 2017 ~ @akrabat
A presentation at PHP Yorkshire in April 2017 in York, UK by Rob Allen
 
                Protect Your API with OAuth 2 Slides: http://akrab.at/2nin0yI Rob Allen ~ April 2017 ~ @akrabat
 
                Authentication Know who is logging into your API • Rate limiting • Revoke application access if its a problem • Allow users to revoke 3rd party applications Rob Allen ~ @akrabat
 
                QWxhZGRpbjpPcGVuU2VzYW1l Rob Allen ~ @akrabat
 
                Problems • All clients have to know user's credentials • Credentials are passed in every request Rob Allen ~ @akrabat
 
                OAuth2 The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service oauth.net Rob Allen ~ @akrabat
 
                Roles • The user (Resource Owner) • The (third-party) application (Client) • The API (Resource Server) • The Authorisation server Rob Allen ~ @akrabat
 
                Grant types Grant type Use case Authorization code 3rd party web or native Password 1st party Client credentials application (no user) Implicit 3rd party JS app Rob Allen ~ @akrabat
 
                Tokens OAuth uses a bearer token GET /books/1 HTTP/1.1 Host: api.example.com Accept: application/json Authorization: Bearer {some-string-here} Rob Allen ~ @akrabat
 
                Password grant (for 1st party apps) Rob Allen ~ @akrabat
 
                Password grant Rob Allen ~ @akrabat
 
                Password flow Rob Allen ~ @akrabat
 
                Password flow Rob Allen ~ @akrabat
 
                Password flow Rob Allen ~ @akrabat
 
                Password flow Rob Allen ~ @akrabat
 
                Implementing in PHP Rob Allen ~ @akrabat
 
                OAuth 2.0 Server PHP by Brent Shaffer $ composer require bshaffer/oauth2-server-php • Implements Authorise and Token endpoints • Mulitple storage backends: PDO, Redis, Mongo, Cassandra, DynamoDB, etc Rob Allen ~ @akrabat
 
                Steps to implement For the client and user credentials grants: 1. Set up the database tables 2. Register the OAuth2 Server 3. Implement the Authorise endpoint Rob Allen ~ @akrabat
 
                Database tables • CREATE TABLE oauth_clients ... • CREATE TABLE oauth_access_tokens ... • CREATE TABLE oauth_authorization_codes ... • CREATE TABLE oauth_refresh_tokens ... • CREATE TABLE oauth_users ... • CREATE TABLE oauth_scopes ... • CREATE TABLE oauth_jwt ... (SQL is in the Cookbook in the docs) Rob Allen ~ @akrabat
 
                new \OAuth2\Server ( $storage ); Rob Allen ~ @akrabat
 
                addGrantType ( $userCreds ); 13 14 return $server ; 15 }; Rob Allen ~ @akrabat
 
                Aside: use Bcrypt namespace MyAuth ; class PdoStorage extends \OAuth2\Storage\Pdo { protected function checkPassword ( $user , $pwd ) { return password_verify ( $pwd , $user [ 'password' ]); } } Rob Allen ~ @akrabat
 
                Credentials We need a client: 1 INSERT INTO oauth_clients 2 ( client_id , client_secret , redirect_uri ) 3 VALUES 4 ( "mywebsite" , "$2y$10$mzP0fR...BHu" , null ); Rob Allen ~ @akrabat
 
                Credentials We need a client: 1 INSERT INTO oauth_clients 2 ( client_id , client_secret , redirect_uri ) 3 VALUES 4 ( "mywebsite" , "$2y$10$mzP0fR...BHu" , null ); & a user: 1 INSERT INTO oauth_users 2 ( username , password , first_name , last_name ) 3 VALUES 4 ( "rob" , "$2y$10$Qq1CsK...LV6" , "Rob" , "Allen" ); Rob Allen ~ @akrabat
 
                send (); 8 exit ; 9 } 10 ); Rob Allen ~ @akrabat
 
                How does this work?
 1 
$ curl -i -X POST http://localhost:8888/token 
 2 
  -H "Accept: application/json" 
 3 
  -H "Content-Type: application/json" 
 4 
  -d $'{
 5 
      "grant_type": "password"
 6 
      "client_id": "mywebsite",
 7 
      "client_secret": "abcdef",
 8 
      "username": "rob",
 9 
      "password": "123456"
10 
  }'
Rob Allen ~ @akrabat
 
                Response 1 HTTP/1.1 200 OK 2 Host: localhost:8888 3 Content-Type: application/json 4 5 { 6 "access_token": "65077f90e3baae8aa863", 7 "expires_in": 3600, 8 "token_type": "Bearer", 9 "scope": null, 10 "refresh_token": "be071d2c6193d32a353d" 11 } Rob Allen ~ @akrabat
 
                Protecting your API endpoints Rob Allen ~ @akrabat
 
                $token [ 'user_id' ]; Rob Allen ~ @akrabat
 
                Unauthorised API call
1 
$ curl -i -H "Accept: application/json" 
2 
  http://localhost:8888/authors
3 
4 
HTTP/1.1 401 Unauthorized
5 
Host: localhost:8888
6 
Connection: close
7 
X-Powered-By: PHP/7.0.15
8 
WWW-Authenticate: Bearer realm="Service"
9 
Content-Type: application/json
Rob Allen ~ @akrabat
 
                Authorised API call
 1 
$ curl -i -H "Accept: application/json" 
 2 
  -H "Authorization: Bearer 65077f90e3baae8aa863" 
 3 
  http://localhost:8888/authors
 4 
 5 
HTTP/1.1 200 OK
 6 
Host: localhost:8888
 7 
Connection: close
 8 
X-Powered-By: PHP/7.0.15
 9 
Content-type: application/hal+json
10 
11 
{
12 
    "count": 6,
13 
    "_links": {
14 
...
Rob Allen ~ @akrabat
 
                Authorisation Code (for 3rd party apps) Rob Allen ~ @akrabat
 
                Authorisation code Rob Allen ~ @akrabat
 
                Authorisation code flow Rob Allen ~ @akrabat
 
                Authorisation code flow Rob Allen ~ @akrabat
 
                Authorisation code flow Rob Allen ~ @akrabat
 
                Authorisation code flow Rob Allen ~ @akrabat
 
                Authorisation code flow Rob Allen ~ @akrabat
 
                Authorisation code flow Rob Allen ~ @akrabat
 
                Authorisation code flow Rob Allen ~ @akrabat
 
                Authorisation code flow Rob Allen ~ @akrabat
 
                Authorisation code flow Rob Allen ~ @akrabat
 
                Implementing in PHP Rob Allen ~ @akrabat
 
                Required pieces 1. A website that talks to the Authorisation server 2. A new endpoint in the Authorisation server to provide auth codes Rob Allen ~ @akrabat
 
                Process 1. 3rd party app sends user to our website: 2. User logs in to our website and authorises app 3. Our website gets code from our API 4. Our website redirects user back to app (or displays a code) Rob Allen ~ @akrabat
 
                addGrantType ( $userCreds ); 8 9 return $server ; 10 }; Rob Allen ~ @akrabat
 
                addGrantType ( $authCode ); 12 13 return $server ; 14 }; Rob Allen ~ @akrabat
 
                Authorise Rob Allen ~ @akrabat
 
                Remember this? Rob Allen ~ @akrabat
 
                It's more like this… Rob Allen ~ @akrabat
 
                'Bearer ' . $webAccessToken , 10 ] 11 ]); Rob Allen ~ @akrabat
 
                send (); exit ; Rob Allen ~ @akrabat
 
                renderPage ( $queryParams [ 'code' ]); Rob Allen ~ @akrabat
 
                Authorised Rob Allen ~ @akrabat
 
                Get token from code
1 
$ curl -X "POST" http://localhost:8888/token 
2 
  -H "Accept: application/json" 
3 
  -H "Content-Type: application/json" 
4 
  -d $'{
5 
   "grant_type": "authorization_code",
6 
   "client_id": "testclient",
7 
   "client_secret": "abcdef",
8 
   "code": "aee25eb86b2be8ca572d9f4031c57a3c5c52137c",
9 
  }'
Rob Allen ~ @akrabat
 
                Response 1 HTTP/1.1 200 OK 2 Host: localhost:8888 3 Connection: close 4 Content-Type: application/json 5 6 { 7 "access_token": "df7fcb455efb9a2c9544", 8 "expires_in": 3600, 9 "token_type": "Bearer", 10 "scope": null, 11 "refresh_token": "bb87ffbef191bdda55b1" 12 } Rob Allen ~ @akrabat
 
                JWT bearer tokens Rob Allen ~ @akrabat
 
                JWT • Cryptographically signed block of data • Potentially faster • A JWT consists of • Header • Payload • Signature Also: JWT is pronounced "jot" Rob Allen ~ @akrabat
 
                Payload 1 { 2 "id" : "394a71988caa6cc30601e43f5b6569d52cd7f" , 3 "jti" : "394a71988caa6cc30601e43f5b6569d52cd7f" , 4 "iss" : "{issuer_id}" , 5 "aud" : "{client_id}" , 6 "sub" : "{user_id}" , 7 "exp" : 1483711650 , 8 "iat" : 1483708050 , 9 "token_type" : "bearer" , 10 "scope" : "read write delete" 11 } Rob Allen ~ @akrabat
 
                Implementation 1. Update token creation to create JWT tokens 2. Update validation to check for JWT tokens Rob Allen ~ @akrabat
 
                new \OAuth2\Server ( $storage ); 6 7 // ... add grants ... Rob Allen ~ @akrabat
 
                true , 7 ]); 8 9 // ... add grants ... Rob Allen ~ @akrabat
 
                Get a token
 1 
$ curl -i -X POST http://localhost:8888/token 
 2 
  -H "Accept: application/json" 
 3 
  -H "Content-Type: application/json" 
 4 
  -d $'{
 5 
      "grant_type": "password"
 6 
      "client_id": "mywebsite",
 7 
      "client_secret": "abcdef",
 8 
      "username": "rob",
 9 
      "password": "123456"
10 
  }'
Rob Allen ~ @akrabat
 
                Response 1 HTTP/1.1 200 OK 2 Host: localhost:8888 3 Connection: close 4 Content-Type: application/json 5 6 { 7 "access_token": "eyJ0eXAiOi...BLUWlojjm24HmNbOMg", 8 "expires_in": 3600, 9 "token_type": "Bearer", 10 "scope": null, 11 "refresh_token": "be071d2c6193d32a353d" 12 } Rob Allen ~ @akrabat
 
                true , 9 ]); Rob Allen ~ @akrabat
 
                $token [ 'user_id' ]; Rob Allen ~ @akrabat
 
                Refresh tokens Rob Allen ~ @akrabat
 
                Refresh tokens
•
Access tokens expire quickly
•
Use the refresh token to get a new access token
•
Guard refresh tokens!
1 
$ curl -i -X POST http://localhost:8888/token 
2 
  -H "Accept: application/json" 
3 
  -H "Content-Type: application/json" 
4 
  -d $'{
5 
      "grant_type": "refresh_token"
6 
      "client_id": "testclient",
7 
      "client_secret": "abcdef",
8 
      "refresh_token": "be071d2c6193d32a353d"
9 
  }'
Rob Allen ~ @akrabat
 
                Response 1 HTTP/1.1 200 OK 2 Host: localhost:8888 3 Connection: close 4 Content-Type: application/json 5 6 { 7 "access_token": "eyJ0eXAiOi...tjD8whWBt8h4oRluOMA", 8 "expires_in": 3600, 9 "token_type": "Bearer", 10 "scope": null 11 } Rob Allen ~ @akrabat
 
                Summary Rob Allen ~ @akrabat
 
                Summary • Authorization header contains token • Two actors • Client (id & secret) • User (username & password) • Grants: • Password: 1st party apps • Authorisation code: 3rd party apps • JWT for speed and scale Rob Allen ~ @akrabat
 
                Resources This talk: • https://github.com/akrabat/slim-bookshelf-api • https://akrabat.com/talks/#oauth2 Around the web: • https://oauth.net/2/ • http://bshaffer.github.io/oauth2-server-php-docs • https://aaronparecki.com/oauth-2-simplified/ Rob Allen ~ @akrabat
 
                Questions? Feedback: https://joind.in/talk/ce818 Rob Allen ~ @akrabat
 
                Thank you! Feedback: https://joind.in/talk/ce818 Rob Allen ~ @akrabat
