Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OAuth 2.0 Simplified - OWASP Portland

OAuth 2.0 Simplified - OWASP Portland

Aaron Parecki

July 16, 2018
Tweet

More Decks by Aaron Parecki

Other Decks in Technology

Transcript

  1. © Okta and/or its affiliates. All rights reserved. Okta Confidential

    Aaron Parecki @aaronpk aaronpk.com OAuth 2.0 Simplified OWASP Portland July 2018
  2. @aaronpk Before OAuth • Apps stored the user’s password •

    Apps got complete access to a user’s account • Users couldn’t revoke access except by changing their password • Compromised apps exposed the user’s password
  3. @aaronpk OAuth 2.0 Terminology • The Application: "Client" • The

    API: "Resource Server" • Authorization Server • The User: "Resource Owner" • Access Token
  4. @aaronpk Obtaining an Access Token Applications use an OAuth flow

    to obtain an access token • Authorization Code Flow: web apps, native apps • Device Flow: browserless or input-constrained devices • Password: not really OAuth, only for first-party apps • Refresh Token: getting a new access token when it expires
  5. @aaronpk Using an Access Token POST /resource/1/update HTTP/1.1 Authorization: Bearer

    RsT5OjbzRn430zqMLgV3Ia Host: api.authorization-server.com description=Hello+World
  6. @aaronpk Registering an Application • Registering an application gives you

    a client_id • and if the application is not a "public client", then also a client_secret • These identify the application to the service.
  7. @aaronpk Client ID and Secret • A "public client" means

    anything where the client cannot keep strings confidential. • Javascript clients: "view source" • Native apps: can decompile and see strings • No secret is used for these clients, only the ID
  8. @aaronpk Authorization Server Resource Server The Best App Ever User:

    I’d like to use this great app App: Please go to the authorization server to grant me access User: I’d like to log in to “The Best App Ever”, it wants write access to my account Authz: Here is a temporary code the app can use App: Here is the temporary code, please give me a token User: Here is the temporary code, please use this to get a token Authz: Here is an access token! App: Please let me access this user’s data with this access token!
  9. @aaronpk Build the "Log in" link • response_type=code - indicates

    that your client expects to receive an authorization code • client_id=CLIENT_ID - The client ID you received when you first created the application • redirect_uri=REDIRECT_URI - Indicates the URL to return the user to after authorization is complete, such as https://example-app.com/callback • scope=photos - A space-separated string indicating which parts of the user's account you wish to access • state=1234zyx - A random string generated by your application, which you'll verify later
  10. @aaronpk If User Allows https://example.com/auth?code=AUTH_CODE_HERE&state=1234zyx The user is redirected back

    to the application with an authorization code https://example.com/auth?error=access_denied The user is redirected back to the application with an error code If User Denies
  11. @aaronpk Exchange the Code for an Access Token • grant_type=authorization_code

    - indicates that this request contains an authorization code • code=CODE_FROM_QUERY - Include the authorization code from the query string of this request • redirect_uri=REDIRECT_URI - This must match the redirect_uri used in the original request • client_id=CLIENT_ID - The client ID you received when you first created the application • client_secret=CLIENT_SECRET - Since this request is made from server- side code, the secret is included
  12. @aaronpk Exchange the Code for an Access Token POST https://api.authorization-server.com/token

    grant_type=authorization_code& code=AUTH_CODE_HERE& redirect_uri=REDIRECT_URI& client_id=CLIENT_ID& client_secret=CLIENT_SECRET
  13. @aaronpk Exchange the Code for an Access Token { "access_token":"RsT5OjbzRn430zqMLgV3Ia",

    "expires_in":3600, "refresh_token":"64d049f8b21191e12522d5d96d5641af5e8" } The server replies with an access token and expiration time or if there was an error: {"error":"invalid_request"}
  14. @aaronpk example-app.com Connect with Google Client accounts.google.com email password Authorization

    Server accounts.google.com Yes No Allow Example App to access your public profile and contacts? example-app.com/callback Loading… Direct the user to the authorization server response_type=code
 &redirect_uri=example-app.com/callback
 &state=00000 User signs in Authorization server redirects back to the app code=XYZ123&state=00000 Exchange authorization code for an access token
  15. © Okta and/or its affiliates. All rights reserved. Okta Confidential

    Javascript Apps Implicit Flow - No Client Secret
  16. @aaronpk Build the "Log in" link • response_type=token - indicates

    that your client expects to receive an access token • client_id=CLIENT_ID - The client ID you received when you first created the application • redirect_uri=REDIRECT_URI - Indicates the URI to return the user to after authorization is complete, such as https://example-app.com/callback • scope=photos - A space-separated string indicating which parts of the user's account you wish to access • state=1234zyx - A random string generated by your application, which you'll verify later
  17. @aaronpk If User Allows https://example.com/auth#token=ACCESS_TOKEN The redirect contains an access

    token in the URL https://example.com/auth#error=access_denied The user is redirected back to the application with an error code If User Denies
  18. @aaronpk Redirect URLs for Native Apps • There is no

    built-in security for redirect URIs like when we use DNS, • since any app can claim any URL scheme.
  19. @aaronpk Redirect URLs for Native Apps Custom URL Scheme: example://redirect

    App-Claimed URL Pattern https://maps.google.com/*
  20. @aaronpk PKCE: Proof Key for Code Exchange RFC 7636: Pioneered

    by Google • Like an on-the-fly client secret
  21. @aaronpk PKCE: Proof Key for Code Exchange • First create

    a "code verifier", a random string 43-128 characters long. • Compute the SHA256 hash of the code verifier, call that the code challenge • Include code_challenge=XXXXXX and code_challenge_method=S256 in the initial authorization request • Send the code verifier when exchanging the authorization code for an access token • (For clients that can't support SHA256, include the plaintext verifier as the challenge, and set the method to "plain")
  22. @aaronpk Generate the Code Verifier 4A6hBupTkAtgbaQs39RSELUEqtSWTDTcRzVh1PpxD5YVKllU Generate a random string

    43-128 characters long ipSBt30y48l401NGbLjo026cqwsRQzR5KI40AuLAdZ8 The challenge is the SHA256 hash of the verifier string base64url(sha256(code_verifier)) Generate the Code Challenge
  23. @aaronpk Build the "Log in" link https://authorization-server.com/auth? response_type=code& client_id=CLIENT_ID& redirect_uri=REDIRECT_URI&

    scope=photos& state=1234zyx& code_challenge=XXXXXXXXXXXXX& code_challenge_method=S256 Include the code challenge (the hashed value) in the request
  24. @aaronpk If User Allows example://auth?code=AUTH_CODE_HERE&state=1234zyx The user is redirected back

    to the application with an authorization code example://auth?error=access_denied The user is redirected back to the application with an error code If User Denies
  25. @aaronpk Exchange the Code for an Access Token Verify state,

    then make a POST request: • grant_type=authorization_code - indicates that this request contains an authorization code • code=CODE_FROM_QUERY - Include the authorization code from the query string of this request • redirect_uri=REDIRECT_URI - This must match the redirect_uri used in the original request • client_id=CLIENT_ID - The client ID you received when you first created the application • code_verifier=VERIFIER_STRING - The plaintext code verifier initially created
  26. @aaronpk Exchange the Code for an Access Token POST https://api.authorization-server.com/token

    grant_type=authorization_code& code=AUTH_CODE_HERE& redirect_uri=REDIRECT_URI& client_id=CLIENT_ID& code_verifier=VERIFIER_STRING Note: code verifier used in place of client secret
  27. @aaronpk Exchange the Code for an Access Token { "access_token":"RsT5OjbzRn430zqMLgV3Ia",

    "expires_in":3600", "refresh_token":"64d049f8b2119a12522d5dd96d5641af5e8" } The server compares the code_verifier with the code_challenge that was in the request when it generated the authorization code, and responds with an access token.
  28. @aaronpk Exchange the Username and Password for a Token POST

    https://api.authorization-server.com/token grant_type=password& username=USERNAME& password=PASSWORD& client_id=CLIENT_ID& client_secret=CLIENT_SECRET The user’s credentials are sent directly! Not a good idea for third party apps!
  29. @aaronpk Exchange the Client ID and Secret for a Token

    POST https://api.authorization-server.com/token grant_type=client_credentials& client_id=CLIENT_ID& client_secret=CLIENT_SECRET No user context in the request, so the access token can only 
 be used to access the application’s resources
  30. © Okta and/or its affiliates. All rights reserved. Okta Confidential

    Request a Device Code { "device_code": "NGU5OWFiNjQ5YmQwNGY3YTdmZTEyNzQ3YzQ1YSA", "user_code": "BDWD-HQPK", "verification_uri": "https://example.com/device", "interval": 5, "expires_in": 1800 } The server responds with a new device code and user code, as well as the URL the user should visit to enter the code.
  31. @aaronpk NLBLMDPP POST https://example.okta.com/token grant_type=urn:ietf:params:oauth:grant- type:device_code &client_id=CLIENT_ID &device_code=NGU5OWFiNjQ5YmQwNGY3YTdmZTEyNzQ3YzQ1YSA While the

    device waits for the user to enter the code and authorize the application, the device polls the token endpoint. { "error": "authorization_pending" }
  32. @aaronpk POST https://example.okta.com/token grant_type=urn:ietf:params:oauth:grant- type:device_code &client_id=CLIENT_ID &device_code=NGU5OWFiNjQ5YmQwNGY3YTdmZTEyNzQ3YzQ1YSA { "access_token": "RsT5OjbzRn430zqMLgV3Ia",

    "expires_in": 3600, "refresh_token": "b7aab35e97298a060e0ede5b43ed1f70a8" } While the device waits for the user to enter the code and authorize the application, the device polls the token endpoint.
  33. @aaronpk What is a Refresh Token? • A special token

    used to get new access tokens • Requested along with the id_token or access token in the initial step • Usually requires scope: offline_access • Usually not issued to Javascript apps
  34. @aaronpk Exchange the Refresh Token for an Access Token POST

    https://authorization-server.com/token grant_type=refresh_token& refresh_token=REFRESH_TOKEN& redirect_uri=REDIRECT_URI& client_id=CLIENT_ID& client_secret=CLIENT_SECRET
  35. @aaronpk New Access Token in the Response { "access_token": "RsT5OjbzRn430zqMLgV3Ia",

    "expires_in": 3600, "refresh_token": "64d049f8b21191e12522d5d96d5641af5e8" }
  36. @aaronpk Authorization Interface Identify your service Identify the third-party app

    List the scopes the app is requesting Identify the developer name Show which user is logged in Allow/Cancel buttons
  37. © Okta and/or its affiliates. All rights reserved. Okta Confidential

    Defining Scopes 89 • Read / Write • Restrict access to sensitive information • e.g. private repos on GitHub • By functionality • e.g. Google Drive, YouTube, Gmail • Billable resources
  38. © Okta and/or its affiliates. All rights reserved. Okta Confidential

    Presenting Scopes to the User 90 • Provide clear and straightforward information • Provide enough detail so the user knows what the application can access • Don't provide too much detail that they are overwhelmed and just click "ok"
  39. © Okta and/or its affiliates. All rights reserved. Okta Confidential

    Allow the user to modify the scopes granted 93