Skip to main content

OpenID Connect (OIDC)

Overview

The OpenID Connect integration delegates user authentication from a Publica.la store to an external Identity Provider (IdP) that you already operate (Keycloak, Auth0, Okta, Azure AD, an in-house OIDC server, or any standards-compliant provider).

Your IdP authenticates the end user; Publica.la consumes the IdP response, provisions or links the local account, and starts a session in the store. The user never sees a Publica.la login form.

Not the right fit? Consider Auth Token when you can sign your own JWT, SAML 2.0 for enterprise SSO, or LTI for LMS contexts.


When to use OIDC

Use this integration when:

  • You already run an OIDC-compliant IdP and want to keep credentials there.
  • You need a hosted login form managed by your team (branding, MFA, password policies).
  • You want a centralized logout across your ecosystem.
  • You operate multiple regional storefronts that should share a single identity layer (see the multi-tenant pattern below).

If your platform can mint its own signed JWTs and you do not need a separate login form, Auth Token is simpler.


Setup

  1. Register Publica.la as a client in your IdP. Create an OIDC client (confidential) with:

    • Grant type: authorization_code (PKCE recommended).
    • Redirect URI: https://{store-domain}/embeddable-login-ui/{aggregator_id?} (provided by support during onboarding).
    • Scopes: at least openid email profile. Add any custom scope needed to expose your stable user identifier.
  2. Contact support with the following configuration:

    ParameterDescription
    issuer_urlBase URL of your OIDC provider (used to fetch .well-known/openid-configuration and JWKS).
    client_idOIDC client identifier registered in your IdP.
    client_secretOIDC client secret. Stored encrypted on our side.
    scopesScopes to request. Defaults to openid email profile.
    external_id_claimClaim that holds the stable user identifier (commonly sub).
    email_claimClaim that holds the user email. Defaults to email.
    logout_urlWhere Publica.la should redirect users after they log out of the store.
    post_login_urlOptional default landing URL after successful login. Defaults to /library.
    force_session_restartIf true, any active store session is terminated before redirecting to your IdP. Default: true.
  3. Wait for confirmation from support before pointing real users at the flow. We will provide a sandbox tenant for end-to-end testing.


ID token requirements

Publica.la validates the ID token returned by your IdP against the JWKS published at {issuer_url}/.well-known/openid-configuration. Tokens that fail any of the checks below are rejected and the user is redirected to your logout_url with an error code.

Validated claims

ClaimValidation
issMust match the configured issuer_url.
audMust contain the configured client_id.
expMust be in the future. Allow up to 60 seconds of clock skew between your IdP and our servers.
iatMust not be more than 5 minutes in the past.
nonceMust match the value Publica.la sent in the authorization request.

Required user data

The following values are pulled from the ID token or from the userinfo endpoint (whichever your IdP populates first):

FieldSource claim (default)Description
external_idsubStable, unique identifier for the user in your IdP. Stored on the Publica.la account and never changes.
emailemailUser email. Updated on every login if it changes upstream.
namenameOptional. Used for display only.
picturepictureOptional. Stored on first login; not updated afterwards.
caution

Choose an external_id_claim that is guaranteed to be stable for the lifetime of the user. sub is the recommended default. Avoid email-based identifiers; users can change their email upstream and you will end up with duplicate accounts.


Authentication flow

The flow always runs inside the embeddable login iframe at /{tenant_id}/embeddable-login-ui/{aggregator_id?}. The host page (your store frontend or mobile app) hosts the iframe and listens for postMessage events.

  1. The guest opens the store and clicks Log in.
  2. Publica.la terminates any existing session (when force_session_restart is enabled).
  3. The browser is redirected to your IdP authorization endpoint with response_type=code, the configured scopes, a fresh state, and a nonce.
  4. The user authenticates on your IdP form.
  5. The IdP redirects back to /{tenant_id}/embeddable-login-ui/{aggregator_id?} with code and state.
  6. Publica.la exchanges the code for an ID token, validates it against JWKS, and reads the configured claims.
  7. The local account is provisioned or linked (see User management).
  8. The iframe emits a loginSuccess message to the host:
    • On web the host stores an Auth Token cookie.
    • On mobile and desktop apps the host stores the platform JWT directly.
  9. The user lands on post_login_url (or /library by default).
View detailed authentication flow

User management

New users

When no Publica.la user exists with the incoming external_id:

  1. A new account is created with external_id set to the IdP's identifier.
  2. email, name, and picture are populated when present.
  3. The user is logged in and redirected to post_login_url.

Returning users

When a user with the same external_id already exists:

  1. The account is matched by external_id.
  2. email is updated if the IdP now returns a different value.
  3. name and picture are not updated after the first login.

Email uniqueness

Each email can be attached to a single external_id. If your IdP returns an email that already belongs to a different user on Publica.la, authentication fails with an email-conflict error. Resolve the conflict in your IdP (merge users, or move the email to the correct identity) before retrying.


Multi-tenant stores

If you operate several regional storefronts that share one IdP, register a single OIDC client and route users to the correct store using an aggregator_id segment in the redirect URI:

https://{store-domain}/{tenant_id}/embeddable-login-ui/{aggregator_id}

aggregator_id is a value defined during onboarding that maps to a specific regional tenant. Your IdP returns the user to the central domain (for example https://www.example.com), and the platform forwards the request to the right regional store based on aggregator_id. Keep the mapping list under version control on your side; support owns the mapping on ours.


Code examples

Generating the authorization URL

Reference snippet
const params = new URLSearchParams({
response_type: 'code',
client_id: process.env.OIDC_CLIENT_ID,
redirect_uri:
'https://store.example.com/2251799858000009/embeddable-login-ui/',
scope: 'openid email profile',
state: crypto.randomUUID(),
nonce: crypto.randomUUID(),
});

res.redirect(`${process.env.OIDC_ISSUER}/auth?${params.toString()}`);

You do not need to implement this yourself in most cases. Publica.la performs the redirect from the embeddable login iframe; the snippet is provided so you can reproduce the request in your IdP logs while debugging.

Listening for loginSuccess in the host

window.addEventListener('message', (event) => {
if (event.origin !== 'https://store.example.com') return;
if (event.data?.type !== 'loginSuccess') return;

const { authToken, user } = event.data;
// Web: persist authToken in a cookie or localStorage
// App: hand authToken to the native bridge
});

Troubleshooting

SymptomLikely causeRecommended fix
Infinite redirect loop after loginWrong aggregator_id or redirect URI mismatchVerify the URI registered in your IdP matches the one provided by support.
invalid_token after callbackClock drift between your IdP and our serversSync both sides via NTP. Skew above 60 seconds will reject the token.
Duplicate accounts for the same personexternal_id_claim is not stable (often email)Switch the claim to sub (or another stable identifier) and reprocess users.
User signs in but lands on /loginpost_login_url not configured or returns 404Set post_login_url to an existing route, or remove it to use /library.
email-conflict errorEmail already attached to a different external_idReconcile the duplicate in your IdP before re-attempting.
loginSuccess never fires in the hostIframe origin not allowlisted on the host pageEnsure the host validates event.origin against the store domain you embed.

Debugging tips

  1. Inspect the ID token at jwt.io to confirm iss, aud, exp, and nonce.
  2. Pull the discovery document at {issuer_url}/.well-known/openid-configuration and confirm jwks_uri is reachable from the public internet.
  3. Capture the IdP redirect in your browser DevTools. The state and nonce echoed back must match the ones Publica.la generated.
  4. Reproduce the failure on the sandbox tenant before changing production configuration.

Security best practices

  1. Use HTTPS end to end. Never expose the authorization or callback endpoints over plain HTTP.
  2. Rotate client secrets. Coordinate rotation with support; we will update the stored credential without downtime.
  3. Enforce short token lifetimes on the IdP side (ID tokens under 5 minutes is a sensible default).
  4. Pin a stable claim for external_id. Avoid mutable fields like email or username.
  5. Keep clock skew under 60 seconds between your IdP and our servers.
  6. Validate event.origin on the host page when handling postMessage to avoid accepting forged login events.
Admin Role Limitation – click to expand
caution

Authentication methods based on IP, URL referrer, LTI, or SAML are designed for end-user access only.
When the identifier corresponds to an administrator account, the platform intentionally starts the session with regular user permissions to prevent privilege escalation.

Administrators who need to access the Control Panel must:

  1. Set a password to their account.
  2. Log out of the current integration-based session.
  3. Log back in using email + password to regain full administrator privileges.
X

Graph View