User Authentication Behaviour

Background

Previously we explained this blog’s API Authorization Behaviour. Next we provide a brief overview of some key points that should be understood about user authentication in an OAuth context.

The OpenID Connect Code Flow

OAuth clients must never implement user authentication in code. Instead  they run an authorization code flow to hand over authentication to the authorization server, as explained in the earlier post on OAuth Messages:

This enables many possible ways to login, including those listed below. Multiple authentication factors can be chained together, and the authentication complexity is externalized to the authorization server:

  • Default passwords, as used in this blog
  • Email or phone verification
  • External identity providers (business partner)
  • External identity providers (social logins)
  • Cryptography backed options such as passkeys or digital wallets
  • Custom authentication using the authorization server’s plugin system

In all cases, once authentication completes, the client receives an authorization code in the browser response. This is swapped for tokens, after which the client receives an access token from the authorization server and can call APIs in the correct manner.

Identity Provider Logins

If an application integrates directly with an external identity provider, an authorization code flow is run, but not against the authorization server. This is incorrect, and limits your login options to a single identity provider.

In addition, the client will also receive foreign access tokens, which are not designed to protect your APIs. Instead, your APIs must only ever receive access tokens issued by your company’s own authorization server.

The correct flow is shown below, where the application performs an authorization code flow against the authorization server. The authorization server then runs a second authentication code flow against the identity provider:

The end-to-end flow then works as follows:

  • Add the identity provider as an authentication method in the authorization server and assign an issuer URI, client ID, client secret and scope
  • Register the authorization server as a client in the identity provider and assign a redirect URI, client ID, client secret and scope

The exact details will vary depending on particular systems, the standards they use, and their published values for federation. In AWS Cognito, an OpenID Connect identity provider is added in the following screen:

When a client is configured to use an external OpenID Connect identity provider, the following main steps occur:

  • Client redirects to authorization server (AS)
  • AS redirects to identity provider (IDP)
  • User authenticates at the IDP
  • IDP returns an authorization code to the AS
  • AS posts the IDP code back to the IDP to get IDP tokens
  • AS returns an authorization code to the client
  • Client posts the AS code back to the AS to get AS tokens

Consistent API User Identity

The deeper behaviour when designing authentication is ensuring that access tokens and their user identities remain the same when the user’s primary authentication factor changes. This blog’s AWS Cognito access token is designed to contain the following fields:

The process of matching logins to existing accounts is called Account Linking. It requires some data storage in the authorization server and you must also define the logic that will be used to link accounts.

In simple cases you may be able to deterministically match up user attributes, such as by email address. Another option is to force the user to login with their existing method as part of onboarding to a new one.

Yet many authorization servers have poor support for account linking. Without care, there can be outcomes such as duplication of user accounts, which damage the integrity of both your identity and business data.

Custom Authentication

Any authentication method implemented by the authorization server consists of the following steps:

  • HTML forms that run in the browser and interact with the server
  • Authorization server endpoints that validate proofs
  • Authorization server logic that assign the user identity

A good authorization server provides extensibility to enable any possible authentication method, using these three main steps. Therefore you should not be restricted by the default login options provided.

Where Are We?

We have explained how OAuth enables users to be authenticated in many ways. When reviewing an authorization server’s authentication support, judge it by the built-in authentication methods, the account linking features and the extensibility.

Next Steps