Keys & tokens¶
In one sentence
These tables connect an APIM application to an OAuth client in a key manager. They also store what the key manager issues from that client (access tokens and authorization codes) and 4.x's hashed API keys.
The idea¶
After subscribing, a developer clicks Generate Keys for their application. Behind the scenes:
- APIM asks a key manager (KM) to create an OAuth client. That's a consumer key and consumer secret. The KM is either the built-in Resident Key Manager or an external one such as Keycloak, Okta or Azure AD.
- APIM records "application 5's PRODUCTION keys live in key manager Resident Key Manager under consumer key
abc123" inAM_APPLICATION_KEY_MAPPING. The app can also have separate SANDBOX keys, and keys in several key managers. - The app uses the consumer key and secret to get access tokens from the KM. It sends a token with every API call, and the gateway checks it.
With the Resident Key Manager, the OAuth client is stored in this same database (IDN_OAUTH_CONSUMER_APPS and friends). Opaque tokens are stored too (IDN_OAUTH2_ACCESS_TOKEN), but in 4.7.0's default setup the JWT access tokens issued to JWT-type applications are not stored at all. Identity Server also creates a matching service provider (SP_APP) for each client. With an external key manager, only APIM's mapping row is stored locally. The client and its tokens live in the external system.
Logical link (no foreign key)
The bridge between APIM and the key manager is AM_APPLICATION_KEY_MAPPING.CONSUMER_KEY = IDN_OAUTH_CONSUMER_APPS.CONSUMER_KEY. It isn't a foreign key, because the key manager may be external.
How the tables connect¶
APIM's side (applications, keys and key managers):
erDiagram
AM_APPLICATION ||--o{ AM_APPLICATION_KEY_MAPPING : "keys per type + KM"
AM_APPLICATION ||--o{ AM_APPLICATION_REGISTRATION : "key requests"
AM_KEY_MANAGER ||..o{ AM_APPLICATION_KEY_MAPPING : "logical: KEY_MANAGER"
AM_KEY_MANAGER ||--o{ AM_KEY_MANAGER_PERMISSIONS : "who may use"
AM_KEY_MANAGER ||--o{ AM_KEY_MANAGER_ALLOWED_ORGS : "which orgs"
The Resident Key Manager's side (OAuth client, service provider and tokens):
erDiagram
AM_APPLICATION_KEY_MAPPING }o..|| IDN_OAUTH_CONSUMER_APPS : "logical: CONSUMER_KEY"
IDN_OAUTH_CONSUMER_APPS ||--o{ IDN_OAUTH_CONSUMER_SECRETS : "secrets"
IDN_OAUTH_CONSUMER_APPS ||--o{ IDN_OAUTH2_ACCESS_TOKEN : "issued tokens"
IDN_OAUTH2_ACCESS_TOKEN ||--o{ IDN_OAUTH2_ACCESS_TOKEN_SCOPE : "granted scopes"
SP_APP ||--o{ SP_INBOUND_AUTH : "inbound config"
SP_INBOUND_AUTH }o..|| IDN_OAUTH_CONSUMER_APPS : "logical: INBOUND_AUTH_KEY"
- Deleting an OAuth client cascades to its tokens, codes and secrets.
- Tokens point at the client through the numeric
CONSUMER_KEY_ID(=IDN_OAUTH_CONSUMER_APPS.ID), not through the key text.
4.x API keys:
erDiagram
AM_API_KEY ||--o{ AM_API_KEY_API_MAPPING : "valid for API"
AM_API_KEY ||--o{ AM_API_KEY_APPLICATION_MAPPING : "belongs to app"
AM_API ||--o{ AM_API_KEY_API_MAPPING : ""
AM_APPLICATION ||--o{ AM_API_KEY_APPLICATION_MAPPING : "by UUID"
The tables¶
AM_KEY_MANAGER¶
One row = one key manager registered in the Admin Portal. The Resident Key Manager is created automatically for each tenant.
| Column | What it means |
|---|---|
UUID |
Primary key. |
NAME, DISPLAY_NAME, DESCRIPTION |
NAME is unique per ORGANIZATION, e.g. Resident Key Manager. |
TYPE |
The connector type, e.g. default (Resident), KeyCloak, Okta, AzureAD. |
CONFIGURATION |
Endpoints, grant types, claim mappings and so on (JSON). |
ENABLED |
On or off. |
TOKEN_TYPE |
Whether it issues tokens directly, or exchanges or accepts tokens from elsewhere. |
ORGANIZATION, EXTERNAL_REFERENCE_ID |
Owner, and an ID in an external system. |
Connects to: AM_KEY_MANAGER_PERMISSIONS and AM_KEY_MANAGER_ALLOWED_ORGS (FKs, cascade). It's referenced logically by AM_APPLICATION_KEY_MAPPING.KEY_MANAGER and AM_APPLICATION_REGISTRATION.KEY_MANAGER.
AM_KEY_MANAGER_PERMISSIONS¶
One row = a role that is allowed or denied (PERMISSIONS_TYPE) to use a key manager in the Developer Portal. It has KEY_MANAGER_UUID (FK, cascade) and ROLE.
AM_KEY_MANAGER_ALLOWED_ORGS¶
One row = an organization allowed to use a key manager. It has KEY_MANAGER_UUID (FK, cascade) and ALLOWED_ORGANIZATIONS.
AM_APPLICATION_KEY_MAPPING¶
One row = one set of keys: application × key type × key manager.
| Column | What it means |
|---|---|
APPLICATION_ID |
Application (FK, cascade). |
KEY_TYPE |
PRODUCTION or SANDBOX. |
KEY_MANAGER |
Which key manager. In 4.x this normally holds the key manager's UUID. Data migrated from older versions may hold the name. |
CONSUMER_KEY |
The OAuth client ID in that key manager (logical link). |
STATE |
E.g. CREATED (waiting for an approval workflow), COMPLETED, REJECTED. |
CREATE_MODE |
CREATED (APIM made the client) or MAPPED (the developer mapped an existing client). |
UUID |
Public ID of this key mapping. |
APP_INFO |
A cached copy of the OAuth client details (JSON). |
The primary key is (APPLICATION_ID, KEY_TYPE, KEY_MANAGER).
AM_APPLICATION_REGISTRATION¶
One row = one key-generation request for an application, key type and key manager. It's used by the approval workflow, but 4.7.0 writes it even when no workflow is configured, and keeps it until the application is deleted.
| Column | What it means |
|---|---|
REG_ID |
Primary key. |
SUBSCRIBER_ID |
FK → AM_SUBSCRIBER, restrict. |
APP_ID |
FK → AM_APPLICATION, cascade. |
TOKEN_TYPE |
The key type requested (production or sandbox). |
KEY_MANAGER |
Which key manager. |
WF_REF |
Links to AM_WORKFLOWS.WF_REFERENCE. |
INPUTS, TOKEN_SCOPE, VALIDITY_PERIOD, ALLOWED_DOMAINS |
What the developer asked for. |
AM_APP_KEY_DOMAIN_MAPPING¶
One row = a domain that is allowed to use a consumer key, for browser-based clients (CONSUMER_KEY, AUTHZ_DOMAIN). It's a logical link to the OAuth client. This is a legacy feature.
AM_SYSTEM_APPS¶
One row = an OAuth client that APIM created for itself. The Publisher, Developer Portal and Admin Portal web apps log in with these. It has NAME, CONSUMER_KEY (unique), CONSUMER_SECRET and TENANT_DOMAIN.
Watch out: these clients also exist in IDN_OAUTH_CONSUMER_APPS, but they belong to no AM_APPLICATION. They're registered the first time a user signs in to each portal's web UI. If APIM is used only through its REST APIs, this table stays empty.
AM_API_KEY¶
One row = one API key. An API key is a simple secret string that can call APIs instead of an OAuth token. Only a hash of the key is stored.
| Column | What it means |
|---|---|
API_KEY_UUID |
Primary key. |
NAME |
Name given when the key was created. |
API_KEY_HASH |
Hash of the key (unique), e.g. $sha256$a858…. The key itself isn't stored. |
KEY_TYPE |
PRODUCTION or SANDBOX. |
AUTHZ_USER |
The user who created it. |
VALIDITY_PERIOD, TIME_CREATED, LAST_USED |
Lifetime and usage. |
STATUS |
E.g. ACTIVE. |
API_KEY_PROPERTIES |
Restrictions such as IP or referrer (JSON). |
AM_API_KEY_API_MAPPING¶
One row = "API key K may be used for API A". FKs → AM_API_KEY and AM_API.API_UUID. Full column list
AM_API_KEY_APPLICATION_MAPPING¶
One row = "API key K belongs to application A". FKs → AM_API_KEY and AM_APPLICATION.UUID (note: the UUID, not APPLICATION_ID). Full column list
IDN_OAUTH_CONSUMER_APPS¶
One row = one OAuth client in the Resident Key Manager.
| Column | What it means |
|---|---|
ID |
Internal number. Tokens and codes point here. |
CONSUMER_KEY |
Client ID (unique). This is the value stored in AM_APPLICATION_KEY_MAPPING.CONSUMER_KEY. |
CONSUMER_SECRET |
Client secret. It may be hashed or encrypted, depending on configuration. |
APP_NAME |
Generated name: <owner>_<application UUID>_PRODUCTION or …_SANDBOX. |
USERNAME, TENANT_ID, USER_DOMAIN |
Owner of the client. |
GRANT_TYPES, CALLBACK_URL |
Allowed OAuth grant types (space-separated) and the redirect URL. |
APP_STATE |
ACTIVE or REVOKED. |
USER_ACCESS_TOKEN_EXPIRE_TIME, APP_ACCESS_TOKEN_EXPIRE_TIME, REFRESH_TOKEN_EXPIRE_TIME, ID_TOKEN_EXPIRE_TIME |
Token lifetimes. |
PKCE_MANDATORY, PKCE_SUPPORT_PLAIN, OAUTH_VERSION |
Protocol settings. |
IDN_OAUTH_CONSUMER_SECRETS¶
One row = one client secret. 4.x allows several secrets per client, each with its own expiry. It has CONSUMER_KEY (FK → IDN_OAUTH_CONSUMER_APPS.CONSUMER_KEY, cascade), SECRET_ID (unique), SECRET_VALUE / SECRET_HASH, DESCRIPTION and EXPIRY_TIME.
IDN_OAUTH2_ACCESS_TOKEN¶
One row = one access token (and its refresh token) issued by the Resident Key Manager.
| Column | What it means |
|---|---|
TOKEN_ID |
Primary key. |
CONSUMER_KEY_ID |
Client (FK → IDN_OAUTH_CONSUMER_APPS.ID, cascade). |
ACCESS_TOKEN, REFRESH_TOKEN (+ _HASH) |
The token values. For an opaque token, ACCESS_TOKEN is the token itself (a UUID). |
AUTHZ_USER, TENANT_ID, USER_DOMAIN, SUBJECT_IDENTIFIER |
Who the token was issued for. |
GRANT_TYPE |
E.g. client_credentials, password, authorization_code. |
TIME_CREATED, VALIDITY_PERIOD, and the refresh-token equivalents |
Lifetime. |
TOKEN_STATE |
ACTIVE, EXPIRED, REVOKED or INACTIVE. |
TOKEN_SCOPE_HASH, TOKEN_BINDING_REF, IDP_ID, CONSENTED_TOKEN |
Supporting data. |
Watch out: in 4.x, gateways validate JWT tokens by signature, without reading this table. With default settings in 4.7.0, a JWT issued to a JWT-type application (for example with client_credentials) has no row here. Only opaque tokens, issued to DEFAULT-type clients, are stored. Revoking a JWT goes to AM_REVOKED_JWT + IDN_INVALID_TOKENS instead (see Revocation).
IDN_OAUTH2_ACCESS_TOKEN_SCOPE¶
One row = one scope granted to one token. It has TOKEN_ID (FK, cascade) and TOKEN_SCOPE. The scope names match AM_SCOPE.NAME / IDN_OAUTH2_SCOPE.NAME. Full column list
IDN_OAUTH2_ACCESS_TOKEN_AUDIT¶
One row = an old token moved out of IDN_OAUTH2_ACCESS_TOKEN when it was replaced or cleaned up. It has the same columns, plus INVALIDATED_TIME. It's only written if token-cleanup auditing is enabled. There are no FKs. Full column list
IDN_OAUTH2_AUTHORIZATION_CODE¶
One row = one authorization code from the OAuth authorization code grant. The user logs in and the app gets a code, which it swaps for a token.
- It has
CONSUMER_KEY_ID(FK, cascade),AUTHORIZATION_CODE(+ hash),CALLBACK_URL,AUTHZ_USER,STATE(e.g.ACTIVEorINACTIVE) and the PKCE fields. - Once the code is used,
TOKEN_IDholds the token it was exchanged for. That's a logical link.
IDN_OAUTH2_AUTHZ_CODE_SCOPE¶
One row = one scope requested with an authorization code. It has CODE_ID (FK, cascade) and SCOPE. Full column list
SP_APP¶
One row = one service provider in the embedded Identity Server. When the Resident Key Manager creates an OAuth client for an APIM application, it also creates a service provider with the same name as IDN_OAUTH_CONSUMER_APPS.APP_NAME.
It has ID, APP_NAME, USERNAME, TENANT_ID, UUID and many login-behaviour flags.
SP_INBOUND_AUTH¶
One row = one inbound protocol setting of a service provider. For OAuth, INBOUND_AUTH_TYPE = 'oauth2' and INBOUND_AUTH_KEY = the consumer key. That's how a service provider is tied to its OAuth client. APP_ID → SP_APP.ID (FK, cascade; it's declared with ALTER TABLE).
Example¶
Production keys for the application PizzaApp (UUID 4023…), owned by admin. Example rows:
| Table | Row |
|---|---|
AM_APPLICATION_REGISTRATION |
REG_ID = 1, APP_ID = 2, TOKEN_TYPE = PRODUCTION, TOKEN_SCOPE = default |
AM_APPLICATION_KEY_MAPPING |
APPLICATION_ID = 2, KEY_TYPE = PRODUCTION, KEY_MANAGER = b446… (Resident KM UUID), CONSUMER_KEY = rfjt…, STATE = COMPLETED, CREATE_MODE = CREATED |
IDN_OAUTH_CONSUMER_APPS |
ID = 2, CONSUMER_KEY = rfjt…, APP_NAME = admin_4023…_PRODUCTION, GRANT_TYPES = client_credentials password |
IDN_OAUTH_CONSUMER_SECRETS |
CONSUMER_KEY = rfjt…, SECRET_HASH = {"hash":"20e9…","algorithm":"SHA-256"} |
SP_APP |
APP_NAME = admin_4023…_PRODUCTION |
SP_INBOUND_AUTH |
INBOUND_AUTH_KEY = rfjt…, INBOUND_AUTH_TYPE = oauth2 |
IDN_OAUTH2_ACCESS_TOKEN |
(no row: the app's JWT access tokens aren't stored) |
Note that the OAuth client name uses the application's UUID, not its display name.
Try it¶
-- Application → keys → OAuth client (Resident Key Manager only)
SELECT app.NAME AS APPLICATION, km.NAME AS KEY_MANAGER, akm.KEY_TYPE,
akm.CONSUMER_KEY, oc.APP_NAME AS OAUTH_CLIENT, oc.GRANT_TYPES
FROM AM_APPLICATION_KEY_MAPPING akm
JOIN AM_APPLICATION app ON app.APPLICATION_ID = akm.APPLICATION_ID
LEFT JOIN AM_KEY_MANAGER km ON km.UUID = akm.KEY_MANAGER
LEFT JOIN IDN_OAUTH_CONSUMER_APPS oc ON oc.CONSUMER_KEY = akm.CONSUMER_KEY;
A NULL OAUTH_CLIENT means the keys live in an external key manager.
Related flows¶
Different in 3.x
3.x has no API-key tables. API keys were stateless JWTs, and 3.x instead had AM_SUBSCRIPTION_KEY_MAPPING. 3.x has no key-manager permission or organization tables, and no IDN_OAUTH_CONSUMER_SECRETS. AM_KEY_MANAGER there is scoped by TENANT_DOMAIN, and key mappings often stored the key manager name. See 3.x Keys & tokens.