OpenID Connect ("Login with Nimbu")
Use a Nimbu site's customer accounts as an OpenID Connect identity provider: discovery, the authorization code flow, ID tokens, userinfo claims and the storefront consent screen.
A Nimbu site can act as an OpenID Connect (OIDC) provider for its customers. Another application, or another Nimbu site, can then offer "Login with your site": the customer signs in on your storefront, approves the request, and the relying party receives an ID token plus profile claims.
This page covers customer login. For OAuth in general (scopes, refresh tokens, password grant) see Authentication. MCP connectors use a separate admin-user flow, documented under MCP.
Enable the provider
OIDC is off by default and is a per-site setting that Nimbu support turns on ("Enable OpenID Connect
Provider"). While it is off, the discovery document returns 404 and any authorization request that
includes the openid scope fails with invalid_scope.
Register a client
In the backoffice of the providing site, go to Settings → Apps → New OAuth 2 app:
- Authorization Callback URL: the relying party's redirect URI. Redirect URIs are matched exactly.
- Confidential: leave on for server-side clients that can keep the secret. Public clients (off) must use PKCE and always see the consent screen.
- Customer scopes: add
openid, plusemail,profileand/orfull_profileas needed. Scopes the client requests but that are not listed here are silently dropped.
The app's key is the client_id; its secret is the client_secret.
Discovery
Each site publishes its configuration on its primary domain:
curl "https://www.example.com/.well-known/openid-configuration"{
"issuer": "https://www.example.com",
"authorization_endpoint": "https://www.example.com/oauth2/authorize",
"token_endpoint": "https://api.nimbu.io/oauth2/tokens",
"userinfo_endpoint": "https://api.nimbu.io/oauth2/userinfo",
"jwks_uri": "https://api.nimbu.io/oauth2/certs",
"scopes_supported": ["openid", "email", "profile", "full_profile"],
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
"claims_parameter_supported": false,
"claims_supported": [
"sub", "iss", "aud", "iat", "name", "family_name", "given_name", "locale", "email", "email_verified",
"https://www.example.com/loyalty_number"
]
}- The issuer is
https://plus the site's primary domain. The authorize endpoint lives on the storefront; token, userinfo and keys live on the API host. - Only the authorization code flow is supported, and ID tokens are signed with RS256.
claims_supportedlists one extra claim per customer custom field, named<issuer>/<field_name>.
The flow
1. Authorize. Send the customer to the storefront:
https://www.example.com/oauth2/authorize
?response_type=code
&client_id=CLIENT_ID
&redirect_uri=https%3A%2F%2Fapp.example.org%2Fcallback
&scope=openid%20email%20profile
&state=RANDOM_STATE
&nonce=RANDOM_NONCE
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256- Only
response_type=codeis accepted. - PKCE: only
S256. Public clients must send acode_challenge; confidential clients may, and once a challenge is sent the verifier is always required at the token endpoint. redirect_urimust match a registered URI. It may be omitted only when the app has exactly one.
If the customer is not signed in, Nimbu asks them to log in first. They then see the consent page,
rendered from your theme's templates/customers/oauth2_consent.liquid (see
Customer login for the template and the
{% oauth2_consent_form %} tag). Confidential apps that the customer already approved for the same
scopes skip the consent screen. On approval the customer is redirected to
redirect_uri?code=…&state=…; on denial with error=access_denied.
2. Exchange the code.
curl -X POST "https://api.nimbu.io/oauth2/tokens" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code="$CODE" \
-d redirect_uri="https://app.example.org/callback" \
-d code_verifier="$VERIFIER"{
"access_token": "eyJ…",
"token_type": "bearer",
"expires_in": 1800,
"refresh_token": "…",
"scope": "openid email profile",
"id_token": "eyJ…"
}- Send the client secret with HTTP Basic auth or as
client_secretin the body. Public clients send onlyclient_id. redirect_urimust be identical to the one used in the authorize request.id_tokenis only returned whenopenidwas granted.- Access tokens last 30 minutes. Refresh with
grant_type=refresh_token; customer refresh tokens are not rotated, so keep using the same one.
3. Validate the ID token. Fetch the keys from https://api.nimbu.io/oauth2/certs and pick the key
by the token's kid (the set also contains keys used for MCP tokens). The ID token holds iss (the
site issuer), sub (the customer id), aud (your client_id), iat, exp (one hour) and nonce
when you sent one. Profile data is not in the ID token; read it from userinfo.
4. Read userinfo.
curl "https://api.nimbu.io/oauth2/userinfo" \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"sub": "66f0a1b2c3d4e5f60718293a",
"email": "jane@example.com",
"email_verified": true,
"name": "Jane Doe",
"given_name": "Jane",
"family_name": "Doe",
"locale": "nl",
"https://www.example.com/loyalty_number": "A-1042"
}GET and POST both work. Claims depend on the granted scopes:
| Scope | Claims |
|---|---|
openid | sub (customer id) |
email | email, email_verified (true once the customer confirmed their address) |
profile | name, given_name, family_name, locale |
full_profile | <issuer>/<field_name> for every customer custom field, with the value as the REST API serializes it |
Using another Nimbu site as the relying party
A second Nimbu site can log its customers in with the first site's accounts, without code:
- On the providing site, create an OAuth 2 app as above with customer scopes
openid,email,profile, and set the callback URL tohttps://<relying-site-domain>/auth/nimbu/callback. - On the relying site, go to Settings → Push & Integrations and enable the Login with
Nimbu integration:
- Issuer: the providing site's primary domain.
- Client ID / Client secret: from the app in step 1.
- Match email: link to an existing customer with the same email address when the account is not linked yet.
- Autoregister: create a customer when none is found (implies email matching). Skip confirmation lets those customers skip email confirmation.
The relying site requests openid email profile and uses discovery on the issuer. The login button and
callback handling in the theme are covered in Customer login.
Any standards-compliant OIDC client library works the same way: point it at the issuer, let it read the discovery document, and use the authorization code flow with PKCE.