The OAuth plug-in issues OAuth 2.0 access tokens as JSON Web Tokens (JWTs) that comply with RFC 9068. Use this plug-in to authenticate API consumers with the Client Credentials grant type on the MSE cloud-native gateway.
Plug-in type: Authentication
How it works
The OAuth plug-in handles two responsibilities: token issuance and request authentication.
A consumer sends a token request with its
client_idandclient_secretto the gateway's token endpoint (default:/oauth2/token).The gateway validates the credentials against the configured
consumerslist and issues a signed JWT.The consumer includes the JWT in subsequent API requests via the
Authorization: Bearer <token>header.The gateway verifies the JWT signature, expiration, and clock skew before forwarding the request to the backend service.
After successful authentication, the gateway adds an
X-Mse-Consumerheader to the upstream request, identifying the consumer.
The examples in this topic use the Client Credentials grant type (client_credentials).
Configuration fields
Authorization settings
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
consumers | array of object | Yes | - | Consumers authorized to request tokens. See Consumer fields for the object schema. |
issuer | string | No | Higress-Gateway | Value of the iss claim in issued JWTs. |
auth_path | string | No | /oauth2/token | Path suffix that triggers token issuance instead of request forwarding. When configured at the route level, the route must match this path. If you use the API management feature, create an API with the same path. |
global_credentials | bool | No | true | When true, a token issued on any route is accepted by all routes that share the same plug-in configuration. When false, tokens are scoped to the route that issued them. |
auth_header_name | string | No | Authorization | Request header from which the gateway reads the JWT. |
token_ttl | number | No | 7200 | Token time to live (TTL) in seconds. |
clock_skew_seconds | number | No | 60 | Allowed clock skew in seconds when validating the exp and iat claims. |
keep_token | bool | No | true | When true, the JWT is retained in the request forwarded to the backend service. Set to false to strip the token before forwarding. |
global_auth | array of string | No (required for instance-level configurations) | - | Instance-level only. When set to true, authentication applies to all routes and domain names. When set to false, authentication applies only to routes and domain names that have the plug-in configured. If omitted, authentication applies globally only when no route- or domain-level configuration exists. |
Consumer fields
Each object in the consumers array requires these fields:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique consumer name. This value appears in the X-Mse-Consumer upstream header after authentication. |
client_id | string | Yes | OAuth 2.0 client identifier. |
client_secret | string | Yes | OAuth 2.0 client secret. |
Access control (route or domain level)
Configure the allow field at the route or domain level to restrict which consumers can access specific routes or domain names.
| Field | Type | Required | Description |
|---|---|---|---|
allow | array of string | No (route or domain level only) | List of consumer names permitted to access the route or domain. Not configurable at the instance level. |
The allow field and the consumers field cannot coexist in the same configuration rule. Define consumers at the instance level, then use allow at the route or domain level.
Token endpoint behavior
When a route has the plug-in enabled and the request path matches
auth_path, the gateway issues a token instead of forwarding the request to the backend service.If
global_credentialsisfalse, do not use exact match on the route with the plug-in enabled. A coexisting route that uses prefix match may cause unexpected behavior.
Configuration examples
Route-scoped tokens
Apply this configuration to both route-a and route-b:
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxxWith this setup, a token issued through route-a is valid only for route-a. The same token cannot access route-b, and vice versa.
Shared tokens across routes
To share tokens across all routes that use the same configuration, set global_credentials to true:
global_credentials: true
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxxA token issued on any route is now accepted by all routes sharing this configuration.
Instance-level consumers with route-level access control
Define consumers at the instance level and restrict access per route or domain.
Instance-level configuration:
global_auth: false
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: consumer2
client_id: 87654321-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: hgfedcba-xxxx-xxxx-xxxx-xxxxxxxxxxxxRoute-level configuration (applied to route-a and route-b):
allow:
- consumer1Domain-level configuration (applied to *.example.com and test.com):
allow:
- consumer2In this setup:
Requests matching
route-aorroute-bare accessible only toconsumer1.Requests matching
*.example.comortest.comare accessible only toconsumer2.All other consumers are denied access.
If a JWT matches multiple JSON Web Key Sets (JWKSs), the first matching
consumerin the configuration sequence takes precedence.
Gateway-wide authentication
Require all requests to authenticate before reaching any backend service:
global_auth: true
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: consumer2
client_id: 87654321-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: hgfedcba-xxxx-xxxx-xxxx-xxxxxxxxxxxxRequest examples
All examples use the Client Credentials grant type to obtain an access token, then use the token to call a protected API.
Obtain an access token
GET request (recommended):
curl 'http://test.com/oauth2/token?grant_type=client_credentials&client_id=12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx&client_secret=abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx'POST request:
curl 'http://test.com/oauth2/token' \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials&client_id=12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx&client_secret=abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx'The POST method requires a route that points to a real backend service. Otherwise, the gateway does not read the request body.
Response:
{
"token_type": "bearer",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6ImFwcGxpY2F0aW9uXC9hdCtqd3QifQ.eyJhdWQiOiJkZWZhdWx0IiwiY2xpZW50X2lkIjoiMTIzNDU2NzgteHh4eC14eHh4LXh4eHgteHh4eHh4eHh4eHh4IiwiZXhwIjoxNjg3OTUxNDYzLCJpYXQiOjE2ODc5NDQyNjMsImlzcyI6IkhpZ3Jlc3MtR2F0ZXdheSIsImp0aSI6IjEwOTU5ZDFiLThkNjEtNGRlYy1iZWE3LTk0ODEwMzc1YjYzYyIsInN1YiI6ImNvbnN1bWVyMSJ9.NkT_rG3DcV9543vBQgneVqoGfIhVeOuUBwLJJ4Wycb0",
"expires_in": 7200
}| Field | Description |
|---|---|
token_type | Token type. Always bearer. |
access_token | Signed JWT. Include this value in the Authorization header for subsequent requests. |
expires_in | Token validity period in seconds. Matches the configured token_ttl (default: 7200). |
Call a protected API
Include the access token in the Authorization header:
curl 'http://test.com' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6ImFwcGxpY2F0aW9uXC9hdCtqd3QifQ.eyJhdWQiOiJkZWZhdWx0IiwiY2xpZW50X2lkIjoiMTIzNDU2NzgteHh4eC14eHh4LXh4eHgteHh4eHh4eHh4eHh4IiwiZXhwIjoxNjg3OTUxNDYzLCJpYXQiOjE2ODc5NDQyNjMsImlzcyI6IkhpZ3Jlc3MtR2F0ZXdheSIsImp0aSI6IjEwOTU5ZDFiLThkNjEtNGRlYy1iZWE3LTk0ODEwMzc1YjYzYyIsInN1YiI6ImNvbnN1bWVyMSJ9.NkT_rG3DcV9543vBQgneVqoGfIhVeOuUBwLJJ4Wycb0'Upstream headers
After successful authentication, the gateway adds the following header to the request forwarded to the backend service:
| Header | Description |
|---|---|
X-Mse-Consumer | The name of the authenticated consumer. Use this header in your backend service to identify which consumer sent the request and implement consumer-specific logic. |
Error codes
| HTTP status code | Error message | Cause | What to check |
|---|---|---|---|
| 401 | Invalid Jwt token. | The request has no JWT, the JWT format is invalid, or the JWT has expired. | Verify that the Authorization header contains a valid Bearer <token> value. Check whether the token has exceeded its token_ttl. If the error occurs near token expiration boundaries, increase clock_skew_seconds. |
| 403 | Access Denied. | The consumer is authenticated but not authorized to access the requested route or domain. | Check the allow list on the target route or domain configuration. Make sure the consumer's name is included. |