Welcome to the OpenID Federation project. This repository implements a Kotlin Multiplatform solution, enabling shared code across multiple platforms (such as the JVM, JS, and Native). Whether you are a developer familiar with Kotlin or new to multiplatform projects, this guide will help you understand the core concepts, architecture, and deployment instructions for the project.
OpenID Federation is a framework designed to facilitate secure and interoperable interactions among entities within a federation. It utilizes JSON Web Tokens (JWTs) to securely represent and transmit necessary metadata, ensuring trust and security across various organizations and systems.
- Federation: A group of organizations that agree to interoperate under a common set of rules defined in a federation policy.
- Entity Statements: JSON objects containing metadata about entities (such as Identity Providers and Relying Parties) along with their federation relationships.
- Trust Chains: Mechanisms by which entities verify one another's trustworthiness by following a chain of entity statements back to a trusted authority.
- Federation API: Standardized interfaces to exchange information and perform operations crucial for federation management.
- Federation Operator: The central authority in the federation responsible for policy management and trust chain verification.
- Identity Providers (IdPs): Entities that authenticate users and issue identity assertions to relying parties.
- Relying Parties (RPs): Entities that rely on the received identity assertions to provide services to users.
- JSON Web Tokens (JWT): Employed to create verifiable entity statements and security assertions.
- JSON Object Signing and Encryption (JOSE): Standards used for signing and encrypting JSON objects to ensure integrity and confidentiality.
This project is developed as a Kotlin Multiplatform project. The goal of this approach is to maximize code reuse and maintain consistency across different platforms:
- Common Code: Shared modules include the business logic and core functionality, written in pure Kotlin.
- Platform-Specific Implementations: Platform-dependent modules exist for code that needs to interact with specific operating system or ecosystem features.
- Supported Platforms: Typically, the project targets the JVM, JavaScript, and native environments. Consult the project’s build configuration for platform-specific details.
- Development Setup: Ensure you have the latest version of Kotlin and the appropriate SDKs installed for the platforms you intend to target.
For complete API details, please refer to the following resources:
OIDFed uses the IDK configuration pipeline. Application code resolves settings through OidfConfigBinder /
OidfPropertyResolution — not via direct System.getenv. Full reference:
docs/CONFIGURATION.md.
| Method | When to use |
|---|---|
IDK YAML (lib-conf-yaml) |
config/application.yaml (+ profile/tenant/principal YAML) via AppConfigService |
| Classpath packaged YAML | Optional defaults in admin/federation jars (IDK classpath fallback) |
| OIDFed reference.properties | Packaged non-YAML defaults only |
| Environment variables | Secrets, CI, Compose; override file values |
| Programmatic maps | Tests / embedders (DefaultAppMapPropertySource) |
| Host contributions | Cloud config, K8s ConfigMaps (IDK PropertySourceContribution) |
Precedence (highest first): AppConfigService (IDK Env + YAML + legacy-env bridge + host sources) → programmatic AppMap → env tier → OIDFed reference file tier → hardcoded defaults.
YAML is only via IDK — not a second parser inside OIDFed.
| Preference | How |
|---|---|
| Recommended | Copy application.yaml.example → application.yaml or config/application.yaml |
| Still fully supported | Env: IDK-normalized OIDF_* or legacy ROOT_IDENTIFIER, DATASOURCE_URL, … — see .env.example |
Docker Compose: copy .env.example to .env / .env.local (env overrides files). Optionally place
file config under config/ (see config/application.yaml.example); compose mounts
./config read-only into admin and federation containers at /app/config.
File-only stack (no OIDFed env vars on app containers):
docker compose -f docker-compose.file-only.yaml up --buildUses config/application.file-only.yaml mounted as
/app/config/application.yaml.
For seamless deployment of the OpenID Federation servers, Docker and Docker Compose are recommended. Docker provides an efficient and straightforward deployment and orchestration environment.
The following Docker Hub images are available for the OpenID Federation project:
- OpenID Federation Admin Server: https://hub.docker.com/r/sphereon/openid-federation-admin-server
- OpenID Federation Server: https://hub.docker.com/r/sphereon/openid-federation-server
These images can be used to quickly deploy the services in a containerized environment. You can use the docker compose file to pull these images, or you can pull them directly using docker pull.
-
docker compose build
Compile the Docker images for the services. -
docker compose build --no-cache
Build the Docker images without using the cache to ensure a clean build.
-
docker compose up
Initiate all services. -
docker compose up -d
Launch all services in detached mode (running in the background). -
docker compose down
Terminate all running services. -
docker compose down -v
Terminate services and remove associated volumes. -
docker compose up db -d
Start only the database container in detached mode. -
docker compose up openid-federation-server -d
Start only the Federation Server in detached mode.
- Federation API: Accessible at http://localhost:8080
- Admin Server API: Accessible at http://localhost:8081 (always requires
Authorization: Bearer …) - Default Keycloak Server (JWT issuer for admin): Accessible at http://localhost:8082
Admin compose service waits for db and keycloak (healthy). Issuer defaults to
http://keycloak:8080/realms/openid-federation via OIDF_OAUTH2_ISSUER_URI /
OAUTH2_RESOURCE_SERVER_JWT_ISSUER_URI (see .env.example). Compose local stacks use account mode by
default. There is no configuration switch to run the admin API without authentication; see
Identity modes below.
This guide will help new users configure and deploy the OpenID Federation service: file-based config (preferred), environment overrides, identity modes, the root entity, KMS, and dependencies. Follow the steps below. For a deep dive (load order, YAML flattener, tenant keys, property-source bootstrap), see docs/CONFIGURATION.md.
Any changes affecting Entity Statements or Subordinate Statements must be explicitly published to take effect. This includes:
- Metadata changes
- Trust Mark modifications
- Configuration updates
- Key rotations
The Local Key Management Service (in-memory) is designed primarily for testing, development, and local experimentation purposes. It is not intended for use in production environments due to significant security and compliance risks.
The federation service stores configuration, keys, and trust data per entity context. How that context is chosen, and who is allowed to call the admin API, depends on the identity mode. From 0.25.0 there are two modes: account (the historical standalone model, and still the default) and external (an OAuth2 / OIDC authorization server owns subjects and tenants). Both modes require a valid Bearer access token on every admin call except health and debug endpoints. There is no longer a configuration switch to turn admin authentication off.
Detailed design notes live in docs/IDENTITY_AND_IDK_ALIGNMENT.md. End-to-end testing with an in-process IDK authorization server is described in docs/PLATFORM_E2E.md.
Admin authentication is always JWT-based. The identity mode controls where the federation entity (tenant) context comes from, not whether a token is required.
Set the mode with property key oidf.identity.mode or env OIDF_IDENTITY_MODE (legacy alias IDENTITY_MODE):
| Value | Meaning |
|---|---|
account (default) |
Pre-0.25.0 standalone model. Federation entities are Account rows managed via /accounts. |
external |
Host or platform embedding. Tenant id comes only from claims on the access token. |
legacy |
Accepted alias for account. |
platform |
Accepted alias for external. |
This is the original open-source multi-entity model and remains the default.
The database seeds a special root account. Its federation entity identifier is ROOT_IDENTIFIER /
OIDF_FEDERATION_ROOT_IDENTIFIER (for example http://localhost:8080). The root account serves the federation
well-known document at that base URL. Additional entities are ordinary accounts created through POST /accounts.
Each non-root account gets an identifier such as {root}/{username} unless you supply an explicit identifier.
Every admin request must present Authorization: Bearer <access_token>. After the token is validated, the server
opens a session bound to an entity context (normally the root account when the token does not name another tenant).
To operate on a different account you may send X-Account-Username with the target username. That header is entity
selection, not authentication. It only succeeds when the authenticated principal appears on a configured allow-list
(see below). If the username is unknown the server returns 404; if the principal is not allowed to rebind, 403.
By default the allow-list is empty, so header rebind is denied for everyone until you list the operators who may
switch entity context. Matching uses a JWT claim (default sub) against
OIDF_ACCOUNT_HEADER_ALLOWED_PRINCIPALS. A single * allows any authenticated principal and is intended only for
local tests, not production.
Account REST (GET / POST / DELETE /accounts) is registered only in this mode (and only when the
openid-federation-account-http module is on the classpath, which it is for the all-in-one admin server in this repo).
Typical Docker Compose and Keycloak setup in this README assumes account mode.
External mode is for deployments where an external authorization server (or the Sphereon IDK OAuth2 AS) already
manages users, organizations, and which caller may act as which tenant. OpenID Federation does not run its own
account registry in this mode: /accounts is not registered (and returns 410 if hit), and X-Account-Username is
never used for identity.
The access token is still required and validated against OIDF_OAUTH2_ISSUER_URI. After validation the server reads
tenant claims from the token (tenant_id, tid, and related IDK claim names) and sets SessionExecution.tenantId to
that value. All domain data for the request is isolated under that tenant. If a protected admin route receives a
token with no usable tenant claim, the request fails closed with 401.
On first contact with a new token tenant id the server can auto-provision a federation row
(FederationTenantProvisioner, tenant_source = idk). Party, user, and organization management stay in the host
identity product; OIDFed only stores federation material (keys, entity configuration, subordinates, trust marks)
keyed by that tenant id.
In external mode there is no privileged root login account inside OIDFed. What remains is a mapping for the federation root entity URL:
OIDF_EXTERNAL_ROOT_TENANT_ID / oidf.identity.external.root.tenant.id names which token tenant id owns the
federation root identifier (ROOT_IDENTIFIER). Other tenants typically receive entity identifiers under
{root}/tenants/{tenantId} unless your host overrides that layout.
That mapping only answers “which validated session tenant publishes as the root entity URL?”. It is not a back door to elevate an unauthenticated caller, and it is not a substitute for the access token.
If operator A must act as tenant B, that is expressed by the authorization server, for example through token exchange, on-behalf-of / STS flows, or actor and delegation claims the AS puts on the access token. OIDFed fully trusts the validated token’s tenant (and related) claims. It does not implement its own impersonation API, does not accept client-supplied headers to change tenant, and does not re-check AS policy beyond JWT validation and the required tenant claim.
Configure the issuer so tokens carry a stable tenant claim your host already uses. Automated EXTERNAL e2e tests mint such tokens with the in-process IDK OAuth2 AS; see docs/PLATFORM_E2E.md.
Earlier versions allowed an “anonymous admin” / no-auth style configuration for local development. That escape hatch
has been removed for security. Related environment variables and config keys (including anything that disabled
JWT requirement on the admin server) are gone. The admin server requires a non-blank OAuth2 issuer at startup and
installs JWT authentication with requireAuth=true.
Integration tests obtain Bearer tokens from an in-process IDK OAuth2 AS rather than disabling auth. Compose-based
local development continues to use Keycloak (or any OIDC issuer you point OIDF_OAUTH2_ISSUER_URI at).
Public federation protocol endpoints on the federation server remain open by design (entity configuration, subordinate listing, trust mark endpoints as specified by OpenID Federation). Only the admin API always requires Bearer tokens.
Prefer oidf.* keys in YAML/properties; env forms remain fully supported.
| Property key | Env (preferred) | Legacy env (JVM) |
|---|---|---|
oidf.identity.mode |
OIDF_IDENTITY_MODE |
IDENTITY_MODE |
oidf.identity.account.header.principal.claim |
OIDF_ACCOUNT_HEADER_PRINCIPAL_CLAIM |
— |
oidf.identity.account.header.allowed.principals |
OIDF_ACCOUNT_HEADER_ALLOWED_PRINCIPALS |
— |
oidf.identity.external.root.tenant.id |
OIDF_EXTERNAL_ROOT_TENANT_ID |
OIDF_PLATFORM_ROOT_TENANT_ID |
oidf.oauth2.issuer.uri |
OIDF_OAUTH2_ISSUER_URI |
OAUTH2_RESOURCE_SERVER_JWT_ISSUER_URI |
oidf.oauth2.audience |
OIDF_OAUTH2_AUDIENCE |
— |
oidf.oauth2.jwt.auth.enabled |
OIDF_OAUTH2_JWT_AUTH_ENABLED |
— |
oidf:
identity:
mode: account # or external
account:
header:
principal:
claim: sub
# Empty = deny X-Account-Username rebind. Comma list, or * for tests only.
allowed:
principals: ""
# external only:
# external:
# root:
# tenant:
# id: my-root-tenant
oauth2:
issuer:
uri: http://keycloak:8080/realms/openid-federation
# audience: openid-federation-adminOIDF_IDENTITY_MODE=account
# OIDF_ACCOUNT_HEADER_PRINCIPAL_CLAIM=sub
# OIDF_ACCOUNT_HEADER_ALLOWED_PRINCIPALS=
# OIDF_EXTERNAL_ROOT_TENANT_ID=
OIDF_OAUTH2_ISSUER_URI=http://keycloak:8080/realms/openid-federation
# OIDF_OAUTH2_AUDIENCE=openid-federation-adminThis README show the steps to interact with the API down below. You can use a tool like CURL or use the Swagger UI to interact with the API. We have also included a postman collection you can import into Postman. It contains examples for most endpoints and also at the toplevel has an OAUth2 integration. So you can get an access token from the toplevel folder and then use that token automatically in subsequent calls.
OIDFed reads configuration through the IDK pipeline (OidfConfigBinder). You can mix methods: files for structure,
env for secrets, and optional tenant overrides in the same YAML.
Full reference: docs/CONFIGURATION.md. Starters: application.yaml.example, config/application.yaml.example, .env.example.
Copy the example to one of these locations (later paths win within the file tier):
| Location | Use |
|---|---|
Working directory application.yaml / .properties |
Local JVM runs |
application-{profile}.yaml |
Profile-specific overrides |
config/application.yaml |
Docker Compose (./config → /app/config) |
Classpath application.* |
Embedded / packaged defaults |
oidf:
federation:
root:
identifier: http://localhost:8080
dev:
mode: false
server:
admin:
port: 8081
federation:
port: 8080
datasource:
url: jdbc:postgresql://db:5432/openid-federation-db
user: openid-federation-db-user
password: openid-federation-db-password
db: openid-federation-db
# Prefer opaque secrets in production:
# password.secret.id: db-password # → OIDF_SECRET_DB_PASSWORD
oauth2:
issuer:
uri: http://keycloak:8080/realms/openid-federation
identity:
mode: account
cors:
allowed:
origins: "*"
methods: GET,POST,PUT,DELETE,OPTIONS
headers: Authorization,Content-Type,X-Account-Username
logger:
severity: INFO
output: TEXT
kms:
default:
provider: memory # memory | aws | azure
# Optional per-tenant overrides (property keys oidf.tenant.<id>.*)
# tenant:
# acme-corp:
# federation:
# root:
# identifier: https://acme.example
# kms:
# provider: azureEquivalent flat properties also work, e.g. oidf.federation.root.identifier=http://localhost:8080.
Compose and many operators still use env. Env overrides file defaults.
Copy .env.example to .env / .env.local for Docker Compose.
| Property key | Env (IDK-normalized) | Legacy env (JVM) |
|---|---|---|
oidf.federation.root.identifier |
OIDF_FEDERATION_ROOT_IDENTIFIER |
ROOT_IDENTIFIER |
oidf.federation.dev.mode |
OIDF_FEDERATION_DEV_MODE |
DEV_MODE / APP_DEV_MODE |
oidf.datasource.url |
OIDF_DATASOURCE_URL |
DATASOURCE_URL |
oidf.datasource.user |
OIDF_DATASOURCE_USER |
DATASOURCE_USER |
oidf.datasource.password |
OIDF_DATASOURCE_PASSWORD |
DATASOURCE_PASSWORD |
oidf.datasource.db |
OIDF_DATASOURCE_DB |
DATASOURCE_DB |
oidf.server.admin.port |
OIDF_SERVER_ADMIN_PORT |
ADMIN_SERVER_PORT |
oidf.server.federation.port |
OIDF_SERVER_FEDERATION_PORT |
SERVER_PORT |
oidf.kms.default.provider |
OIDF_KMS_DEFAULT_PROVIDER |
KMS_PROVIDER |
oidf.logger.severity |
OIDF_LOGGER_SEVERITY |
LOGGER_SEVERITY |
oidf.cors.allowed.origins |
OIDF_CORS_ALLOWED_ORIGINS |
CORS_ALLOWED_ORIGINS |
APP_KEY=Nit5tWts42QeCynT1Q476LyStDeSd4xb
ROOT_IDENTIFIER=http://localhost:8080
DATASOURCE_URL=jdbc:postgresql://db:5432/openid-federation-db
DATASOURCE_USER=openid-federation-db-user
DATASOURCE_PASSWORD=openid-federation-db-password
DATASOURCE_DB=openid-federation-db
# Or IDK form: OIDF_FEDERATION_ROOT_IDENTIFIER=... OIDF_DATASOURCE_URL=...These may differ per tenant via oidf.tenant.<id>.* (or bare keys on IDK session tenant config):
| Setting | Example tenant key |
|---|---|
| Root entity URL | oidf.tenant.acme.federation.root.identifier |
| KMS provider | oidf.tenant.acme.kms.provider |
| Cache locality | oidf.tenant.acme.cache.http.resolver.locality |
| Header allow-list | oidf.tenant.acme.identity.account.header.allowed.principals |
Still APP-only: ports, datasource, identity.mode, OAuth2 issuer, CORS, logger.
Use OidfConfigBinder.getEffectiveFederationConfig / getEffectiveKmsConfig /
getEffectiveIdentityConfig / getEffectiveCacheLocality. Details:
docs/CONFIGURATION.md.
Select the provider with oidf.kms.default.provider or env OIDF_KMS_DEFAULT_PROVIDER / legacy KMS_PROVIDER:
memory: In-memory KMS (for development and testing only!)aws: AWS Key Management Serviceazure: Azure Key Vault
oidf:
kms:
default:
provider: memoryKMS_PROVIDER=memory
# Or: OIDF_KMS_DEFAULT_PROVIDER=memoryNote: The memory KMS is not storing any private keys!. It is in memory, which means they will be gone after a reboot. This was done on purpose, as you should use an external KMS system with proper protection like Azure Keyvault or AWS KMS.
When configured like below, AWS Key Management Service will be used. You need to provide your AWS region, and you will also need to provide an access key id and secret.
Creating an AWS Access Key and Secret Access Key
-
Sign In to AWS Management Console Use your AWS account credentials to sign in at https://aws.amazon.com/.
-
Navigate to IAM (Identity and Access Management)
- In the AWS Management Console, search for IAM or select it from the list of services.
- IAM lets you manage users, groups, roles, and their associated access credentials.
-
Create or Select an IAM User
- If you already have a user for programmatic access, select that user from the Users tab.
- Otherwise, to create a new user:
- Click on Add User.
- Enter a username.
- Under Select AWS Access Type, check Programmatic access (this is needed to generate an access key and secret key).
-
Set Permissions
- For initial testing of key management tasks, you could attach existing policies like * AWSKeyManagementServicePowerUser. This policy grants broad administrative permissions for KMS, allowing the creation and management of keys.
- Important: The
AWSKeyManagementServicePowerUserpolicy does not grant permissions to perform cryptographic signing operations. To enable signing operation, you must explicitly grant thekms:Signpermission. - For production environments, always follow the principle of least privilege, granting only the necessary permissions for the required tasks.
-
Review and Create User
- Complete the steps and on the final screen, AWS will display an Access Key ID and Secret Access Key.
- Important: Save your secret access key securely as it is shown only once. You can download the credentials as a CSV file.
Use the values from step 5 in env (or map them under IDK kms.providers.aws.* property keys):
KMS_PROVIDER=aws
# Or: OIDF_KMS_DEFAULT_PROVIDER=aws
AWS_APPLICATION_ID=your-own-id
# The ID is only used internally and is not related to anything in AWS itself
AWS_REGION=your-aws-region-like-eu-west-1
# The AWS region where the KMS key is located. Example: eu-west-2
AWS_ACCESS_KEY_ID=your-aws-access-key
# Your AWS access key ID from step 5.
AWS_SECRET_ACCESS_KEY=your-aws-secret-access-key
# Your AWS secret access key from step 5.Optional environment variables for AWS:
AWS_MAX_RETRIES=5
# The maximum number of retries for operations against AWS KMS, defaults to 10.
AWS_BASE_DELAY=200
# The base delay (in milliseconds) before retrying a failed operation, defaults to 500
AWS_MAX_DELAY=2000
# The maximum delay (in milliseconds) to wait between retries, defaults to 15000KMS_PROVIDER=azure
# Use 'azure' to select Azure Key Vault as the KMS.
AZURE_KEYVAULT_APPLICATION_ID=your-azure-app-id
# The Azure application (client) ID used to authenticate with Azure Key Vault.
AZURE_KEYVAULT_URL=https://your-keyvault-name.vault.azure.net/
# The URL for your Azure Key Vault instance.
AZURE_KEYVAULT_TENANT_ID=your-azure-tenant-id
# The Azure tenant ID used for authentication.
AZURE_KEYVAULT_CLIENT_ID=your-keyvault-client-id
# The client ID for the Azure Key Vault application.
AZURE_KEYVAULT_CLIENT_SECRET=your-keyvault-client-secret
# The client secret corresponding to the Azure Key Vault client.Optional environment variables for Azure Keyvault:
AZURE_KEYVAULT_MAX_RETRIES=5
# The maximum number of retries for operations against Azure Key Vault, defaults to 10
AZURE_KEYVAULT_BASE_DELAY=200
# The base delay (in milliseconds) before retrying a failed operation, defaults to 500
AZURE_KEYVAULT_MAX_DELAY=2000
# The maximum delay (in milliseconds) to wait between retries, defaults to 15000KC_BOOTSTRAP_ADMIN_USERNAME=admin
# Default username for the local Keycloak OAuth2 provider.
KC_BOOTSTRAP_ADMIN_PASSWORD=admin
# Default password for the local Keycloak instance.
OAUTH2_RESOURCE_SERVER_JWT_ISSUER_URI=http://keycloak:8080/realms/openid-federation
# The JWT issuer URI for the local Keycloak instance.- Prefer file config for non-secrets; put secrets in env or opaque secret ids (
oidf.datasource.password.secret.id). - Replace default values (e.g.,
admin,localhost,password) with secure values for production environments. - Ensure the root identifier (
oidf.federation.root.identifier/ROOT_IDENTIFIER) is a publicly accessible URL if deploying in a live environment. - Select the appropriate KMS provider based on your environment:
- For development or testing, the in-memory KMS is sufficient. Note: Keys are ephemeral and thus will be gone after a restart/reboot
- For production, use AWS KMS or Azure Key Vault to ensure robust security for key management.
- Never commit sensitive credentials (such as AWS or Azure secrets) into version control.
Once configuration is in place (files and/or env), start the OpenID Federation service stack with Docker Compose:
docker compose upThis command will initialize all necessary services, including the database and Keycloak, as defined in the Docker Compose configuration file.
The admin endpoints are protected and require a valid JWT access token. To acquire one, follow these steps:
-
Send a POST Request to the Keycloak Token Endpoint:
POST http://localhost:8082/realms/openid-federation/protocol/openid-connect/token
-
Provide the Required Credentials in the Request Body:
Use
x-www-form-urlencodedformat with the following parameters:
/
3. **Example cURL Command**:
```bash
curl -X POST http://localhost:8082/realms/openid-federation/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=openid-client" \
-d "client_secret=th1s1s4s3cr3tth4tMUSTb3ch4ng3d"
-
Parse the Response:
A successful request returns a JSON object. Extract the
access_tokenfield:{ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "expires_in": 300, "token_type": "Bearer", "not-before-policy": 0, "scope": "openid" } -
Use the Access Token in Subsequent API Requests:
Admin is always authenticated. Every Admin API call (except
/health) must include:Authorization: Bearer <access_token>Without a valid Bearer token the admin server returns 401.
Replace client_secret with a secure value in a production environment. The token expires after the duration given in
expires_in; acquire a new token when needed.
Set oidf.oauth2.issuer.uri / OIDF_OAUTH2_ISSUER_URI (or the JVM alias OAUTH2_RESOURCE_SERVER_JWT_ISSUER_URI) so
the admin server can validate tokens. Startup fails if the issuer is blank. There is no setting to skip admin
authentication.
The examples below assume account mode (the default). After authentication, send X-Account-Username when you
need to work on a non-root account and your principal is allow-listed for that rebind. The header selects the entity
context; it does not authenticate the caller. In external mode omit the header entirely: the token’s tenant claim
is the only entity context, and /accounts is not available.
Account REST applies only when oidf.identity.mode / OIDF_IDENTITY_MODE is account (or legacy). In external mode the host provisions
tenants outside this API.
-
Send a
POSTrequest to the following endpoint:POST http://localhost:8081/accounts Authorization: Bearer <access_token> Content-Type: application/json
-
Include a JSON body with the desired account details. For example:
{ "username": "{username}", "identifier": "http://localhost:8081/{username}" }
All admin requests need Authorization: Bearer <access_token>. Subsequent requests use X-Account-Username to select
the account context when the caller is allow-listed (defaults to the root account when the header is omitted).
To delete a tenant account, follow these steps:
-
Send a
DELETErequest to the following endpoint:DELETE http://localhost:8081/accounts Authorization: Bearer <access_token> X-Account-Username: {username} # root account cannot be deleted
-
Send a
POSTrequest to create a new key pair:POST http://localhost:8081/keys Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
You can use the param kms_key_ref to assign a name to the key. This value will not end up in the JWK itself, but you can use it for selecting which key to sign with. The kid value could also be used, but that is typically not known upfront as in most cases it is generated as the SHA1 thumbprint of the JWK.
You can also provide a JOSE/JWA signature algorithm provided the KMS configured supports it. Typical values are ES256, ES384, ES512
{
"kmsKeyRef": "my-key",
"signatureAlgorithm": "ES256"
}-
Send a
GETrequest to list the keys:GET http://localhost:8081/keys Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Send a
DELETErequest to revoke a key:DELETE http://localhost:8081/keys/{keyId} Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Optionally, include a
reasonquery parameter to specify the reason for revocation:DELETE http://localhost:8081/keys/{keyId}?reason=Key+compromised Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
To assign metadata to your entity, follow these steps:
-
Send a
POSTrequest to the following endpoint:POST http://localhost:8081/metadata Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Include a JSON body with the metadata details. For example:
{ "key": "basic_metadata", "metadata": { "client_uri": "http://localhost:8081", "contacts": [ "admin@example.com", "support@example.com" ] } }
-
Send a
GETrequest to list all metadata:GET http://localhost:8081/metadata Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Send a
DELETErequest to delete a metadata entry by its ID:DELETE http://localhost:8081/metadata/{id} Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
Authority Hints are used to indicate which authorities an entity recognizes in the federation. These authorities can validate trust chains and issue trust marks.
Send a GET request to retrieve all authority hints for an account:
GET http://localhost:8081/authority-hints
Authorization: Bearer <access_token>
X-Account-Username: {username} # Optional, defaults to rootSend a POST request to add a new authority hint:
POST http://localhost:8081/authority-hints
Authorization: Bearer <access_token>
X-Account-Username: {username} # Optional, defaults to root
{
"identifier": "http://localhost:8081/authority-hints"
}Send a DELETE request to remove an authority hint by its ID:
DELETE http://localhost:8081/authority-hints/{id}
Authorization: Bearer <access_token>
X-Account-Username: {username} # Optional, defaults to rootRemember to publish your entity configuration after making changes to authority hints for them to take effect.
-
Send a
POSTrequest to the following endpoint:POST http://localhost:8081/subordinates Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Include a JSON body with the subordinate details. For example:
{ "identifier": "http://localhost:8081/subordinate1" }
-
Send a
GETrequest to list all subordinates:GET http://localhost:8081/subordinates Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Send a
DELETErequest to delete a subordinate by its ID:DELETE http://localhost:8081/subordinates/{id} Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Send a
POSTrequest to the following endpoint:POST http://localhost:8081/subordinates/{subordinateId}/metadata Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Include a JSON body with the metadata details. For example:
{ "key": "example_key", "metadata": { "description": "Example metadata description" } }
- Send a
GETrequest to list all metadata for a subordinate:GET http://localhost:8081/subordinates/{subordinateId}/metadata Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
- Send a
DELETErequest to delete a metadata entry by its ID:DELETE http://localhost:8081/subordinates/{subordinateId}/metadata/{id} Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Send a
POSTrequest to the following endpoint:POST http://localhost:8081/subordinates/{id}/jwks Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Include a JSON body with the JWKS details. For example:
{ "kid": "example_key", "key_ops": ["sign", "verify"], "kty": "EC" }
-
Send a
GETrequest to list all JWKS for a subordinate:GET http://localhost:8081/subordinates/{id}/jwks Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Send a
DELETErequest to delete a JWKS entry by its ID:DELETE http://localhost:8081/subordinates/{id}/jwks/{jwkId} Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Send a
GETrequest to retrieve the statement for a subordinate:GET http://localhost:8081/subordinates/{id}/statement Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Send a
POSTrequest to publish a subordinate statement:POST http://localhost:8081/subordinates/{id}/statement Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Optionally include a
kmsKeyRefparameter if you want to sign with a specific key. kmsKeyRef always overrideskidif both are supplied. If none are specified the first key available will be used -
Optionally include a
kidparameter if you want to sign with a specific key. The kid is typically the sha1 thumbprint of the key, and generated by the underlying KMS. Opposed to the kmsKeyRef which you choose yourself . -
Optionally include a
dryRunparameter in the request body to test the statement publication without making changes:{ "dryRun": true }
-
Send a
GETrequest to retrieve the entity configuration statement:GET http://localhost:8081/entity-statement Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Send a
POSTrequest to publish the entity configuration statement:POST http://localhost:8081/entity-statement Authorization: Bearer <access_token> X-Account-Username: {username} # Optional, defaults to root
-
Optionally include a
kmsKeyRefparameter if you want to sign with a specific key. kmsKeyRef always overrideskidif both are supplied. If none are specified the first key available will be used -
Optionally include a
kidparameter if you want to sign with a specific key. The kid is typically the sha1 thumbprint of the key, and generated by the underlying KMS. Opposed to the kmsKeyRef which you choose yourself. -
Optionally, include a
dryRunparameter in the request body to test the statement publication without making changes:{ "dryRun": true }
sequenceDiagram
participant TA as Trust Anchor
participant TI as Trust Mark Issuer
participant H as Holder
TA->>TA: Create Trust Mark Type
TA->>TI: Authorize Issuer
TI->>H: Issue Trust Mark
H->>H: Store Trust Mark
Note over H: Publish new Entity Statement
# Create Trust Anchor account
POST http://localhost:8081/accounts
Authorization: Bearer <access_token>
Content-Type: application/json
{
"username": "trust-anchor",
"identifier": "http://localhost:8080/trust-anchor"
}
# Generate Trust Anchor keys
POST http://localhost:8081/keys
Authorization: Bearer <access_token>
X-Account-Username: trust-anchor
# Create Trust Mark type
POST http://localhost:8081/trust-mark-types
Authorization: Bearer <access_token>
X-Account-Username: trust-anchor
Content-Type: application/json
{
"identifier": "http://localhost:8080/trust-anchor/trust-mark-types/exampleType"
}# Create Issuer account
POST http://localhost:8081/accounts
Authorization: Bearer <access_token>
Content-Type: application/json
{
"username": "trust-mark-issuer",
"identifier": "http://localhost:8080/trust-mark-issuer"
}
# Generate keys for the Issuer
POST http://localhost:8081/keys
Authorization: Bearer <access_token>
X-Account-Username: trust-mark-issuer
# Publish Issuer configuration
POST http://localhost:8081/entity-statement
Authorization: Bearer <access_token>
X-Account-Username: trust-mark-issuer# Authorize Issuer using Trust Anchor account
POST http://localhost:8081/trust-mark-types/{trust-mark-type-id}/issuers
Authorization: Bearer <access_token>
X-Account-Username: trust-anchor
Content-Type: application/json
{
"identifier": "http://localhost:8080/trust-mark-issuer"
}
# Publish Trust Anchor configuration
POST http://localhost:8081/entity-statement
Authorization: Bearer <access_token>
X-Account-Username: trust-anchor# Issue Trust Mark to holder
POST http://localhost:8081/trust-marks
Authorization: Bearer <access_token>
X-Account-Username: trust-mark-issuer
Content-Type: application/json
{
"sub": "http://localhost:8080/trust-mark-holder",
"trust_mark_id": "http://localhost:8080/trust-mark-types/exampleType"
}# Create Holder account
POST http://localhost:8081/accounts
Authorization: Bearer <access_token>
Content-Type: application/json
{
"username": "trust-mark-holder",
"identifier": "http://localhost:8080/trust-mark-holder"
}
# Generate keys for the Holder
POST http://localhost:8081/keys
Authorization: Bearer <access_token>
X-Account-Username: trust-mark-holder
# Store received Trust Mark
POST http://localhost:8081/received-trust-marks
Authorization: Bearer <access_token>
Content-Type: application/json
X-Account-Username: trust-mark-holder
{
"trust_mark_id": "http://localhost:8080/trust-mark-types/exampleType",
"jwt": "eyJ..." # Replace with JWT token issued in step 4
}
# Publish Holder configuration
POST http://localhost:8081/entity-statement
Authorization: Bearer <access_token>
X-Account-Username: trust-mark-holder# Check Trust Mark status
POST http://localhost:8080/trust-mark-issuer/trust-mark-status
Content-Type: application/json
{
"trust_mark_id": "http://localhost:8080/trust-mark-types/exampleType",
"sub": "http://localhost:8080/trust-mark-holder"
}Apache License Version 2.0
- John Melati
- Niels Klomp
- Zoë Maas
- Sander Postma