Skip to main content

WebSSO of the platform: Keycloak or LemonLDAP::NG

Navin Enterprise (portal, Control Plane, sector agents) signs its users in with OpenID Connect. The Manager installs the provider the bank chooses, or connects to the one it already runs. Contract of the bindings: navin-manager/CONTRACT.md (section 10, generated parameters and OpenID Connect bindings).

Choosing the provider​

ChoiceWhen to take itWhat the Manager does
Keycloak (default)No preference of the bank. Apache-2.0 licence.Installs the service keycloak, imports the realm enterprise-ai (clients portal, control-plane, cli, roles, TOTP), federates the LDAP directory or Entra ID.
LemonLDAP::NGThe bank already runs LemonLDAP::NG or requires an OW2 WebSSO. GPL-2.0-or-later licence: exception LEX-034, pending the decision of the client.Installs the service lemonldap-ng: OpenID Connect portal over the LDAP or Active Directory of the bank, roles computed from the groups, TOTP, sessions in PostgreSQL.
LemonLDAP::NG already installedThe bank already runs its LemonLDAP::NG.Nothing to install: add the existing service lemonldap-ng (Add a service, existing service) with a confidential client of the portal; the Manager checks this client and gives Navin Enterprise the issuer and the JWKS the portal publishes.
Other existing providerAnother OpenID Connect WebSSO already in place.Nothing to install: declare a connection of type oidc (https issuer) and choose it for the oidc binding of Navin Enterprise.

When both Keycloak and LemonLDAP::NG are installed on the cluster, the Manager keeps Keycloak; the explicit choice of the install wizard (binding oidc, managed service lemonldap-ng) always wins.

Installing LemonLDAP::NG​

  1. Declare the directory as an ldap connection (Administration, Connections): URL ldaps://directory:636, or ldap://directory:389 with StartTLS, DN of the service account, its password, and the certificate of the authority of the directory. The portal never speaks LDAP without TLS and always verifies the certificate.
  2. In the install wizard, add lemonldap-ng: binding database to PostgreSQL (installed or a connection), binding directory to the connection of step 1.
  3. Fill in at least the users base DN (users_base), the directory kind (openldap or active_directory), the URL of the Navin Enterprise portal (portal_url) and the public URL of the WebSSO (public_url).
  4. Mode cluster: at least two portal nodes behind a load balancer; public_url is then the address of the load balancer. The nodes share the sessions in PostgreSQL and get the same configuration.

The Manager renders the whole portal configuration (lmConf-1.json): the LemonLDAP::NG manager interface is not installed and the configuration only changes through the parameters of the service. A one shot task creates the session tables (sessions, psessions, oidcsessions) when they do not exist.

Connecting a LemonLDAP::NG already installed​

  1. In the LemonLDAP::NG of the bank, declare a confidential OpenID Connect client for the Manager (identifier, secret, authentication client_secret_basic). It needs no grant: the Manager only checks that the token endpoint of the portal accepts this secret.
  2. In the Manager, Add a service, existing service, LemonLDAP::NG: URL of the portal (for example https://auth.bank.example), identifier and secret of this client, and the certificate of the authority of the portal when a public authority did not issue it. The Manager reads the discovery document (/.well-known/openid-configuration) and checks the secret at the token endpoint (/oauth2/token): a wrong secret is refused and nothing is saved. The secret is sealed and never shown again; the check runs again every minute.
  3. Install navin-enterprise: its oidc binding offers the connected LemonLDAP::NG. The Control Plane gets the issuer (issuer) and the JWKS (jwks_uri) exactly as the portal publishes them, read again at each check.
  4. On the LemonLDAP::NG side, the team of the bank declares the client portal of Navin Enterprise: public client, PKCE S256 required, redirect URI <Navin Enterprise portal>/auth/callback, additional audience control-plane in JWT access tokens, and the claims roles (array of the platform roles), groups, tenant and clearance. The Control Plane reads the roles in roles as in realm_access.roles.

Connecting Navin Enterprise​

In the wizard, the oidc binding of navin-enterprise offers keycloak, lemonldap-ng or an oidc connection. With lemonldap-ng, the Manager gives the Control Plane:

VariableValue
EAI_OIDC_ISSUERpublic_url of the WebSSO
EAI_OIDC_JWKS_URLpublic_url followed by /oauth2/jwks
EAI_OIDC_AUDIENCEcontrol-plane (parameter audience of the WebSSO, oidc_audience of Navin Enterprise)

The client portal (identifier portal_client_id) is public, with PKCE S256 required, a single redirect URI (portal_url followed by /auth/callback) and the audience control-plane added to its access tokens. Other clients (the BFF of a sector agent, for example) are declared in extra_clients; a client with a secret is confidential (client_secret_basic).

Roles, organisation and clearance​

ClaimOrigin
roles and realm_access.rolesviewer for everyone, plus the role of each directory group listed in role_groups (by default navin-platform-admins, navin-tenant-admins, navin-security-officers, navin-finops, navin-developers, navin-auditors)
groupsgroups of the user in the directory (nested groups with nested_groups)
tenantattribute tenant_attribute of the directory, else default_tenant
clearanceattribute clearance_attribute of the directory (public, internal, confidential, restricted or secret)

realm_access.roles is the claim of the Keycloak realm of the platform: a plugin of the catalog (Navin::LemonLDAP::Claims) copies it from roles, so every consumer reads the same roles whatever the provider. The claims are given in the access and ID tokens, whatever the scopes requested.

What was verified​

navin-manager/server/tests/test_lemonldap_ng_real.py runs LemonLDAP::NG 2.23.4 (packages of lemonldap-ng.org) with the configuration rendered by the catalog, OpenLDAP with StartTLS, PostgreSQL and nginx, then:

  • creates the session tables with the rendered task, twice in a row;
  • refuses a wrong password, then signs in a user of the directory through the authorization code flow with PKCE of the client portal, and refuses a reuse of the code;
  • has the access token verified by the code of the Control Plane (eai.auth.verify_jwt): RS256 signature of the published JWKS, issuer, audience control-plane, roles viewer and developer from the group navin-developers, realm_access.roles, groups, organisation and clearance;
  • finds the SSO session and the OpenID Connect sessions in PostgreSQL;
  • runs the purge of the local session cache the start script runs every hour: the live sessions are kept;
  • has nginx validate the rendered TLS configuration;
  • registers the same portal as an existing service: the confidential client is accepted with the right secret and refused with a wrong one, and the issuer and the JWKS the portal publishes are what Navin Enterprise receives.

A second run renders the mode cluster with two portal nodes behind a load balancer without stickiness (each request goes to the next node) and the TOTP required, the default:

  • both nodes get the same configuration, announce the same issuer and publish the same keys;
  • at the first sign in, the portal imposes the registration of a TOTP application: a wrong code is refused, the right one registers it, and the sign in resumes on the other node;
  • at the next sign in, the TOTP code is asked: a wrong code is refused without an SSO cookie, the right one signs in;
  • an authorization code issued by one node is exchanged at the other, the SSO cookie opens both nodes without a new password, and the Control Plane accepts the tokens;
  • the TOTP device is in the persistent sessions of PostgreSQL;
  • the purge the start script runs every hour removes the expired sessions and keeps the others.

This second run found a defect, now fixed: the local session cache of each node served a stale TOTP registration token after the other node had updated it. In mode cluster, the nodes now read every session from PostgreSQL, without a local cache.

test_catalog_lemonldap_ng.py checks the renders (OpenLDAP and Active Directory, LDAPS and StartTLS, session storage, clients, secrets kept out of the command line of the container) and the binding of Navin Enterprise.

Limits​

  • Official image docker.io/lemonldapng/lemonldap-ng:2.23.4 for x86_64 only: place the portal on x86_64 nodes.
  • No device code sign in (RFC 8628): LemonLDAP::NG 2.23.4 does not offer it. The CLI then authenticates with an API key; the cli client of the Keycloak realm has no equivalent.
  • The real runs go without Podman (absent from the development machine): the container image is not started there, its files and its command line are checked by the render tests. The switch of the start script to the user www-data for the purges is only checked through the syntax of the script.
  • Active Directory is only checked through the renders.
  • External PostgreSQL in verify-ca or verify-full: the portal verifies the certificate with the system authorities of the image; a private authority of the bank is not among them and the connection is then refused.
  • The token signing key is generated at install (RSA 3072 bits, sealed). Changing it means replacing the parameter signing_key, then applying with a restart: the tokens in flight become invalid.