Client Credentials (OIDC) – M2M JWT access token (60 min TTL)
Summary of use case
Section titled “Summary of use case”This use case shows how to configure FortifiedID Access (OIDC OP) to support the client_credentials grant for Machine-to-Machine (M2M), where the token endpoint returns an access token as a JWT created by a pipe with TTL = 60 minutes.
(Ref: OIDC Authorization Code Flow / client_credentials section – docs.fortifiedid.se)
Scenario
Section titled “Scenario”A backend service (OIDC Relying Party / client) authenticates with client_id and client_secret against the OIDC token endpoint using grant_type=client_credentials.
FortifiedID Access executes a dedicated client credentials pipe, which:
- Creates a signed JWT access token.
- Sets the token lifetime to 60 minutes.
- Returns a standard OAuth 2.0 token response:
access_tokentoken_typeexpires_inscope
This flow is typically used for machine-to-machine (M2M) integrations where no end-user is involved.
Prerequisite
Section titled “Prerequisite”There are some prerequisites for this use case. You will need the following:
- A running FortifiedID Access installation.
- A completed OIDC basic installation (tenant/provider configured and reachable).
- An OIDC Relying Party (RP) configured with:
client_idclient_secret
- A configured keystore containing a signing certificate for JWT creation.
- Good knowledge of FortifiedID Access, including:
- Understanding of pipes and valves
- Basic OIDC configuration structure
- How to edit and deploy configuration files
- How the OIDC module and RP configuration are structured
Note: This guide assumes you are familiar with FortifiedID Access configuration concepts and comfortable modifying OIDC module settings.
Configuration
Section titled “Configuration”1. Enable client_credentials in discovery metadata
Section titled “1. Enable client_credentials in discovery metadata”In the OIDC module configuration, ensure client_credentials is included in grant_types_supported inside discovery_meta.
"grant_types_supported": [ "authorization_code", "refresh_token", "client_credentials"]2. Connect a pipe to the RP for client_credentials
Section titled “2. Connect a pipe to the RP for client_credentials”In your Relying Party (RP) configuration, reference the pipe that should handle the client credentials flow:
"client_credentials_pipe_id": "client_credentials_pipe"Example: Minimal RP configuration (client_credentials)
Section titled “Example: Minimal RP configuration (client_credentials)”Below is a simple example of how an RP (machine-to-machine client) can be configured for the client_credentials grant. The RP references the pipe used to create the JWT access token.
{ "client_id": "m2m", "client_secret": "${secrets.1ILhU8IiKwG4FnvUTnEbQ8nprseHt9wxbgaRw2+AqZ1ukvw7oYQfXK1Xqt+AeRJaMY7dVezHeKcxbyYw}", "client_credentials_pipe_id": "client_credentials_pipe"}Important:
Replace the example values with your own environment settings:
- Set
client_idto your preferred client identifier.- Set
client_secretto your own secret reference/value (according to how secrets are managed in your setup).- If you use a different pipe id, update
client_credentials_pipe_idaccordingly.
3. Add the client credentials pipe (JWT access token, TTL 60 minutes)
Section titled “3. Add the client credentials pipe (JWT access token, TTL 60 minutes)”Add the following pipe configuration to your pipes section.
{ "id": "client_credentials_pipe", "config": { "valves": [ { "name": "CreateJwt", "enabled": true, "config": { "dest": "access_token", "jwt_ttl": 60, "jwt_headers": { "typ": "at+jwt" }, "jwt_claims": { "iss": "${globals.default_login_location}/oidc/m2m", "sub": "${request.client_id}", "aud": "https://api.example.com/", "client_id" : "${request.client_id}", "test_claim": "test_claim" }, "keystore": { "path": "${system.customer_home}/resources/keystore.p12", "password": "supersecret", "type": "PKCS12" }, "keystore_password": "supersecret", "keystore_alias": "oidc_sign" } }, { "name": "JsonObjectCreate", "enabled": true, "config": { "src": { "access_token": "${item.access_token}", "token_type": "Bearer", "expires_in": 3600, "scope": "read" }, "dest": "client_credentials" } } ] }}This pipe:
- Creates a signed JWT access token
- Sets the TTL to 60 minutes (
jwt_ttl: 60) - Returns a standard OAuth 2.0 token response
- Uses the configured keystore for signing
Important:
The JWT headers and claims in the example above are placeholders.
You must adapt thejwt_claims(and, if required,jwt_headers) to match your specific environment, security requirements, and integration needs.In addition, both
jwt_ttland thekeystoreconfiguration must be adjusted according to your security policies and operational requirements.
jwt_ttlshould reflect your organization’s token lifetime policy.- The
keystorepath, type, alias, and passwords must match your actual signing certificate configuration.For example, you may need to include:
iss(issuer)aud(audience)subaligned with your client or service identity- Custom claims required by the consuming API
Always ensure that the claims structure and signing configuration comply with your target resource server’s validation logic and your organization’s security policies.
Test the use case
Section titled “Test the use case”1. Verify OIDC well-known configuration
Section titled “1. Verify OIDC well-known configuration”Open:
https://<HOST>:<PORT>/oidc/<TENANT>/.well-known/openid-configurationVerify:
- The endpoint is reachable
grant_types_supportedcontainsclient_credentials- The
token_endpointis correctly published
2. Call the token endpoint
Section titled “2. Call the token endpoint”Use Postman or curl.
Token request:
- Method:
POST - URL:
<token_endpoint> - Authentication:
client_secret_basic(or according to your RP configuration) - Body (x-www-form-urlencoded):
grant_type=client_credentialsExpected result
Section titled “Expected result”A successful response should return:
{ "access_token": "<JWT>", "token_type": "Bearer", "expires_in": 3600, "scope": "read"}access_tokenis a signed JWTexpires_inis3600seconds (60 minutes)- The token is created by the configured pipe