Skip to main content
  1. Posts/

Understanding other oAuth flows

·13 mins
oAuth pprovides more flows than just the authentication of a user, but also authentication of services against one Authentication Server or a federated net of Authentication Servers.

What was the basic oAuth flow and what to know additionally? #

The Authorication Code Flow is the most common flow for authenticating a human against an Authorization Server, often combined with Open ID Connect. This flow is described in my last Post: Understanding oAuth Authentication Code Flow.

Terminology #

Actors:

  • Resource Owner - The human that can authenticate
  • Resource Server - The server that holds the resources, for basic authentication this might be only the user identifier
  • Client - The service that wants to know who the user is
  • Authorization Server - The server that authenticates the user and issues tokens
  • Access Token - credentials used to access protected resources.
  • Refresh Token - credentials used to obtain a new access token when the current access token becomes invalid.

The flows are defined in the RFC 67491, while I found the best overview at curity.io 2 and Auth0 3.

Client Credentials Flow - A Service wants to authenticate against another service without a user present #

This flow is used when the Client wants to authenticate itself against another service without an involved Human. This does not authenticate the Client as a specific user, but as a service; therefore, the Client does not get access to any human resources.

The benefit of this flow is that the Client can authenticate itself (e.g. against other microservices) in an unified way, with the possibility to change permissions on the fly, while minimizing the number of calls using static credentials.

  1. The client authenticates with the authorization server and requests an access token from the token endpoint with its client_id, client_secret, and scope.

    client_id, client_secret: The credentials of the Client to authenticate itself against the Authorization

  2. The authorization server authenticates the client, and if valid, issues an access token.

CClliie(<en2nt)t{a(c1c)es{sc_ltioeknetn_,ide,xpcilrieesn_ti_ns,ecsrceotp,e,sctooApkAueeut}nth_hotoryripizeza>}attiioonnSSeerrvveerr

Why use the Client Credentials Flow? #

From the first look, the Client Credentials Flow seems to use static credentials, and there are not many benefits over API keys. From the security perspective, the Client Credentials Flow requests the authentication token from a Authentication Server, which might revoke the token if the Client is compromised (without an additional deployment). The Client can also request a new token with the refresh token, which is not possible with API keys.

From a human developer perspective, the Client Credentials Flow is a unified way to authenticate services in a microservice environment. Unified means, testers or developers who join the team don’t need to be introduced to the authentication of the service whenever they switch teams, but can use the same flow for all services. DevOps Engineers can rely on the same flow for all services, which makes the deployment and monitoring easier.

Such human benefits are often underestimated, but neglecting them causes a lot of friction in the development process, deployment delays, and security issues.

Refresh - A Service has a token and wants a new token without the user present #

To keep the access token short-lived and the user experience smooth, the Client can request a new access token with the refresh token. To prevent malicious usage, the refresh_token in the response is not the same as the one that was sent and the Client has to update their refresh token.

CClliie(<en2nt)t{a(c1c)es{sc_ltioeknetn_,idr,efcrleisehn_tt_oskeecnr,ete,xprierferse_sihn_,toskceonp,e,sctooApkAueeut}nth_hotoryripizeza>}attiioonnSSeerrvveerr

Token Exchange Flow - A Service has a token, but wants one from a different Authentication Authority #

If a Client has an access token from one Authorization Server, it may need a token from the same Authorization Server with other privileges to conform the least-privileges principle. Token exchange also allows you to cross security domains by exchanging a token from one trust domain to a token of a different one 4.

  • Subject token - The token that the Client has
  • Actor token - Optional Deligation token if the Client acts on behalf of a human or another service
  • Requested token - The token that the Authorization Server returns

In processing the request, the authorization server must perform the appropriate validation procedures for the indicated token type and, if the actor token is present, also perform the appropriate validation procedures for its indicated token type.

CClliie(<en1nt)t({2a)ct{oarc_cteoskse_nt,okaecnt,ori_stsoukeedn__ttoykpeen,_tsyupbej,ectto_kteonk_etny,pes,ubejxepcitr_etso_kienn,_tsAycAupoutepth,eho}orriiz.za}>attiioonnSSeerrvveerr
{
  "type": "object",
  "description": "The Request of the Client to exchange the code for an access token.",
  "properties": {
    "grant_type": {
      "type": "string",
      "description": "The value must be `urn:ietf:params:oauth:token-type:access_token` to indicate that a token exchange is being performed"
    },
    "resource": {
      "type": "string",
      "description": "A URI that indicates the target service or resource where the client intends to use the requested security token"
    },
    "audience": {
      "type": "string",
      "description": "The logical name of the target service where the client intends to use the requested security token"
    },
    "scope": {
      "type": "array",
      "description": "Additional (space separated) resources of the requested token"
    },
    "requested_token_type": {
      "type": "array",
      "description": "An identifier for the type of the requested security token"
    },
    "subject_token": {
      "type": "array",
      "description": "A security token that represents the identity of the party on behalf of whom the request is being made"
    },
    "subject_token_type": {
      "type": "array",
      "description": "An identifier for the type of the subject token"
    },
    "actor_token": {
      "type": "array",
      "description": "A security token that represents the identity of the acting party"
    },
    "actor_token_type": {
      "type": "array",
      "description": "An identifier for the type of the actor token. Required when the actor_token parameter is present"
    }
  },
  "required": [
    "grant_type",
    "subject_token",
    "subject_token_type"
  ]
}

The Token Types are defined in the RFC, for example urn:ietf:params:oauth:token-type:access_token Indicates that the token is an OAuth access token issued by the given authorization server.

Why use the Token Exchange Flow? #

A big request for the Token Exchange Flow is to enable a federated network of Authentication Authorities. Here are some use cases5:

Pipelines with tokens instead of credentials

CI/CD pipelines that need to authenticate against a different service to upload artifacts or read secrets. Github (but also Gitlab) injects a token into the pipeline 6, which can be used to access some github endpoints, but also to be exchanged for a token from another Authentication Authority, like your artifact repository 7, or accessing some other resources. For this to work, the Authentication Authority must trust the token from Github, and still validate the token (not every Github token is should access the resources).

Using federated authentication to authenticate against third parties

When working with third parties authentication is often a pain, eased by static credentials. But with the Token Exchange Flow, the Client can authenticate against the third party with a token from the Authorization Server (if both parties are willing to use oAuth). Again the Authorization Server must trust the third party Authorisation Server and validate the token.

One benefit of this is again that the token may be revoked. If a service is deployed already with token (like some cloud provider already inject into the container), the token can be exchanged at the third party Authentication Server for a third party resource authorizing token. After a new deployment, the previous token can be revoked, meaning in case of a breach a redeployment is enough to revoke the access.

Again, this requires trust between the Authentication Authorities.

Up or downstreaming tokens

Sometimes, the current token is not enough to access a resource, but the Client can exchange the token for a new one with higher privileges - or vice versa (to not use a token with too many privileges, following the least-privileges principle).

Connect a service with oAuth that otherwise does not comply

Imagine working on a project, that does not support oAuth, while all other surrounding (and depending) services do. Exchanging an injected token for a token that the service can use to authenticate against other services might be a first step into a more secure and easier to use environment.

Other flows #

There are more flows in the oAuth specification, which are not covered in this post, but honorable mentions are:

Device Flow: A Service wants to authenticate a User on a device with limited input capabilities (e.g. a TV)

Revoke Flow: A Service wants to invalidate a refresh token before it expires

Conclusion #

oAuth can do so much more than just authenticate a human against an Authorization Server.

I made the experience, that developer do underestimate the benefits of using oAuth for services, and overestimate the complexity (if libraries are used). Not only user facing, but also in the pure service-to-service communication, oAuth provides a unified way to authenticate services over different domains.

Happy Coding :)