-
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
111 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,156 +1,167 @@ | ||
--- | ||
title: Opaque token | ||
tags: [oauth 2.0, oidc] | ||
description: An opaque token is a type of token whose format is determined by the issuer, typically appearing as a string of characters or numbers, and requires validation by the issuer rather than containing all necessary information for direct validation. | ||
description: An opaque token is a random, unique string that is meaningless to the client but serves as a reference key to lookup authorization data in the server's database. | ||
--- | ||
|
||
## What is token? | ||
|
||
Before introducing opaque tokens, it’s important to understand what a token is: | ||
|
||
Tokens are used to represent and transmit secure information between parties, and they support the vast majority of <Ref slug="authentication" /> and <Ref slug="authorization" /> processes that occur on the internet behind the scenes. The two most popular types of tokens in web services are <Ref slug="jwt" /> and opaque tokens. | ||
|
||
## What is opaque token? | ||
|
||
Opaque tokens are tokens in a proprietary format that you cannot access and typically contain some identifier to information in a server's persistent storage. | ||
An opaque token is a random, unique string that is meaningless to the client but serves as a reference key to lookup authorization data in the server's database. | ||
|
||
Opaque tokens are usually generated using a <Ref slug='csprng' /> to ensure its unpredictability and security, and its format is determined by its issuer. | ||
|
||
An opaque token is one form a token can take, and <Ref slug="access-token">access tokens</Ref> and <Ref slug="refresh-token">refresh tokens</Ref> can exist as opaque tokens. The format of an opaque token is determined by its issuer, and it is typically a string of numbers and/or characters used to help the issuer retrieve and identify certain information in a database. Here is an example of an opaque token: | ||
Here is an example of an opaque token: | ||
|
||
``` | ||
M-oxIny1RfaFbmjMX54L8Pl-KQEPeQvF6awzjWFA3iq | ||
``` | ||
|
||
On the other hand, JWT is another common token format. It is a JSON string that contains all the claims and information, along with a signature from the issuer. By default, it is not encrypted, though it can be encrypted using the <Ref slug="jwe" /> standard. Even though JWT is typically unencrypted, it does not compromise its security — the presence of the signature ensures the integrity of the token’s contents, allowing full trust in the data inside the JWT. | ||
## What is the difference between JWT (JSON Web Token) and opaque token? | ||
|
||
The main difference lies in how these tokens handle and validate authorization information: | ||
|
||
An opaque token is a random string that contains no information itself. The server must query its backend database to retrieve any authorization data associated with this token. This makes opaque tokens completely dependent on the authorization server for validation and interpretation. | ||
|
||
```mermaid | ||
sequenceDiagram | ||
participant Client as Client | ||
participant ResourceServer as Resource Server | ||
participant AuthServer as Authorization Server | ||
participant Database as Database | ||
autonumber | ||
Client->>ResourceServer: Request with opaque token | ||
ResourceServer->>AuthServer: Validate token | ||
AuthServer->>Database: Query token data | ||
Database->>AuthServer: Return token info | ||
AuthServer->>ResourceServer: Token validation result | ||
ResourceServer->>Client: Response | ||
``` | ||
|
||
Unlike JWT, which contains all the information necessary to be validated directly at the protected resource, opaque tokens cannot be directly validated by the resource. Instead, they require validation by the issuer of the opaque token (usually the <Ref slug="authorization-server" />). This validation process is typically referred to as <Ref slug="token-introspection" />. | ||
|
||
## What is JWT? | ||
JWT is a self-contained token that carries all necessary information within itself. | ||
|
||
In contrast to opaque tokens, a JWT is a self-contained, stateless token that carries information in a structured and readable format. | ||
Here is an example of a JWT, its a base64 encoded string: | ||
|
||
A JWT is composed of three parts: a `header`, a `payload`, and a `signature`, each encoded in Base64URL. | ||
``` | ||
eyJhbGciOiJIUzI1NiIs.eyJzdWIiOiIxMjM0NTY3O.SflKxwRJSMeKKF2QT4f | ||
``` | ||
|
||
Here's an example of a JWT: | ||
And it contains three parts separated by dots: | ||
|
||
`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c` | ||
1. **Header** - Contains information about the type of token and the algorithm used for signing. For example, `{"alg": "HS256", "typ": "JWT"}`. | ||
2. **Payload** - Contains claims—pieces of information about the user or the authorization, like user ID, expiration time, and scopes. Anyone can decode it to see the claims because it's encoded but not encrypted. | ||
3. **Signature** - Generated by combining the header, payload, and a secret key using the specified algorithm. This signature is used to verify the integrity of the token and ensure that it has not been tampered with. | ||
|
||
- The `header` contains information about the type of token and the algorithm used for signing. For example, `{"alg": "HS256", "typ": "JWT"}`. | ||
- The `payload` section contains claims—pieces of information about the user or the authorization—such as user ID, expiration time, and scopes. Because this data is encoded but not encrypted, anyone who has the token can decode it to see the claims, though they cannot alter it without invalidating the signature. Based on the specification and authorization server configuration, various claims can be included in the payload. This gives the token its self-contained nature. For example, `{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}`. | ||
- The `signature` is generated by combining the header, payload, and a secret key using the specified algorithm. This signature is used to verify the integrity of the token and ensure that it has not been tampered with. | ||
This structure allows JWTs to be validated and used without querying a database. | ||
|
||
JWTs are commonly used because they can be verified locally by the client or any service, without needing to interact with the authorization server. This makes JWTs particularly efficient for distributed systems, where multiple services might need to verify the token's authenticity independently. | ||
For more detailed information about JWTs, please refer to <Ref slug='jwt' />. | ||
|
||
However, this convenience also comes with the responsibility of ensuring that the token's claims are not excessively exposed, as they are visible to anyone who has access to the token. Also, JWTs are typically short lived, and the expiration time is included in the token's claims to ensure that the token is not valid indefinitely. | ||
And check out [Opaque token vs JWT](https://blog.logto.io/opaque-token-vs-jwt) to learn more about their differences in more depth. | ||
|
||
## Opaque access token validation | ||
## How to validate opaque token | ||
|
||
A opaque access token is validated by sending it back to the authorization server for verification. The authorization server maintains the state of issued tokens and can determine the token's validity based on its internal storage. | ||
In simple systems, opaque token validation is typically handled directly by the server, which queries the database using the opaque token as a key to retrieve the associated authorization information. | ||
|
||
```mermaid | ||
sequenceDiagram | ||
participant C as Client | ||
participant AS as Authorization Server | ||
participant RP as Resource Provider | ||
C ->> AS: 1. post /oidc/token | ||
AS ->> C: 2. opaque token | ||
C ->> RP: 3. get /api/resource | ||
RP ->> AS: 4. post /oidc/introspect | ||
AS ->> RP: 5. token info | ||
participant S as Server | ||
participant D as Database | ||
autonumber | ||
C->>S: Request with opaque token | ||
S->>D: Query token info | ||
D->>S: Return token data | ||
S->>C: Response | ||
``` | ||
|
||
1. The client requests an access token from the authorization server. | ||
2. The authorization server issues an opaque token. | ||
3. The client sends the resource access request with the opaque token in the header. | ||
4. The resource provider sends a token introspection request to the authorization server to validate the token. | ||
5. The authorization server responds with the token information. | ||
|
||
## JWT access token validation (offline) | ||
|
||
A JWT access token can be validated offline by the client or any service that has access to the token's public key. | ||
In multi-party systems introducing OAuth 2.0, multiple resource servers (see: <Ref slug='resource-server' />) may need to validate the same opaque token. OAuth 2.0 provides a standardized token introspection mechanism for this validation: | ||
|
||
```mermaid | ||
sequenceDiagram | ||
participant C as Client | ||
participant RS as Resource Server | ||
participant AS as Authorization Server | ||
participant RP as Resource Provider | ||
RP ->> AS: 0. get /oidc/.well-known/jwks.json | ||
C ->> AS: 1. post /oidc/token | ||
AS ->> C: 2. JWT token | ||
C ->> RP: 3. get /api/resource | ||
RP ->> RP: 4. validate JWT token | ||
RP ->> C: 5. response 200 OK | ||
participant D as Database | ||
autonumber | ||
C->>RS: Request with opaque token | ||
RS->>AS: POST /oauth/introspect | ||
AS->>D: Validate token | ||
D->>AS: Token info | ||
AS->>RS: Token validation response | ||
RS->>C: Response | ||
``` | ||
|
||
1. The resource provider pre-fetches the authorization server's public key from the <Ref slug="openid-connect-discovery" />. The public key is used to verify the token's signature and ensure its integrity. | ||
2. The client requests an access token from the authorization server. | ||
3. The authorization server issues a JWT token. | ||
4. The client sends the resource access request with the JWT token in the header. | ||
5. The resource provider decodes and validates the JWT token using the public key obtained from the authorization server. | ||
6. The resource provider grants access based on the token's validity. | ||
|
||
## Use cases in OIDC | ||
For detailed information about token introspection, please refer to <Ref slug='token-introspection' />. | ||
|
||
In the context of OIDC (<Ref slug="openid-connect" />), opaque tokens and JWTs serve different purposes and are used in distinct scenarios. | ||
## How are opaque tokens used in OIDC? | ||
|
||
### Opaque tokens | ||
In the context of OIDC (<Ref slug='openid-connect' />), opaque tokens serve specific purposes in different scenarios: | ||
|
||
1. User profile retrieval: | ||
### User profile retrieval | ||
|
||
By default, when a client requests an access token without specifying a resource and includes the `openid` scope, the authorization server issues an opaque access token. This token is primarily used to retrieve user profile information from the OIDC `/oidc/userinfo` endpoint. Upon receiving a request with the opaque access token, the authorization server checks its internal storage to retrieve the associated authorization information and verifies the token's validity before responding with the user profile details. | ||
By default, when a client requests an access token without specifying a resource and includes the `openid` scope, the authorization server issues an opaque access token. This token is primarily used to retrieve user profile information from the OIDC `/oidc/userinfo` endpoint (see: <Ref slug='userinfo-endpoint' />). | ||
|
||
2. Refresh token exchange: | ||
```mermaid | ||
sequenceDiagram | ||
participant Client as Client | ||
participant AuthServer as Authorization Server | ||
participant Database as Database | ||
autonumber | ||
Client->>AuthServer: GET /oidc/userinfo with opaque token | ||
AuthServer->>Database: Query token data | ||
Database->>AuthServer: Return token info | ||
AuthServer->>Client: User profile data | ||
``` | ||
|
||
Refresh tokens are designed to be exchanged only between the client and the authorization server, without needing to be shared with resource providers. As such, refresh tokens are typically issued as opaque tokens. When the current access token expires, the client can use the opaque refresh token to obtain a new access token, ensuring continuous access without re-authenticating the user. | ||
### Refresh token exchange | ||
|
||
### JWTs | ||
Refresh tokens (see: <Ref slug='refresh-token' />) are typically issued as opaque tokens since they are only exchanged between the client and the authorization server. When the current access token expires, the client can use the opaque refresh token to obtain a new access token without re-authenticating the user. | ||
|
||
1. ID token: | ||
```mermaid | ||
sequenceDiagram | ||
participant Client as Client | ||
participant RS as Resource Server | ||
participant AS as Authorization Server | ||
autonumber | ||
Client->>RS: Request with expired access token (JWT) | ||
RS->>Client: 401 Token expired | ||
Client->>AS: POST /oidc/token with refresh token | ||
AS->>Client: New access token | ||
Client->>RS: Request with new access token | ||
RS->>Client: Success response | ||
``` | ||
|
||
In OIDC, the ID token is a JWT that contains user information and is used to authenticate the user. Typically issued alongside the access token, the ID token allows the client to verify the user's identity. For example: | ||
## What are the pros and cons of opaque token? | ||
|
||
```json | ||
// Decoded payload of an ID token | ||
{ | ||
"iss": "<https://auth.wiki>", | ||
"sub": "1234567890", | ||
"aud": "client_id", | ||
"exp": 1630368000, | ||
"name": "John Doe", | ||
"email": "[email protected]", | ||
"picture": "<https://example.com/johndoe.jpg>" | ||
} | ||
### Pros | ||
|
||
``` | ||
- **Security**: Opaque tokens are perfect for handling sensitive data like refresh tokens. Since the content is completely random and meaningless, even if someone intercepts the token, they cannot extract any useful information. This makes them especially valuable in high-security scenarios like banking transactions or handling sensitive user data. | ||
|
||
The client can validate the ID token to ensure the user's identity and extract user information for personalization or authorization purposes. ID token is for one-time use only and should not be used for API resource authorization. | ||
- **Revocability**: The server can immediately invalidate an opaque token at any time. This is particularly useful when you need to quickly remove user access. Unlike JWTs that remain valid until they expire, opaque tokens can be instantly revoked (see: [Limitations of JWT](https://blog.logto.io/why-jwt-in-most-oauth-2-services#hard-to-revoke)). | ||
|
||
2. API resource access (using access token): | ||
- **Size**: Opaque tokens are typically much shorter than JWTs. This smaller size reduces network bandwidth usage and storage requirements. The benefit becomes particularly noticeable in systems that frequently transmit tokens, such as mobile applications or IoT devices. | ||
|
||
When a client requests an access token with a specific <Ref slug="resource-indicator" />, the authorization server issues a JWT access token intended for accessing that resource. The JWT contains claims that the resource provider can use to authorize the client's access. For example: | ||
- **Simplicity**: The implementation of opaque tokens is straightforward. You generate a random string and store it with its associated data. There's no need to handle complex encryption or signature verification like with JWTs. This simplicity makes them ideal for internal system authentication. | ||
|
||
```json | ||
// Decoded payload of a JWT access token | ||
{ | ||
"iss": "<https://auth.wiki>", | ||
"sub": "1234567890", | ||
"aud": "<https://api.example.com>", | ||
"scope": "read write", | ||
"exp": 1630368000 | ||
} | ||
### Cons | ||
|
||
``` | ||
- **Stateful**: Every opaque token requires storage on the server side. This creates additional complexity in distributed systems because token data must be synchronized across multiple servers. For example, if you have multiple authentication servers, they all need access to the same token database or cache system to validate tokens properly. | ||
|
||
The resource provider can validate the request by checking the claims: | ||
- **Performance**: Token validation always requires a database lookup or API call. In high-traffic systems, these extra database queries can create performance bottlenecks. For instance, if your system handles thousands of requests per second, each requiring token validation, the additional database load becomes significant. | ||
|
||
- `iss`: Confirms the token was issued by a trusted authorization server. | ||
- `sub`: Identifies the user associated with the token. | ||
- `aud`: Ensures the token is intended for the specific resource. | ||
- `scope`: Verifies the permissions granted to the user. | ||
- **Interoperability**: Different systems might implement opaque tokens in different ways. This can cause integration challenges when working with third-party services or different authorization servers. While standards like OAuth 2.0 token introspection help, you may still encounter compatibility issues when systems use different token formats or validation methods. | ||
|
||
<SeeAlso slugs={['jwt']} /> | ||
<SeeAlso slugs={[ | ||
'csprng', | ||
'jwt', | ||
'resource-server', | ||
'token-introspection', | ||
'openid-connect', | ||
'refresh-token', | ||
'userinfo-endpoint' | ||
]} /> |