Authentication (Keycloak)
What credentials a tenant receives, how to use them, and what they represent. Assetera Identity is a Keycloak realm speaking standard OIDC / OAuth 2.0.
Assetera Identity is a Keycloak realm speaking standard OpenID Connect / OAuth 2.0. Because it is
standard OIDC, any conforming OIDC client library works. As a tenant you are provisioned an OIDC client;
Assetera Identity issues tokens with the configured tenant context, and Assetera's APIs validate them
locally against the realm's public keys (JWKS). No client secret is shared with the API, and there is no
per-request round trip back to Keycloak.
What you are provisioned
When your tenant is onboarded you receive a confidential OIDC client:
| You receive | What it is | What it represents |
|---|---|---|
client_id | Your tenant's client identifier | Your tenant's machine identity |
A client secret or (preferred) a key pair for private_key_jwt | How the client proves itself | Only your backend can authenticate this client to the token endpoint |
| The issuer / realm URL and its discovery document | .../realms/assetera + .well-known/openid-configuration | Where to authorize, get tokens, and fetch keys |
| An audience for the API you call | e.g. marketplace-api | Which API your tokens are valid for |
| Redirect URIs (for user login or sign-up) | Your BFF callback URLs | Where users return after authentication |
The client secret / private key is a backend credential. It never ships to a browser. Prefer
private_key_jwt: you hold the private key and Assetera registers only your public keys, so no shared
secret crosses the boundary.
Customer sign-up and sign-in
A tied-agent customer signs up against Assetera Identity through the partner BFF. Registration uses the
same Authorization Code + PKCE callback as sign-in, but starts at the realm's registration journey. After
the callback, the BFF stores the tokens server-side and uses the access token's sub as the customer's
stable Assetera user ID.
browser -> partner BFF -> Assetera registration -> partner BFF callbackThe browser keeps only the partner's opaque, HttpOnly session cookie. The customer sub is then reused as
MetaKYC's externalRefId and as the embedded wallet's external user ID.
Grants
How you obtain a token depends on who is calling:
| Grant | Who | When |
|---|---|---|
| Authorization Code + PKCE | a user (via your BFF) | someone signs in and clicks through a UI |
| Client Credentials | your backend | server to server, no user present |
| Token Exchange | your BFF | exchange an authenticated user's token for one restricted service audience, such as the embedded wallet |
Authorization Code and Client Credentials yield signed tokens with tenant context. Token Exchange narrows a user token to a configured target audience and must not be treated as user impersonation or a general API token.
Getting a token (backend, Client Credentials)
curl -X POST \
"https://auth.<base_domain>/realms/assetera/protocol/openid-connect/token" \
-d grant_type=client_credentials \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...",
"token_type": "Bearer",
"expires_in": 300
}You then call the API with Authorization: Bearer <access_token>. Use
the discovery document
to resolve the real token, authorize, and jwks_uri endpoints for your environment rather than hardcoding
paths.
Inside the token
The access token is a signed JWT. The claims that matter to an integrator:
{
"iss": "https://auth.<base_domain>/realms/assetera",
"aud": "marketplace-api",
"sub": "b1e...-user-or-service-id",
"tenant": "your-tenant-id",
"tenants": ["your-tenant-id"],
"realm_access": { "roles": ["customer"] },
"exp": 1730000000
}tenantscopes every response. Assetera Identity derives it from the configured client and user context; a caller cannot select or spoof it on the API request (see Tenancy).tenantslists the user's tenant memberships. User requests are accepted only in an allowed tenant context.audpins the token to one API. A token for one service is rejected by another, so it cannot be replayed.expis short (minutes). Refresh (user flows) or re-request (M2M) rather than caching long-lived tokens.
How validation works
The API validates every token itself, against the realm's JWKS: signature, issuer, audience, expiry,
then it reads the tenant claim and scopes the query. If you gate your own UI, validate the same way in
your BFF, and always re-check authorisation on the server.
The interactive (user) flow
Embedded wallet token exchange
Where the Assetera wallet is enabled, the BFF exchanges the server-held user access token for a second,
short-lived JWT whose audience is restricted to the wallet service. The subject remains the same Keycloak
sub. Only this restricted token is returned to the browser for the wallet SDK's external-JWT sign-in.
The original Assetera API token never reaches the browser. The wallet token is rejected by Marketplace and Compliance APIs because its audience is different.
DSP identity federation
A DSP can connect an existing OIDC or SAML identity provider where agreed. Assetera Identity validates the partner assertion, applies the configured subject mapping, and issues an Assetera token for the DSP tenant. The relying-party configuration, linking rules, logout behavior, and accepted claims are provisioned per partner. See DSP reliance integration.