Authentication
Overview
The Lyriq Connector accepts only access tokens issued by the platform IAM (Lyriq IAM).
Any other tokens are explicitly unsupported and will be rejected:
- raw FIS IdP tokens;
- raw bank IdP tokens;
- raw SAML assertions;
- upstream IdP claim payloads.
Which token flow should I use?
| Caller | Supported flow |
|---|---|
| FIS IdP human user | IAM authorization-code flow with the FIS IdP broker (see Human users) |
| Bank SSO human user | IAM authorization-code flow with the bank IdP broker (see Human users) |
| Bank backend or system client | FIS workload assertion exchanged at IAM with client_credentials and JWT client authentication (see Bank backends) |
| Raw FIS token, raw bank token, or SAML assertion | Unsupported |
The IAM endpoints below are served by the platform IAM host, not by the Lyriq Connector.
Sending the token
Every endpoint requires the token as a bearer credential. Requests sent without it are
rejected immediately with 401 Unauthorized.
export CONNECTOR_URL=<your-connector-base-url>
export TOKEN=<paste_your_token_here>
curl "$CONNECTOR_URL/v1/operations" \
--header "Authorization: Bearer $TOKEN" \
--header "Accept: application/vnd.api+json"
Mutating requests additionally send Content-Type: application/vnd.api+json and an
Idempotency-Key header.
What's in the access token
The access token is a signed JWT. You never build or edit it: the platform IAM mints it
from the configuration approved when your bank was onboarded, and the Lyriq Connector
validates every claim below on every request. A token issued to a bank backend (M2M
workload) through the client_credentials flow looks like this once decoded:
{
"iss": "https://iam.example.com/realms/keystone-network",
"sub": "6f1c2d3e-4b5a-4c6d-8e7f-9a0b1c2d3e4f",
"aud": ["bank-connector"],
"azp": "bank-m2m-01927c3e-8b4a-7d21-9f3e-5a6b7c8d9e0f",
"iat": 1790000000,
"exp": 1790000300,
"scope": "connector:transfers:create connector:operations:read",
"connector_token_version": "v1",
"principal_type": "service_account",
"principal_kind": "bank_member",
"connector_managed_client": true,
"bank_memberships": [
{
"bank_id": "11111111-1111-7111-8111-111111111111",
"roles": ["maker"],
"scopes": ["connector:transfers:create", "connector:operations:read"]
}
]
}
| Claim | What it holds | What the Lyriq Connector checks |
|---|---|---|
iss | The platform IAM issuer URL for your deployment | Must be a trusted platform issuer; a token from any other issuer is rejected |
sub | The platform's identifier for your workload | Must be a UUID; recorded as the acting principal in audit |
aud | The intended recipients of the token | Must include bank-connector |
azp | The platform client id generated for your workload at onboarding (shown as Platform client ID in the bank portal) | Must be a client the platform recognises for the Lyriq Connector |
iat, exp | Issue and expiry times, in seconds since the epoch | exp must be in the future; tokens live 300 seconds |
scope | Space-separated API capabilities granted to the workload | See How scopes are resolved |
connector_token_version | The version of this claim contract | Must be v1 |
principal_type | The kind of caller: service_account for an M2M workload, a user type for a person signed in through a browser flow | Must be present |
principal_kind | bank_member for every bank caller | Must be bank_member on bank-facing routes |
connector_managed_client | true when the workload was provisioned through bank onboarding | Together with principal_type and principal_kind, marks the client as one the platform created for your bank |
bank_memberships | The banks this caller may act for, each with its bank_id, descriptive roles and approved scopes | See Selecting the target bank |
Every value comes from onboarding: the bank, the roles and the scopes are those approved for the workload, so the token for an M2M workload always carries exactly one membership. To change what a workload may do, change its onboarding configuration; requesting a different token does not widen it.
To inspect a token, decode its middle segment:
jq -R 'split(".")[1] | gsub("-"; "+") | gsub("_"; "/") | @base64d | fromjson' <<< "$TOKEN"
How scopes are resolved
A request is allowed a scope only when it appears in both the token's top-level
scope and the scopes of the selected bank membership. A scope present in only one of
the two is not granted. Each endpoint in this specification names the scope it requires.
Scopes describe coarse API capabilities, such as connector:transfers:create. They never
name a bank, an account or an asset; which bank a request acts for comes only from
bank_memberships. The roles in a membership (for example maker, checker or
readonly) are descriptive labels for the preset the workload was created from. They
grant nothing on their own.
Selecting the target bank
The token's bank_memberships claim lists the banks the caller may act on, for example:
"bank_memberships": [
{
"bank_id": "11111111-1111-7111-8111-111111111111",
"roles": ["maker"],
"scopes": ["connector:transfers:create", "connector:operations:read"]
},
{
"bank_id": "22222222-2222-7222-8222-222222222222",
"roles": ["readonly"],
"scopes": ["connector:operations:read"]
}
]
With exactly one membership the Lyriq Connector selects it automatically. With more than one, declare the target explicitly with the bank's UUID:
--header "x-dan-bank-id: 11111111-1111-7111-8111-111111111111"
Send the header at most once. A value that is empty, not a UUID, or not one of your
memberships is rejected with 403 Forbidden and code BANK_SCOPE_MISMATCH.
Human users: authorization-code flow
FIS IdP users and bank SSO users start authentication at the platform IAM realm. The IAM redirects the user to the configured upstream identity provider, accepts the successful upstream authentication response, applies the configured mappers, and issues the normalized Lyriq Connector access token.
User
-> platform IAM authorization endpoint
-> FIS IdP (or bank SSO) login
-> platform IAM mapper configuration
-> IAM-issued Lyriq Connector access token
-> Lyriq Connector API
The token sent to the Lyriq Connector is always the IAM-issued access_token. Do not
send the raw FIS IdP token, raw SAML assertion, or copied upstream FIS claims to the
Lyriq Connector.
Start the brokered login
Start an OIDC authorization-code flow against the IAM. Public browser clients should
use PKCE. The kc_idp_hint parameter is optional; when it is set to the configured
identity-provider alias (for example the FIS IdP alias), the IAM routes the user
directly to that provider's login instead of showing the identity-provider selector.
export IAM_AUTH_URL="https://<iam-host>/realms/keystone-network/protocol/openid-connect/auth"
export CLIENT_ID="bank-portal"
export REDIRECT_URI="https://<portal-host>/oauth/callback"
export FIS_IDP_ALIAS="fis"
# Open this URL in the user's browser. URL-encode redirect_uri before sending it.
"$IAM_AUTH_URL?client_id=$CLIENT_ID&response_type=code&redirect_uri=<url-encoded-redirect-uri>&scope=openid%20profile%20email&state=<opaque-state>&code_challenge=<pkce-code-challenge>&code_challenge_method=S256&kc_idp_hint=$FIS_IDP_ALIAS"
After the user authenticates, the IAM redirects back to the registered redirect_uri
with an authorization code.
Exchange the code at the IAM
Exchange the authorization code at the IAM token endpoint. Use the same redirect URI and PKCE verifier that were used to start the login.
export IAM_TOKEN_URL="https://<iam-host>/realms/keystone-network/protocol/openid-connect/token"
export CLIENT_ID="bank-portal"
export REDIRECT_URI="https://<portal-host>/oauth/callback"
export AUTHORIZATION_CODE="<code-from-iam-redirect>"
export CODE_VERIFIER="<original-pkce-code-verifier>"
curl -sS -X POST "$IAM_TOKEN_URL" \
-H 'content-type: application/x-www-form-urlencoded' \
-d grant_type=authorization_code \
-d "client_id=$CLIENT_ID" \
-d "redirect_uri=$REDIRECT_URI" \
-d "code=$AUTHORIZATION_CODE" \
-d "code_verifier=$CODE_VERIFIER"
For confidential interactive web applications, include the registered client secret in the token request. This applies only to the web application's OIDC registration, never to a bank M2M workload:
-d "client_secret=$CLIENT_SECRET"
Use the access_token from the response as the bearer token for the Lyriq Connector.
Bank backends: workload assertion exchange
Bank backend and system integrations authenticate to FIS with a bank-controlled
asymmetric credential. FIS returns a short-lived workload assertion whose sub is
the FIS Client ID registered during bank onboarding. The platform IAM verifies that
assertion and resolves it to the secretless federated client provisioned for the
workload. This flow is for machine-to-machine clients only; human users use the
authorization-code flow above.
Obtain the FIS workload assertion from the FIS token endpoint using the bank-controlled credential, then exchange it once at the IAM:
export IAM_TOKEN_URL="https://<iam-host>/realms/keystone-network/protocol/openid-connect/token"
export FIS_WORKLOAD_ASSERTION="<short-lived-assertion-from-fis>"
curl --fail --show-error --silent --request POST "$IAM_TOKEN_URL" \
-H 'content-type: application/x-www-form-urlencoded' \
-d grant_type=client_credentials \
-d 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode "client_assertion=$FIS_WORKLOAD_ASSERTION"
The access token is valid for 300 seconds; request a new one before it expires. Its claims, including the bank membership and scopes approved for the workload at onboarding, are described in What's in the access token.
Common problems
Missing bearer token
Ensure the Authorization header is present exactly once and uses the
Bearer <token> syntax. The response is 401 with code AUTHENTICATION_REQUIRED.
Expired token
Access tokens expire after 5 minutes (300 seconds). Requests using an expired token are
rejected with 401 (title Expired token); get a new one via the relevant flow.
Wrong issuer or audience
Occurs when passing a token issued directly by an external entity instead of the
platform IAM, or when the requesting client isn't configured with the Lyriq Connector
audience. The response is 401 with code AUTHENTICATION_REQUIRED.
Request a new token from the IAM using the relevant flow. If the problem persists, contact your system administrator to verify the client's audience configuration.
Missing scope
The token is valid but lacks the permission scope the endpoint requires; the response
is 403 with code UNAUTHORIZED_SCOPE. Check the endpoint's required scope in this
specification against both the token's scope and the selected membership's scopes
(see How scopes are resolved).
A fresh token carries the same scopes, so if the scope is missing from either, contact
your system administrator to update your client's access rights.
Missing or ambiguous bank membership
Missing: the token's bank_memberships array is empty, so the caller has no bank
relationship yet (403, title Missing bank membership). Contact your system
administrator to provision one.
Ambiguous: the array has more than one entry and the request didn't declare a target
bank (403, title Bank selector required). Resolve it with the x-dan-bank-id header
described in Selecting the target bank.
Suspended or terminated bank
Requests for a bank that is suspended or terminated on the network are rejected with
403 and code BANK_SUSPENDED or BANK_TERMINATED.