Skip to content

Connecting your identity provider

People sign in to TinyConductor with an identity provider over OIDC. This page says what the provider must give TinyConductor, then shows the setup for Keycloak, Microsoft Entra ID, Okta and Auth0: what to do on the provider’s side, and the TinyConductor settings that match. How sign-in, mapping rules and permissions work is described in Identity and access.

There are two ways to connect:

  • Through the TinyBlox identity provider. TinyConductor trusts only the TinyBlox identity provider, which in turn signs people in with your directory. Every component then sees the same issuer and the same claims, and the provider differences below are handled once, in the TinyBlox identity provider. This is the recommended setup on the TinyBlox platform.
  • Directly. TinyConductor trusts your own provider. There is one hop less, and TinyConductor’s mapping rules read your provider’s claims as they are. The sections below are for this setup.

Either way, TinyConductor is registered as one client of the provider it trusts, with the values in the next section.

TinyConductor publishes its requirements as a client requirements document, a short machine-readable description that a provisioning tool can register at any OIDC provider, and that you can use as a checklist when you register it by hand. A running engine serves it, filled in with its own settings:

Terminal window
curl -s https://bpm.example.com/.well-known/tinyblox-client-requirements

Before the engine runs, use the template. {publicUrl} is the engine’s public host, such as bpm.example.com, and {clientId} the client id your provider gives TinyConductor:

apiVersion: tinyblox.ai/v1alpha1
kind: ClientRequirements
component: tinyconductor
clients:
- name: console
type: confidential
redirectUris: ["https://{publicUrl}/auth/callback"]
postLogoutRedirectUris: ["https://{publicUrl}/"]
grantTypes: [authorization_code]
responseTypes: [code]
tokenEndpointAuthMethod: client_secret_basic
scopes: [openid, profile, email, groups]
audience: "{clientId}"
idTokenSigningAlg: RS256
acceptedSigningAlgs: [RS256, ES256]
pkce: required
pkceMethod: S256
requiredClaims: [sub, groups, roles]
claims:
subject: sub
username: [preferred_username, email, sub]
displayName: name
mapped: [groups, roles]
tokenLifetimes:
clockSkewSeconds: 60
refreshTokens: false
sessionIdleSeconds: 1800
sessionAbsoluteSeconds: 43200
logout:
rpInitiated: true
idTokenHint: true
frontChannel: false
backChannel: false
secretRef: {secret: tinyconductor-oidc, key: client-secret}
roles: [admin, operator, task-worker, tenant-operator, viewer]

In plain words:

WhatValue
Kind of clientA web application with a client secret (a confidential client). A client without a secret (a public client) also works: leave the secret unset
Sign-in flowAuthorization Code with PKCE (S256). No implicit flow, no password grant
Redirect URIhttps://bpm.example.com/auth/callback, exactly
After sign-outhttps://bpm.example.com/, where the provider sends the browser back
Client authenticationThe client id and secret as HTTP Basic credentials (client_secret_basic)
Scopesopenid, profile, email, and groups where your provider sends group membership only when that scope is asked for
Token signaturesRS256 or ES256. Tokens signed any other way are refused
AudienceAccess tokens sent to the API carry TinyConductor’s audience in aud: the client id unless you choose another
Who a person isThe provider’s issuer and the person’s sub. The username comes from preferred_username, then email, then sub, unless you name another claim
Groups and rolesWhichever claims your mapping rules read, usually groups and roles
Token lifetimesThe ID token is checked once, at sign-in (allowing 60 seconds of clock difference); after that TinyConductor keeps its own session, which ends after 30 minutes without activity or after 12 hours. Refresh tokens are not used
Sign-outTinyConductor sends the browser to the provider’s end-session endpoint with the ID token as a hint. Front-channel and back-channel logout are not used

roles lists the role names a mapping rule can give: the built-in ones and any you add.

In Keycloak (in the realm your people sign in to):

  1. Clients → Create client. Client type OpenID Connect, client ID tinyconductor.
  2. Capability config: turn Client authentication on, and leave only Standard flow checked.
  3. Login settings: valid redirect URI https://bpm.example.com/auth/callback, valid post-logout redirect URI https://bpm.example.com/.
  4. Advanced → Proof Key for Code Exchange Code Challenge Method: S256.
  5. Credentials: copy the client secret.
  6. Client scopes → tinyconductor-dedicated → Add mapper → By configuration:
    • Group Membership, token claim name groups, Full group path off, added to the ID token and the access token;
    • Audience, included client audience tinyconductor, added to the access token. Programs that call the API with a Keycloak token need it.

Keycloak sends realm roles in realm_access.roles and client roles in resource_access.tinyconductor.roles; a mapping rule reads either by that dotted path. The issuer is https://keycloak.example.com/realms/<realm>.

TinyConductor:

Terminal window
docker run -d --name tinyconductor \
-p 8080:8080 \
-v tinyconductor-pgdata:/var/lib/postgresql/data \
-v /srv/secrets/keycloak-client-secret:/run/secrets/oidc-client-secret:ro \
-e CONDUCTOR_OIDC_ISSUER=https://keycloak.example.com/realms/acme \
-e CONDUCTOR_OIDC_CLIENT_ID=tinyconductor \
-e CONDUCTOR_OIDC_CLIENT_SECRET_FILE=/run/secrets/oidc-client-secret \
-e CONDUCTOR_OIDC_REDIRECT_URI=https://bpm.example.com/auth/callback \
-e CONDUCTOR_OIDC_DISPLAY_NAME=Keycloak \
-e 'CONDUCTOR_AUTH_MAPPING_RULES_JSON=[{"claim":"groups","equals":"bpm-admins","roles":["admin"]},{"claim":"realm_access.roles","equals":"bpm-operator","roles":["operator"]}]' \
registry.tinyfactory.ai/tinyblox/tinyconductor-bundled:0.1.0

Keycloak sends groups through the mapper, so the default scopes (openid,profile,email) are enough, and the username is Keycloak’s own (preferred_username). A local Keycloak, end to end walks through the same setup on one machine with Keycloak’s command-line tool.

In the Microsoft Entra admin center:

  1. App registrations → New registration. Name TinyConductor, Accounts in this organizational directory only, redirect URI of platform Web: https://bpm.example.com/auth/callback.
  2. Authentication: add a second Web redirect URI, https://bpm.example.com/. Entra sends the browser back after sign-out only to a registered redirect URI. Leave Access tokens and ID tokens under implicit grant unchecked.
  3. Certificates & secrets → New client secret: copy its Value.
  4. Token configuration → Add groups claim: Groups assigned to the application, emitted as Group ID in the ID token. Then Enterprise applications → TinyConductor → Users and groups: assign the groups that may use TinyConductor.
  5. Or, instead of groups, App roles → Create app role (for example value TinyConductor.Admin, allowed for users and groups) and assign people or groups to it. App roles arrive in the roles claim.
  6. Token configuration → Add optional claim → ID: email, if you want the console to show it.
  7. Manifest: set "requestedAccessTokenVersion": 2 ("accessTokenAcceptedVersion": 2 in the older manifest format). Programs that call the API with an Entra access token need it: version 1 tokens name another issuer (https://sts.windows.net/…), which TinyConductor refuses.

Watch for:

  • Many groups. When a person is in more than 200 groups, Entra leaves the groups claim out and sends a link to look them up instead. TinyConductor does not follow that link, so such a person matches no group rule. Emitting only groups assigned to the application (step 4), or using app roles (step 5), avoids it.
  • Group ids. groups holds object ids, so a mapping rule names a group by its id, such as 7c5a…-….
  • Do not ask for the groups scope. Entra has no such scope and refuses the sign-in; keep the default scopes.

TinyConductor:

Terminal window
-e CONDUCTOR_OIDC_ISSUER=https://login.microsoftonline.com/<directory-id>/v2.0 \
-e CONDUCTOR_OIDC_CLIENT_ID=<application-id> \
-e CONDUCTOR_OIDC_CLIENT_SECRET_FILE=/run/secrets/oidc-client-secret \
-e CONDUCTOR_OIDC_REDIRECT_URI=https://bpm.example.com/auth/callback \
-e CONDUCTOR_OIDC_DISPLAY_NAME='Microsoft' \
-e 'CONDUCTOR_AUTH_MAPPING_RULES_JSON=[{"claim":"roles","equals":"TinyConductor.Admin","roles":["admin"]},{"claim":"groups","equals":"<group-object-id>","roles":["operator"]}]'

The username is the sign-in name in preferred_username, such as wanda@contoso.com; Entra’s sub and oid are opaque ids and make poor usernames. With requestedAccessTokenVersion 2, access tokens for the API carry the application id as audience, which is the default CONDUCTOR_OIDC_AUDIENCE. If you expose an API with its own application ID URI instead, set CONDUCTOR_OIDC_AUDIENCE to what its tokens carry.

In the Okta Admin Console:

  1. Applications → Create App Integration: OIDC - OpenID Connect, Web Application.
  2. Grant type: Authorization Code only. Sign-in redirect URI https://bpm.example.com/auth/callback, sign-out redirect URI https://bpm.example.com/.
  3. Client Credentials: Client secret, and check Require PKCE as additional verification. Copy the client ID and the secret.
  4. Assignments: assign the groups that may use TinyConductor.
  5. Security → API → Authorization Servers → default → Claims → Add Claim: name groups, include in the ID token (Always) and in the access token, value type Groups, filter Starts with bpm- (or Matches regex .* for every group). Okta sends no groups claim until such a filter exists.
  6. On the same authorization server, Settings → Audience is the audience of its access tokens (api://default unless you change it).

TinyConductor:

Terminal window
-e CONDUCTOR_OIDC_ISSUER=https://acme.okta.com/oauth2/default \
-e CONDUCTOR_OIDC_CLIENT_ID=<client-id> \
-e CONDUCTOR_OIDC_CLIENT_SECRET_FILE=/run/secrets/oidc-client-secret \
-e CONDUCTOR_OIDC_AUDIENCE=api://default \
-e CONDUCTOR_OIDC_REDIRECT_URI=https://bpm.example.com/auth/callback \
-e CONDUCTOR_OIDC_DISPLAY_NAME=Okta \
-e 'CONDUCTOR_AUTH_MAPPING_RULES_JSON=[{"claim":"groups","equals":"bpm-admins","roles":["admin"]},{"claim":"groups","equals":"bpm-operators","roles":["operator"]}]'

The issuer is the authorization server’s (…/oauth2/default), not the bare Okta address: only that server issues access tokens other applications can check. The groups claim configured in step 5 comes with the default scopes; do not add a groups scope, which this server does not define. The username is the Okta login in preferred_username, usually an email address.

In the Auth0 Dashboard:

  1. Applications → Create Application: Regular Web Applications.

  2. Settings: Allowed Callback URLs https://bpm.example.com/auth/callback, Allowed Logout URLs https://bpm.example.com/. Copy the domain, the client ID and the client secret.

  3. Credentials → Authentication Method: Client Secret (Basic).

  4. Advanced Settings → OAuth: JSON Web Token signature algorithm RS256.

  5. User Management → Roles: create roles such as bpm-admin and assign them to people.

  6. Actions → Triggers → post-login: add an action that puts the roles in the tokens. Auth0 requires custom claims to be named by a URL you own:

    exports.onExecutePostLogin = async (event, api) => {
    const roles = event.authorization?.roles ?? [];
    api.idToken.setCustomClaim('https://bpm.example.com/roles', roles);
    api.accessToken.setCustomClaim('https://bpm.example.com/roles', roles);
    };
  7. For programs that call the API with an Auth0 token, Applications → APIs → Create API with an identifier such as https://bpm.example.com/api: that identifier is the audience of its access tokens.

TinyConductor:

Terminal window
-e CONDUCTOR_OIDC_ISSUER=https://acme.eu.auth0.com/ \
-e CONDUCTOR_OIDC_CLIENT_ID=<client-id> \
-e CONDUCTOR_OIDC_CLIENT_SECRET_FILE=/run/secrets/oidc-client-secret \
-e CONDUCTOR_OIDC_AUDIENCE=https://bpm.example.com/api \
-e CONDUCTOR_OIDC_REDIRECT_URI=https://bpm.example.com/auth/callback \
-e CONDUCTOR_OIDC_DISPLAY_NAME=Auth0 \
-e 'CONDUCTOR_AUTH_MAPPING_RULES_JSON=[{"claim":"https://bpm.example.com/roles","equals":"bpm-admin","roles":["admin"]}]'

A mapping rule names the custom claim by its whole URL; the dots in it are not read as a path. Auth0 sends no preferred_username, so the username is the email address; set CONDUCTOR_OIDC_USERNAME_CLAIM to another claim if you prefer.

The chart sets the same values under auth.oidc (issuer, clientId, audience, redirectUri, scopes, displayName, usernameClaim, alias) and reads the client secret from a Secret you create (auth.oidc.clientSecret.existingSecret, key client-secret). Mapping rules go in auth.mappingRules.

TinyConductor can trust more than one provider, for example your staff directory and a partner’s. Each is known by its issuer and has a short alias; see Several identity providers for the settings.