Skip to content

Instantly share code, notes, and snippets.

@disintegrator
Last active September 2, 2015 05:41
Show Gist options
  • Save disintegrator/e310a25c1ed9819364d7 to your computer and use it in GitHub Desktop.
Save disintegrator/e310a25c1ed9819364d7 to your computer and use it in GitHub Desktop.
API and design notes for an OAuth2 provider with threat mitigation strategies

Overview

This design must be aware of the OAuth2 thread models and mitigation strategies as described in the following resources:

A host of vulnerabilities can be removed by pinning redirect_uri, scope, response_type (read: allowed grants for each client) variables in client settings when registering clients (apps).

Decisions

Authorization vs Authentication

Authentication is the process of ascertaining that somebody really is who he claims to be.

Authorization refers to rules that determine who is allowed to do what

The design of the OAuth system must be aware of this distinction and delegate authentication to the appropriate services.

During registration, clients may define an authentication service by its URI and supply an accompanying session signing key - these are considered trusted authenticators if approved for use. This is not required and most clients will likely allow the authorization service to determine where users should authenticate.

The authentication service must authenticate users by generating a JWT and redirecting the user back to the authorization flow with the query parameter session set to the JWT.

JWT: Signing Data and Token Types

Data

In a distributed services architecture, it may be beneficial to use JWT to sign data between servers.

The examples that follow assume that the identity (read: authentication) service is separate to the oauth service. As such there are deviations from the OAuth 2.0 spec where we sign GET request parameters and attach them to a URL.

Tokens

JWT can be used for access and refresh tokens. With regards to access tokens, it is recommended to follow the OAuth 2.0 JWT Bearer tokens spec with the following definitions:

  • ISS claim must be the client id
  • SUB claim must be the user id for code, implicit and password grants and the client id for client credetials grant
  • JTI claim is required in order to track blacklisted tokens
  • SCOPE claim containing a comma-delimited list of scope strings e.g. "user,post:create,post:publish"

Refresh tokens are not covered by the spec but these can also be JWT tokens with:

  • Identical ISS, AUD, SUB and SCOPE as the corresponding token
  • JTI claim must also be present and set to JTI of the corresponding access token.
  • EXP claim must be present

Authentication tokens which were discussed in the previous section must contain the following claims:

  • ISS claim must be the client id
  • SUB claim must be the resource owner's id
  • AUD claim must be authorization server's authorize endpoint URL
  • IAT claim must be present

Clients

During client registration, it is important to record a specific grant type and a single redirect URI. This prevents a host of vulnerabilities. The values for grant_type and redirect_uri passed by clients during the various flows may be ignored or verified against the stored values.

It is also important to pin down a set of scopes for each client. This will allow better controls and reviews of what clients can ask of resource owners (in many cases, the user).

Data model

At it's simplest the data model for a client must hold the following fields:

id - string - A generated client identifier
grant_type - enum["code", "implicit", "password", "client"] - The allowed grant type for a client
secret - string - A generated client secret for the client credentials grant type
redirect_uri - string - A specific redirect URI for authorization code and implicit grant types
scopes - List<string> - A list of allowed scopes that the client may request from users or on behalf of itself

Authorization Code Grant

Authorization

-> GET /oauth/authorize?client_id=...&redirect_uri=...&scope=...&state=...
<- 303 /login?next=%2Foauth%2Fauthorize%3Fclient_id%3D...%26scope%3D...%26state%3D...
<- 200 {"code": ..., "state": ...}
<- 403 {"error": "", "error_description": "", "error_uri": "", "state": ""}
-> POST /login?username=...&password=...&next=%2Foauth%2Fauthorize%3Fclient_id%3D...%26scope%3D...%26state%3D...
<- 303 /oauth/authorize?session=...&client_id=...&scope=...&state=...
<- 403 {"error": "", "error_description": "", "error_uri": "", "state": ""}

Token

-> POST /oauth/token?code=...&client_id=...
<- 200 {"access_token": "...", "refresh_token": "...", "token_type": "...", "expires_in": "...", "scope": "..."}
<- 403 {"error": "", "error_description": "", "error_uri": "", "state": ""}

Implicit grant

-> GET /oauth/authorize?client_id=...&scope=...&state=...
<- 200 {"access_token": "...", "token_type": "...", "expires_in": "...", "scope": "..."}}
<- 303 /ident/sessions/oauthlogin?req=Sign({"redirect": "/oauth/authorize?client_id=...&scope=...&state=..."})
<- 403 {"error": "", "error_description": "", "error_uri": "", "state": ""}
-> POST /ident/sessions/oauthlogin?username=...&password=...&next=/oauth/authorize?client_id=...&scope=...&state=...
<- 303 /oauth/authorize?client_id=...&scope=...&state=...
<- 403 {"error": "", "error_description": "", "error_uri": "", "state": ""}

Resource Owner Password Credentials Grant

This grant type should only permitted by clients that also own the authorization service. Note that while the state parameter is not present a CSRF token would typically be present either in the form or as a header.

-> POST /oauth/token?client_id=...&scope=...&username=...&password=...
<- 200 {"access_token": "...", "token_type": "...", "expires_in": "...", "scope": "..."}}
<- 403 {"error": "", "error_description": "", "error_uri": "", "state": ""}

Client Credentials Grant

The client credentials grant does not deviate from the OAuth 2.0 spec except for the ommission of client_id from the POST form.

It is important to note that this flow does not issue refresh tokens and that clients MUST be authenticated before an access token is issued

-> POST /oauth/token?&client_id=...&client_secret=...&scope=...
<- 200 {"access_token": "...", "token_type": "...", "expires_in": "...", "scope": "..."}}
<- 403 {"error": "", "error_description": "", "error_uri": "", "state": ""}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment