Token Management
The primary goal of authenticating with remote systems is to ultimately use the resulting credentials (access tokens, API keys, etc) to interact with a remote server. Authentication is only a means to an end, providing a client with access to a remote Resource Server.
As a result, the Okta Client SDK features a central set of capabilities which validates, stores, secures, and maintains the lifecycle of these credentials to enable client applications to simply and securely access remote API resources.
Problem statement
The primary goal of the token management system is to simplify the storage and retrieval of tokens, allowing developers to focus on their use-cases as opposed to fighting with the security and encryption lifecycle of these tokens. A lot of real-world concerns exist within the area of token storage, such as:
- Multi-threaded access to shared resources
- Data race conditions during asynchronous operations
- Biometrics and secure storage requirements
- Developer features for querying & differentiating tokens
Some of these are obvious, but there are a few important scenarios that should be called out explicitly.
Multi-token storage
The Token object is the logical representation of the full details of a token response from an Authorization Server, including the access_token, refresh_token, id_token, and other values. It is intended to be an immutable object which can safely be passed across threading contexts, and avoiding race conditions. It also aims to ensure all token strings associated with a logical identity are consistently stored together.
There are scenarios where a developer will want to store / manage multiple sets of these tokens, keeping them alive/refreshed, to support advanced patterns such as multiuser apps, different sets of tokens with escalated scopes or permissions, or to manage tokens that reference different authroization servers altogether.
This token storage system therefore provides capabilities that enable the CRUD operations against secure storage, as well as advanced query features to simplify how develoeprs can filter out the appropriate token for their specific use-case.
Common concurrent operations
Not only can developers store multiple sets of tokens, this token storage system supports the idea of a "default" token, simplifying common single-user applications. Tokens are often fetched, defaults assigned, and tokens refreshed all within a short period of time. Furthermore, developer applications are often quite complex, with different parts of their app performing multiple actions simultaneously.
As a result, this token storage system should be very stable and thread-safe, guaranteeing concurrent access to shared resources.
For example, when a token is refreshed, its current value is first fetched from storage to trigger a refresh operation, and then its value is merged and updated with the new response before being updated back in storage. During the runtime of an app, especially during application start/initialization, many concurrent operations are made to Token Storage which can result in data races that affect the stability of the SDK and the developer's app.
Feature design
The public API surface area of this feature should be as convenient as possible for developers, ensuring that all access patterns support synchronous access. The primary capabilities are:
- Developer stores a Token with an optional set of tags and security options, returning a Credential that wraps it.
- Developer gets a single Token by its unique ID.
- Developer assigns tags to a Credential that can be used to fetch it later.
- Developer fetches an array of Credentials filtered using tags assigned to it.
- Developer fetches an array of Credentials filtered using claims present in the ID Token.
- Developer updates tags assigned to a Credential by setting them to the object's
tagsproperty. - Developer removes a Credential from storage without revoking it.
- A Credential is automatically removed from storage when it is revoked.
- A Token is automatically updated in storage when it is refreshed.
- Only a single instance of a Credential should be created for a corresponding Token.
Architecture Design
This consists of multiple capabilities and classes, but overall this area is described as the Token Management system. The primary components are:
| Name | Description |
|---|---|
| Credential | Wrapper that provides a runtime abstraction for a Token. |
| Token | Immutable value that represents a set of OAuth2 tokens. |
| TokenStorage | Implements the secure storage of OAuth2 Token objects. |
| CredentialDataSource | Factory that produces Credential instances, ensuring consistent shared access to these Credential objects. |
| CredentialCoordinator | Orchestrates the relationship between the TokenStorage, CredentialDataSource, Token, and Credential objects. |
Entity Relationship
📄️ Credential
Convenience class that provides methods and properties for using a user's authentication tokens.
📄️ Credential Coordinator
Coordinates interactions between Token Storage, Credentials, and OAuth2 token refreshes.
📄️ Credential Data Source
Factory class that ensures only a single Credential instance exists for a given Token.
📄️ Token Metadata
Stores contextual and less-sensitive information about the token.
📄️ Token Storage
Manages the secure storage and retrieval of Tokens.