Authentication and authorization are critical parts of most web applications today. As a web developer, you need to understand how to properly implement and use standards like OAuth 2.0 to secure your applications. In this post, we’ll demystify these technologies and show how to put them into practice.
What is OAuth 2.0?
OAuth 2.0 is an authorization framework that enables applications to obtain limited access to user accounts on an HTTP service, such as Facebook, GitHub, and DigitalOcean. It works by delegating user authentication to the service that hosts the user account, and authorizing third-party applications to access the user account.
Here’s a quick overview of how OAuth 2.0 works:
- The user tries to log in to the client app using OAuth.
- The client app redirects the user to the authorization server to log in.
- The user logs in and authorizes the client app.
- The authorization server issues an access token to the client app.
- The client app uses the access token to access the user’s account on the resource server.
- The resource server validates the token and responds with requested data.
The core concepts in OAuth 2.0 include:
- Authorization server – The service that authenticates the user, issues access tokens, and returns information about the token. For example, Facebook’s authorization server.
- Resource server – The API server that hosts the protected user data and resources. For example, Facebook’s Graph API.
- Client – The untrusted third-party application that wants to access the user’s data.
- Access token – The credential that the client uses to access the user’s data on the resource server.
By delegating user authentication to the authorization server, OAuth 2.0 enables applications to obtain limited access to user accounts without exposing user credentials.
OpenID Connect
OpenID Connect (OIDC) is an authentication protocol built on top of OAuth 2.0. It adds an identity layer that allows clients to verify users’ identities via the authorization server.
In addition to the OAuth 2.0 flow, OpenID Connect adds the following steps:
- The client app requests the OpenID scope along with other scopes.
- The authorization server authenticates the user and obtains consent.
- The authorization server returns an ID token containing user profile attributes.
- The client app verifies the ID token signature and parses the claims.
The key difference is the ID token, which contains identity claims about the authenticated user, such as name, email, etc. OpenID Connect allows the client to receive verified information about the user, while OAuth 2.0 only provides access to user resources without identity.
Concrete OAuth Flows
We’ve covered the abstract OAuth flow above which was:
- The client requests authorization from the user to access their data on the resource server.
- The authorization server authenticates the user and obtains their authorization.
- The authorization server issues an access token with specific scopes and expiry time.
- The client uses the access token to make requests to the resource server on behalf of the user.
- The resource server validates the access token and responds with the requested data.
However, this abstract flow has a couple of concrete implementations that you will come across that you will want to be familiar with. Each have their advantages and disadvantages and some vendors or services will only support a certain “concrete” type. Here they are:
Authorization Code Grant Flow
Best for web apps executing logic on a server.
- Client directs user to the OAuth authorization URL including the client ID, requested scopes, local state, and redirect URI.
- User authenticates with authorization server and grants access.
- Authorization server generates an authorization code and redirects back to the client redirect URI along with the code.
- Client exchanges authorization code for an access token by making a POST request to the token endpoint including the client ID, client secret, and redirect URI.
- Authorization server validates the request, generates an access token and refresh token, and responds back to the client.
- Client can use the access token to make API calls to the resource server.
Implicit Grant Flow
Optimized for client-side JavaScript apps where the client secret may be exposed.
- Client directs user to the OAuth authorization URL including the client ID, requested scopes, local state, and redirect URI.
- User authenticates and grants access.
- Authorization server generates an access token and redirects back to the client redirect URI along with the access token in the URL fragment.
- Client can extract access token from the URL fragment and use it to make API calls.
Resource Owner Password Credentials Flow
Allows exchanging user credentials for an access token directly. Avoid this flow due to security risks.
- Client collects username and password from the user.
- Client exchanges username and password with the authorization server token endpoint for an access token.
- Authorization server validates credentials and responds with an access token.
- Client uses access token to make API calls.
Client Credentials Flow
Allows client to request an access token using only its client credentials for non-user resources.
- Client authenticates with the authorization server token endpoint using its client ID and client secret.
- Authorization server validates the credentials and responds with an access token.
- Client uses access token to access protected resources.
That covers the most popular concrete OAuth flows. Nows lets go into a bit more detail about those tokens!
Access Token Scope
Access tokens represent specific scopes and durations of access requested by the client application. Some examples:
read
– Allows reading resources and datawrite
– Allows modifying resources and dataemail
– Grants access to a user’s emailoffline_access
– Allows refreshing expired tokens
Access Token Lifetime
Access tokens eventually expire after a set lifetime, usually on the order of hours. When an access token expires, the client must obtain a new one using the refresh token.
Refresh Tokens
Refresh tokens allow the client to request new access tokens after expiration without going through the full authorization code flow again. However, refresh tokens also expire to maintain security.
OpenID Connect
OpenID Connect builds identity authentication on top of the OAuth 2.0 framework. Let’s explore the detailed flow:
Additional OpenID Connect Flow
On top of the OAuth 2.0 protocol, OpenID Connect adds identity verification through signed JSON Web Tokens (JWTs).
- Along with requesting OAuth access, the client also requests the
openid
scope. - After OAuth authorization, the authorization server also generates a signed ID Token containing user identity claims.
- The authorization server includes the ID Token in the OAuth token response.
- The client validates the ID Token signature and parses the claims to identity the user.
ID Token JWT Structure
The ID Token is a JSON Web Token (JWT) with three parts:
- Header – Contains the JWT type (“JWT”) and signature algorithm (like “RS256”).
- Payload – Contains identity claims about the user like
sub
,name
,email
. Also contains issuer, expiry time, audience, etc. - Signature – Generated by signing the header and claims payload. The client validates this signature against the issuer’s public keys.
Here is an example JWT ID Token:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.EkN-DOsnsuRjRO6BxXemmJDm3HbxrbRzXglbN2S4sOkopdU4IsDxTI8jO19W_A4K8ZPJijNLis4EZsHeY559a4DFOd50_OqgHGuERTqYZyuhtF39yxJPAjUESwxk2J5k_4zM3O-vtd1Ghyo4IbqKKSy6J9mTniYJPenn5-HIirE
Standard Identity Claims
OpenID Connect defines a set of standard claims that are included in the ID Token:
iss
– The issuer URLsub
– The subject identifieraud
– The target audienceexp
– The expiry timeiat
– The issue timeauth_time
– When authentication occurrednonce
– Unique state stringacr
– Authentication context classamr
– Authentication methods referencedazp
– Authorized party
Client Validation
When receiving an ID Token, the client must:
- Validate the issuer matches the authorization server URI.
- Check that the token is not expired.
- Verify the nonce to prevent replay attacks.
- Validate the signature against the issuer’s public keys.
This covers all the most important details around OAuth 2.0 and OpenID Connect so that you can start working with and integrating with systems and services that use them. Let me know if you need any clarification or have additional questions in the comments!