Skip to content

Instantly share code, notes, and snippets.

@jcoglan
Created July 5, 2012 15:23
Show Gist options
  • Save jcoglan/3054344 to your computer and use it in GitHub Desktop.
Save jcoglan/3054344 to your computer and use it in GitHub Desktop.

This document is intended to be a simplified version of the OAuth 2.0 specification. In particular it has been written with implementors in mind, and as such attempts to trim the spec down to just what you need to implement an OAuth provider or client. It is necessarily not complete, but attempts to introduce spec requirements in the same order in which the protocol proceeds in practise, with everything you need to know about each protocol endpoint brought together in one place rather than scattered across a large document.

Caveat emptor: some details have been deliberately left out because the author considered them detrimental to an easy understanding of how the protocol should be used. Almost everything in the spec is suffixed with, 'or, do whatever you want,' so I doubt this has done much to damage the document's accuracy.

It is recommended that implementors read the Security Considerations in the original specification.

Roles

There are four entities in OAuth:

  • Resource owner - an entity capable of granting access to protected resources
  • Resource server - a server storing and mediating access to protected resources
  • Client - an application that wants access to the resource owner's resources
  • Authorization server - server that issues tokens to clients

Protocol flow

+--------+                               +---------------+
|        |--(A)- Authorization Request ->|   Resource    |
|        |                               |     Owner     |
|        |<-(B)-- Authorization Grant ---|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(C)-- Authorization Grant -->| Authorization |
| Client |                               |     Server    |
|        |<-(D)----- Access Token -------|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(E)----- Access Token ------>|    Resource   |
|        |                               |     Server    |
|        |<-(F)--- Protected Resource ---|               |
+--------+                               +---------------+

Grant types

  • Authorization code
  • Implicit
  • Resource owner password credentials
  • Client credentials
  • Extension types

Authorization code

The client redirects to the authorization server, which authenticates the resource owner and obtains authorization, then redirects to the client with an authorization code. The client then exchanges the code for an access token by supplying its credentials. This allows client to authenticate separately and means the access token never is exposed to the resource owner's user agent.

Implicit

Optimized for in-browser JS clients. Instead of an authorization code, an access token is issued immediately without client authentication. Identity is handled using redirect URI. Because the access token is returned via a redirect rather than as the result of a server-side call, it is exposed to the resource owner and any other software with access to the user agent.

Resource owner password credentials

Username and password are used instead of an authorization code to obtain an access token. Should only be used for highly trusted clients or when other types are not available. Username and password are only used to get an access token and are not stored. This flow can also be used to migrate legacy systems to OAuth by exchanging stored user credentials for an access token.

Client credentials

The client supplies its own credentials to get an access token. Used when the client and resource owner are the same entity. This flow must only be used by confidential clients (defined below).

Extension types

The client gets an access token by declaring it's using an extension type, identified by an absolute URI, and supplying any parameters required by that type. In earlier drafts of the spec this mechanism was referred to as 'assertions' but its scope has since broadened.

Access and refresh tokens

Both types of tokens are opaque to the client. Access tokens may be identifiers used to retrive authorization data, or self-contain the data using a signed representation. Refresh tokens are identifiers.

More detailed information on different token types appears at the end of this document.

Using refresh tokens

+--------+                                           +---------------+
|        |--(A)------- Authorization Grant --------->|               |
|        |                                           |               |
|        |<-(B)----------- Access Token -------------|               |
|        |               & Refresh Token             |               |
|        |                                           |               |
|        |                            +----------+   |               |
|        |--(C)---- Access Token ---->|          |   |               |
|        |                            |          |   |               |
|        |<-(D)- Protected Resource --| Resource |   | Authorization |
| Client |                            |  Server  |   |     Server    |
|        |--(E)---- Access Token ---->|          |   |               |
|        |                            |          |   |               |
|        |<-(F)- Invalid Token Error -|          |   |               |
|        |                            +----------+   |               |
|        |                                           |               |
|        |--(G)----------- Refresh Token ----------->|               |
|        |                                           |               |
|        |<-(H)----------- Access Token -------------|               |
+--------+           & Optional Refresh Token        +---------------+

HTTP redirection

Though 302 is used in the spec, any workable method of redirecting the client is acceptable.

Client registration

Clients must provide:

  • Client type
    • Confidential - can keep their credentials secure, e.g. server-side apps
    • Public - cannot keep credentials secure, e.g. JS/native apps
  • Redirect URI(s)
    • If the client cannot pre-regsiter its complete redirect URI, it must at least register the scheme, authority and path. The client can still vary the query string in this URI when obtaining authorization.
    • The client can register multiple redirect URIs.
    • The provider should never redirect to an unregistered URI.
  • Server-defined metadata e.g. name, website, description, logo

All public clients, and confidential clients using the implicit grant type, must pre-register with the provider. The provider should (not must) require other client types to register as well.

On registration, client receives:

  • Client identifier - unique string identifying the client, not secret
  • Client credentials - if confidential client, get a password, key pair, etc.

Providers may issue credentials to public clients but should not trust them.

Providers may elect to respond to unregistered clients at their own discretion, for example see the remoteStorage protocol.

Protocol endpoints

An OAuth provider must have:

  • An authorization endpoint, which the client redirects to when it needs the user to grant authorization.
    • Used by the authorization code and implicit flows
    • May include application/x-www-form-urlencoded query component
    • Must not include fragment component
    • Must respond to GET, may respond to POST
  • A token endpoint, which the client uses to obtain access tokens
    • Used by all flows except the implicit grant type
    • May include application/x-www-form-urlencoded query component
    • Must not include fragment component
    • Must respond to POST

An OAuth client must have:

  • A redirection endpoint, which the provider redirects to with the user's authorization code
    • May include application/x-www-form-urlencoded query component
    • Must not include fragment component
    • Must respond to GET
    • Used by the authorization code and implicit flows

In general, all endpoints must be accessed over TLS to protect credentials transferred over the wire. (This requirement is a MUST for the provider and a SHOULD for the client.)

Query parameters must not appear more than once in any request, and should be ignored if sent without a value. Unknown parameter names should be ignored.

Obtaining the owner's authorization

In the authorization code and implicit flows, the client begins by redirecting the user agent to the provider's authorization endpoint. It includes the following parameters:

  • response_type - required, can be one of:
    • code causes the provider to issue an authorization code, which the client must send to the token endpoint along with its credentials to obtain an access token.
    • token causes the provider to issue an access token immediately (implicit flow). The client can immediately use this token without authenticating through the token endpoint.
    • Extension types may be defined that contain space (%20) delimited unorderd list of values. The meaning of these is defined in other specs.
  • client_id - required, the client's unique identifier.
  • redirect_uri - required unless the client has registered a single complete redirection endpoint. If it has registered multiple endpoints, an incomplete endpoint or no endpoint at all, this parameter is required. When present, the provider must check that at least one of the client's registered URIs matches this value. (See RFC3986 section 6.)
  • scope - optional, space-separated list of scope names. The provider should show the owner a readable list of the scopes the client has asked for. If no scope is requested, the provider must assume an implicit default scope or treat the request as invalid.
  • state - optional (but recommended) string value used by the client to maintain state between the request and the callback. The provider must echo this value unmodified when it redirects back to the client. Can be used to prevent cross-site request forgery (CSRF).

Response format

The provider returns its response either by displaying a page to the resource owner (in the case of some types of error) or by redirecting to the client's redirection endpoint. The response contains a series of parameters expressed in application/x-www-form-urlencoded format. These parameters appear in the query string if the client sent response_type=code, or in the fragment if the client sent response_type=token. Since some user agents do not support fragments in the Location header, it may be necessary to redirect via other means, for example a form whose action refers to the required URI.

Success response

If the client's authorization request was valid, and the resource owner successfully authenticates and grants access, the provider redirects back to the client's redirection endpoint. It should warn the resource owner if the redirection endpoint does not use TLS since leaking a code or access token affects the security of the protected resources. This is particularly important if the client is using the provider as a 3rd-party authentication service a-la 'Sign in with Facebook'.

The provider includes the following url-encoded parameters in the redirect URI sent to the client:

  • code - required if the client sent response_type=code. A unique token representing the owner's access grant to the client. It is recommended this token expire after at most 10 minutes, but the code must expire at some point. The client cannot use this code more than once to obtain an access token. If the token is used more than once the provider should revoke tokens derived from it, if possible.
  • access_token - required if the client sent response_type=token. A token representting the resource owner's access grant to the client, encapsulating the grant's scope(s) and duration. The client uses this token to access protected resources.
  • token_type - required if the client sent response_type=token. Case- insensitive value that tells the client what type of token is being issued and how to use it.
  • expires_in - recommended if the client sent response_type=token. The lifetime in seconds of the access token. If omitted, the provider should document the default value.
  • scope - if applicable, a space-separated list of the scopes that the resource owner granted the client access to. This may be a subset of the scopes requested by the client, based on the provider's policies and the resource owner's instructions. The server must include this if it differs from what the client requested.
  • state - the same value that the client included in the authorization request.

If the client responds to the redirection endpoint with an HTML page (rather than redirecting further), it should not include any 3rd-party JavaScript in the page since the authorization code or access token is visible to all JS code via the window.location object.

Error response

If the authorization request fails due to an invalid redirect_uri or client_id, the provider must inform the resource owner of the error and must not redirect to the supplied URI. If the request is invalid for any other reason (e.g. the resource owner denied the request), and the provider has enough information to redirect to the client, it should do so.

The provider will include the following url-encoded parameters when redirecting to the client. Chars in these parameters must be in the range %x20-21 / %x23-5B / %x5D-7E.

  • error - required, one of:
    • invalid_request - the request was missing a required parameter, includes invalid values or is otherwise malformed
    • unauthorized_client - the client cannot request authorization via this method
    • access_denied - the resource owner denied the request
    • unsupported_response_type - the requested response_type is not supported
    • invalid_scope - the requested scope is invalid, unknown, or malformed
    • server_error - the server encountered an unhandled error during processing
    • temporarily_unavailable - current unable to handle the request
  • error_description - optional text explanation of the error
  • error_uri - URI of a page giving more information about the error
  • state - the same value that the client included in the authorization request

e.g.

HTTP/1.1 302 Found
Location: https://client.example.com/cb?error=access_denied&state=xyz

Obtaining an access token

In all flows except the implicit grant type, the client calls the provider's token endpoint to exchange some set of credentials for an access token. This typically involves the client authenticating to guard against codes and tokens being leaked or clients being compromised.

Client authentication

Client can use HTTP Basic authentication when obtaining an access token. It uses its url-encoded client_id as the username and its url-encoded client_secret as the password. e.g.:

Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3

The client can include client_id and client_secret in the request body. Clients should only do this if they're incapable of using HTTP Basic auth. e.g.:

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded;charset=UTF-8

grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
&client_id=s6BhdRkqt3&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw

The client must not put client_id or client_secret in the URI when authenticating.

This request must be performed using TLS. The server must protect the client_secret against brute force attacks.

If the client was never issued with a client_secret (e.g. because it's a public client) it only needs to send client_id in the token request. This should not be treated as trustworthy and should only be used for informing the user, gathering stats, etc.

The server may support other authentication mechanisms if it wants to.

Request parameters

As well its authentication credentials, the client passes the following url-encoded parameters in the body of the POST request:

  • grant_type - required, one of:
    • authorization_code if the client used response_type=code previously
    • password if using the resource owner password credentials flow
    • client_credentials if using the client credentials flow
    • refresh_token if the client was previously issued with a refresh token
    • An absolute URI indicates an extension type
  • code - required if grant_type=authorization_code. The value of the code parameter given to the client by the provider in the previous step.
  • username - required if grant_type=password.
  • password - required if grant_type=password.
  • redirect_uri - required if this value was included in the authorization request. Must be identical to the previous value. Not required if there was no authorization request, for example if grant_type=password.
  • scope - optional, space-separated list of scope names. If no scope is requested, the provider must assume an implicit default scope or treat the request as invalid. The client includes the scope as this stage if there was no previous authorization request, for example if grant_type=password.

If using an extension type, the client must include whichever parameters are required by that type, for example for SAML 2.0 assertions:

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded;charset=UTF-8

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Asaml2-
bearer&assertion=PEFzc2VydGlvbiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDU

The provider must, where applicable:

  • authenticate the client if it was issued with credentials
  • check the authorization code was issued to that client
  • verify the authorization code is valid
  • check the refresh token is valid and was issued to that client
  • if the redirect_uri param was used in the authorization request, check it is present in the token request and has the same value
  • validate the given scope(s)
  • carry out any validation required by an extension type

Response format

The provider must return a JSON document using the application/json content type and the Cache-Control: no-store and Pragma: no-cache headers. The client must ignore parameters it does not recognize.

The status code is 200 for a successful response, and 400 or 401 for an error response.

e.g.:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

Success response

If the request is valid, the server issues a 200 response containing a JSON document with the following fields:

  • access_token - a token representting the resource owner's access grant to the client, encapsulating the grant's scope(s) and duration. The client uses this token to access protected resources.
  • token_type - required, case-insensitive value that tells the client what type of token is being issued and how to use it. This will either be a registered name (e.g. bearer, mac) or an absolute URI.
  • expires_in - recommended, the lifetime in seconds of the access token. If omitted, the provider should document the default value.
  • refresh_token - optional, a token that can be used to obtain new access tokens using the same authorization grant.
  • scope - if applicable, a space-separated list of the scopes that the resource owner granted the client access to. This may be a subset of the scopes requested by the client, based on the provider's policies and the resource owner's instructions. The server must include this if it differs from what the client requested.

Error response

If the request is not valid, the server issues a 400 response containing a JSON document with the following fields:

  • error - required, one of:
    • invalid_request - the request was missing a required parameter, includes invalid values or is otherwise malformed
    • invalid_client - the client did not authenticate successfully. The provider may return a 401 to indicate which authentication methods are supported. If the client attempted to authenticate via the Authorization header, the provider must respond with a 401 and use the WWW-Authenticate header matching the authentication scheme used by the client.
    • invalid_grant - the provided authorization grant (e.g. authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.
    • unauthorized_client - the client cannot request authorization via this grant type
    • unsupported_grant_type - the requested grant_type is not supported
    • invalid_scope - the requested scope is invalid, unknown, or malformed
  • error_description - optional text explanation of the error
  • error_uri - URI of a page giving more information about the error

Accessing a protected resource

Once the client has obtained an access token it can use it to make authenticated requests on the resource owner's behalf. The resource server checks the token is valid, not expired, and carries sufficient scope to access/modify the requested resource.

The method by which the token is embedded in the request depends on the token_type the token was issued with, but typically involves data transferred in the Authorization header. The particular syntax, as well as the format of error responses, is defined by other specs.

Clients must not use token types that they do not understand. Some examples of standardized token types follow.

These tokens have token_type=bearer. They are opaque values that either encode the authorization using a cryptographic signature, or are identifiers that refer to authorization information stored on the provider's servers. If the latter, they should have negligible probability (must be less than 2^-128, preferably less than 2^-160) of being guessed.

Request format

Bearer tokens may be transmitted using the Authorization header using the Bearer scheme. They must only be transmitted via TLS. They are confined to the character set used in base64-encoding, but are not actually encoded as such before transmission - they are included literally in the request.

GET /resource/1 HTTP/1.1
Host: example.com
Authorization: Bearer mF_9.B5f-4.1JqM

They may be embedded in the body of an application/x-www-form-urlencoded request using the access_token parameter, if all the following are true:

  • The request includes the header Content-Type: application/x-www-form-urlencoded
  • The entity body is correctly url-encoded
  • The entity body is single-part (e.g. no file uploads)
  • The entity body consists only of ASCII characters
  • The HTTP verb has defined semantics for the body, e.g. GET is not allowed

Finally, the token may be included as a query string parameter. Clients must send the header Cache-Control: no-store with such requests, and the provider should return the header Cache-Control: private with 200 responses.

Clients should use, in order of preference for security reasons: the Authorization header, entity body encoding, then query-string encoding. They should use each scheme only if they are incapable of using more desirable scheme. Providers should make sure tokens do not appear in HTTP logs.

Error responses

If the token is not valid for accessing the requested resource, the provider returns an error using the WWW-Authenticate header, e.g.:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example",
                  error="invalid_token",
                  error_description="The access token expired"

The header uses the Bearer scheme and may include the following parameters. The provider should not include specific error information if the request contained no (or unrecognized) authentication data.

  • realm
  • scope - a space-separated list of scopes required to access the resource
  • error - if the request contained an access token that was not valid, this value can be one of the following. Associated status codes are given in brackets.
    • invalid_request [400] - required parameter missing, request malformed
    • invalid_token [401] - the token is expired, revoked, malformed, etc.
    • insufficient_scope [403] - the token does not have all the scope(s) required to access the resource
  • error_description - human-readable explanation of the error
  • error_uri - URI of a page containing more information about the error

In addition to the statuses 400, 401 and 403 listed above, the server may return 405 (Method Not Allowed) in response to a failed request.

These tokens have token_type=mac and are issued as a token along with the mac_key and mac_algorithm parameters. They must be issued over TLS.

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"SlAV32hkKG",
  "token_type":"mac",
  "expires_in":3600,
  "refresh_token":"8xLOxBtZp8",
  "mac_key":"adijq39jdlaska9asud",
  "mac_algorithm":"hmac-sha-256"
}

access_token and mac_key are opaque values. mac_algorithm may be one of hmac-sha-1 or hmac-sha-256; this tells the client how to calculate MACs when using this token type. These values must not include characters other than %x20-21 / %x23-5B / %x5D-7E.

Request format

MAC tokens are transmitted using the Authorization header using the MAC scheme. The client calculates a MAC based on the access token parameters and the properties of the request, and embeds the result in this header, e.g.:

GET /resource/1?b=1&a=2 HTTP/1.1
Host: example.com
Authorization: MAC id="h480djs93hd8",
                   ts="1336363200",
                   nonce="dj83hs9s",
                   mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM="
                   ext="an optional value"

These requests can be made over a plaintext connection.

The client constructs the request as follows. It first generates the current Unix timestamp (ts) and a random string (nonce). It then generates a normalized request string (NRS) by concatenating the follow values separated by a newline 0x0A character:

  • The current Unix timestamp ts
  • The nonce nonce
  • The uppercase HTTP method e.g. GET, POST, etc.
  • The request URI
  • The hostname as contained in the Host header
  • The port number as contained in the Host header, or the default port number for the request scheme (80 for HTTP, 443 for HTTPS)
  • The value of the ext Authorization field if present

For example the HTTP request

POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b&c2&a3=2+q HTTP/1.1
Host: example.com

Hello World!

along with the timestamp 264095:7d8f3e4a, nonce 7d8f3e4a and ext value a,b,c produces the NRS:

264095\n
7d8f3e4a\n
POST\n
/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b&c2&a3=2+q\n
example.com\n
80\n
a,b,c\n

The client then calculates mac = HMAC(key, text) where HMAC is either hmac-sha-1 or hmac-sha-256 as required by the provider, key is the issued mac_key and text is the NRS value. It then constructs the Authorization header value by including the ts, nonce, mac, ext and id values, where id is the value of access_token issued by the provider that identifies the key being used so the provider can verify it.

The provider must verify the MAC by using the id to look up the key in its database and recalculating the MAC by the above steps. It must check that the (ts,nonce,mac) combination has never been received before. It must also verify that the token has not expired, has not been revoked, and covers the scope(s) required by the requested resource.

The server can reject requests whose timestamps are beyond a certain threshold, so that it does not have to store an indefinite number of MACs to prevent replay attacks.

The first time the provider receives a request with a given key identifier id, it stores the request time delta, the difference between the request timestamp ts and the server's clock. For every subsequent request using the same id, it calculates the adjusted request time by applying the delta to the request timestamp to adjust the time to its own clock. If the adjusted time is too far in the past (allowing for reasonable network latency) the request is rejected.

Error response

If the token verification fails, the provider returns a 401 response with the WWW-Authenticate header, e.g.:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: MAC error="The MAC credentials expired"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment