Nimbu Developer Docs
Other

Customer Login with External Providers

Let customers sign in with Microsoft, Facebook, SAML, another Nimbu site or JWT SSO, and build the OAuth2 consent screen when your site is the identity provider

Customers can sign in to your site with an external identity provider instead of (or next to) a password. The provider is configured once in the Nimbu admin; the theme only renders the buttons.

Supported providers

Providerprovider: valueConfigured with
Microsoft Entra IDmicrosoftTenant, client ID
FacebookfacebookApp ID, app secret
Another Nimbu site (OpenID Connect)nimbuIssuer, client ID, client secret
SAML 2.0 identity providersamlSP entity ID, IdP SSO URL, IdP certificate
TwittertwitterAPI key, API secret

Google, GitHub and LinkedIn are not built-in providers. To use them, put an OpenID Connect or SAML broker in front of them, or sign customers in from your own backend with JWT single sign-on.

Add a provider under Settings → Push & Integrations in the Nimbu admin. The callback (redirect) URL to register at the provider is always:

https://<your primary domain>/auth/<provider>/callback

Login buttons

{% login_with %}

Renders a small form that starts the login flow:

{% login_with provider: 'microsoft' %}

{% login_with provider: 'facebook', text: 'Continue with Facebook', class: 'social-login', button_class: 'btn btn-facebook' %}

{% login_with provider: 'saml', text: 'Company login', return_to: '/account/orders' %}

Options (all optional, all literal strings):

OptionDescription
providerOne of facebook, twitter, nimbu, microsoft, saml. Defaults to nimbu.
actionlogin (default), link or unlink.
textButton label. Defaults to the translation login_with.login / login_with.link / login_with.unlink ("Login with microsoft", ...).
classCSS class on the <form>.
button_classCSS class on the <button>.
return_toPath to send the customer to after a successful login.

The provider must be passed as the provider: keyword. A positional value such as {% login_with 'facebook' %} is ignored and the tag falls back to nimbu. Option values are not evaluated as Liquid, so return_to: page.url sends the literal text page.url; use a quoted path.

The tag outputs a POST form to /auth/<provider> with the CSRF token included. For {% login_with provider: 'microsoft', class: 'social-login', button_class: 'btn' %}:

<form id="login-with-microsoft-form-login" action=/auth/microsoft method="POST" class="social-login">
  <input name="authenticity_token" type="hidden" value="..."/>
  <button id="login-with-microsoft-form-login-button" type="submit" name="login" value="login" class="btn">Login with microsoft</button>
</form>

The login flow only accepts POST requests with a valid CSRF token. A plain link to /auth/microsoft does not work. If you build the form yourself (the url.login_with_<provider> helpers return the same /auth/<provider> path), include {{ auth_token }} as the authenticity_token field.

Linking and unlinking an account

A logged-in customer can connect a provider to their existing account, and disconnect it again:

{% login_with provider: 'microsoft', action: 'link', text: 'Connect Microsoft' %}

{% unlink_from provider: 'microsoft', text: 'Disconnect Microsoft' %}

{% unlink_from %} takes the same options as {% login_with %} and always uses the unlink action. It posts to /auth/<provider>/unlink. After linking or unlinking, the customer is redirected to /account with a flash message.

Checking linked providers

customer.identities exposes one entry per provider. Each linked identity has provider and unlink_path; Facebook and Twitter identities also have image, Nimbu identities locale.

<h2>Connected accounts</h2>

<div class="connection">
  <strong>Microsoft</strong>
  {% if customer.identities.microsoft %}
    <span class="status connected">Connected</span>
    {% unlink_from provider: 'microsoft', text: 'Disconnect', button_class: 'btn btn-sm btn-danger' %}
  {% else %}
    <span class="status">Not connected</span>
    {% login_with provider: 'microsoft', action: 'link', text: 'Connect', button_class: 'btn btn-sm' %}
  {% endif %}
</div>

<div class="connection">
  <strong>Facebook</strong>
  {% assign facebook = customer.identities.facebook %}
  {% if facebook %}
    <img src="{{ facebook.image }}" alt="" width="32" height="32">
    {% unlink_from provider: 'facebook', text: 'Disconnect', button_class: 'btn btn-sm btn-danger' %}
  {% else %}
    {% login_with provider: 'facebook', action: 'link', text: 'Connect', button_class: 'btn btn-sm' %}
  {% endif %}
</div>

customer.identities.any? tells you whether any provider is linked.

What happens after the provider sends the customer back

  1. A customer is logged in: the identity is linked to that customer.
  2. A customer is already linked to this provider account: they are logged in. Their first name, last name and email are updated from the provider when the provider sends them.
  3. Match on email is on and a customer with the same email exists (and has no identity for this provider yet): the identity is linked to that customer and they are logged in.
  4. Automatic registration is on: a new customer is created with a random password, the identity is linked, and the customer is logged in. This needs an email, first name and last name from the provider; if one is missing, the customer continues with step 5. Without skip confirmation, the customer first has to confirm their email address.
  5. Otherwise: the customer is sent to /register. The registration form is pre-filled with the name and email from the provider, and the identity is linked when they complete the form.

If the provider reports an error, the customer lands on /login (or /account when they were linking) with a flash error.

Provider flags

Facebook, Microsoft, Nimbu and SAML integrations share three flags:

FlagAdmin labelEffect
match_emailAlso match on e-mailStep 3 above.
autoregisterAutomatically register new customersStep 4 above. Turning it on also turns on match_email.
skip_confirmationSkip confirmation for automatically registered customersNew customers from step 4 can log in without confirming their email.

Provider setup

Microsoft Entra ID

Microsoft login uses OpenID Connect against the Microsoft identity platform v2.0 endpoint. It requests an ID token (response_type=id_token, response_mode=form_post) with the scope openid profile. No client secret is involved.

  1. In the Azure portal, open Microsoft Entra ID → App registrations → New registration.
  2. Add a Web redirect URI: https://<your primary domain>/auth/microsoft/callback.
  3. Under Authentication, enable ID tokens.
  4. Copy the Application (client) ID and the Directory (tenant) ID.
  5. In Nimbu, add the Microsoft integration with:
    • Tenant: the Directory (tenant) ID or a verified domain of your tenant. common or organizations accept users from any Entra tenant.
    • Client ID: the Application (client) ID.

The customer's email address is the token's email claim, falling back to preferred_username. On a multi-tenant setup (common, organizations), only preferred_username is used: the email claim is whatever the other tenant's admin typed in and is not verified. The email claim is only present when you add it as an optional claim in the app registration.

Nimbu also stores the Entra object ID and tenant ID of each linked customer. When you switch to a different app registration, customers are re-linked on their next login even though Microsoft issues a new user ID per app registration.

Facebook

Create an app in the Facebook developers dashboard, add Facebook Login with the redirect URI https://<your primary domain>/auth/facebook/callback, and enter the App ID and App Secret in the Nimbu integration.

Another Nimbu site (OpenID Connect)

A Nimbu site with OpenID Connect enabled can act as identity provider for another site. On the provider site, create an OAuth client under Settings → Apps with the redirect URI https://<your primary domain>/auth/nimbu/callback. On the site that shows the login button, add the Nimbu integration with:

  • Issuer: the primary domain of the provider site.
  • Client ID and Client secret: from the client you just created.

Nimbu discovers the endpoints from the issuer's /.well-known/openid-configuration and requests the scopes openid email profile. See OpenID Connect for the provider side.

SAML

Enter the SP entity ID (the name of your application), the identity provider's SSO service URL and its certificate (PEM). Optionally add your own certificate and private key to sign authentication requests or metadata, and choose the digest and signature methods your IdP expects. The name identifier format defaults to urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress.

Nimbu reads the email, first name, last name and name from common attribute names (for example mail/email, givenname/firstName, surname/lastName).

With restricted on, a Liquid-style condition on the SAML attributes decides who may log in or register, for example department == 'sales' or role == 'admin'. Customers who fail the check get the login error. The debug flag logs the received attributes to help you write the expression.

Twitter

Enter the API key and API secret of your Twitter app. Twitter does not share an email address, so customers either link Twitter to an existing account or complete the registration form.

JWT single sign-on

If customers already log in on your own platform, you can sign them in on the Nimbu site with a short-lived token instead of a provider button. Add the JWT SSO integration to get a shared secret, then send customers to any page of the site with a jwt query parameter:

https://<your primary domain>/account?jwt=<token>

The token is an HS256 JWT signed with the shared secret. It must contain email, firstname, lastname, iat and jti, is valid for 2 minutes and can be used once. Nimbu logs in the customer with that email, creating the account (already confirmed) if needed. Other claims are applied to the customer as regular customer attributes; password is ignored.

With SSO enabled, /login redirects to your remote login URL (with a return_to parameter) and logout redirects to your remote logout URL, so customers never see the Nimbu login form.

When your site is an OpenID Connect provider for other applications (see OpenID Connect), a logged-in customer who starts an authorization at /oauth2/authorize sees templates/customers/oauth2_consent.liquid. Trusted clients and clients the customer already approved (for the same scopes) skip the screen; public clients always show it.

Variables in the consent template:

VariableDescription
applicationName of the requesting client.
clientname, hostname, type_label, unverified, redirect_host, ...
requested_scopesList of { scope, description }. Descriptions come from the translation oauth2.consent.scope.<scope>.
scope_groupsThe same scopes grouped for display: { heading, key, scopes }.
sitesSites the grant can be bound to (for a customer: the current site).
page.title"Authorize ..." from the translation oauth2.consent.page_title.

Renders the form that approves or denies the request. It posts to /oauth2/authorize with the CSRF token and the original request parameters (client_id, response_type, redirect_uri, scope, state, nonce, code_challenge, code_challenge_method) as hidden fields, followed by an Approve and a Deny button.

OptionDescription
classCSS class on the <form>.
button_classCSS class on both buttons.
picker_classCSS class on the site <select>, only rendered when more than one site is offered.
<div class="consent">
  <h1>{{ page.title }}</h1>
  <p><strong>{{ application }}</strong> wants to access your account on {{ site.name }}.</p>

  <ul>
    {% for scope in requested_scopes %}
      <li>{{ scope.description }}</li>
    {% endfor %}
  </ul>

  {% oauth2_consent_form class: 'consent-form', button_class: 'btn' %}
</div>

On this page