BuildBaseBuildBase

Add a custom OpenID Connect provider

Add a sign-in button for an identity provider BuildBase has not built, such as Keycloak, Okta or your own, with every field and URL check explained.

A custom provider puts one more sign-in button on your hosted page, for an identity provider BuildBase has not built in: a Keycloak server, Okta, Auth0, or a sign-in service your company runs itself. It works with any provider that speaks OpenID Connect (OIDC, the standard way one site confirms who someone is for another), or plain OAuth 2.0 with a user info address. The person presses the button, signs in on your provider's page, and comes back to your app signed in. You make a client (an application) in the provider, and swap a few values with BuildBase. The screenshots below use a small test provider named Acme ID, run on its own server, and show a real sign-in through it.

Before you start

  • An identity provider where you can register a client: its issuer URL, a client ID and a client secret.
  • One per organization. Every app in it can tick the same custom provider. Once one exists, Custom OIDC provider is no longer offered in Add new method, and adding a second is refused with "This app already has a custom provider. Edit that one instead."
  • A plan that includes the custom OIDC sign-in provider. A plan without it shows the choice locked with "Your plan does not include custom OIDC sign-in provider.", and adding one is refused. While your plan does not name the feature at all, the add goes through. It also counts toward your plan's limit of social sign-in providers.

What goes where

Copy thisFromPaste it into
Issuer URLYour provider, such as https://id.acme.example/realms/acmeBuildBase, Issuer URL
Client IDYour provider, the client you madeBuildBase, Client ID
Client secretYour provider, the same clientBuildBase, Client secret
Redirect URLBuildBase, after you press CreateYour provider, the client's allowed redirect URLs

Okta calls the last one Sign-in redirect URIs, Auth0 Allowed Callback URLs, and Keycloak Valid redirect URIs.

1. Make a client in your provider

These are the steps the console shows under How to connect your identity provider. In Keycloak, as an example:

  1. In your realm, open Clients and choose Create client. Keep OpenID Connect as the type, and give a Client ID, such as acme-analytics.
  2. Turn Client authentication on, so the client has a secret, and keep Standard flow, the one that sends the person to the sign-in page and back.
  3. Leave Valid redirect URIs for now: you paste BuildBase's at the end of the next part.
  4. On the client's Credentials tab, copy the Client secret.
  5. Your issuer URL is your Keycloak address with /realms/ and the realm's name, such as https://id.acme.example/realms/acme. In Okta it is your Okta address, and in Auth0 your Auth0 domain with https:// in front.

2. Add the provider in the BuildBase console

  1. In the console, open User management, then Authentication under Access, and the Methods tab.
  2. Press Add auth method and choose Custom OIDC provider.

The Add new method dialog with Custom OIDC provider chosen: what it is for, that it allows one per app, and the open guide How to connect your identity provider with its five steps

  1. Fill in the form. Each field:
FieldWhat to give it
TitleYour own name for the method, shown in the console only. Here Acme ID.
Button labelRequired. What the button on the sign-in page says, such as Acme ID.
Issuer URLFor a provider with OIDC discovery. Paste it without /.well-known/openid-configuration: BuildBase reads every other address from that document.
Client ID, Client secretRequired. From the client you made.
Authorization URLOnly for a provider without discovery: the page where people type their password. Give all three addresses and leave Issuer URL empty.
Token URLOnly without discovery: where BuildBase collects the sign-in, behind the scenes.
User info URLOnly without discovery: where BuildBase reads the person's email address and name.
Icon URLA square image over https://. Without one, or if it fails to load, the button shows a key.
Trust its email addressesNever (the default), When it sends email_verified: true, or Always. See Trusting its email addresses.
Scopesopenid profile email by default. Keep them: a new account is made with the email address.

The custom provider form: Title Acme ID, Button label Acme ID, Issuer URL https://id.acme.example/realms/acme, Client ID acme-analytics, Client secret as dots, the three endpoint boxes and Icon URL left empty, Trust its email addresses on Never, and the scopes openid profile email

  1. Press Create. BuildBase reads the provider's discovery document before it saves, so a mistyped issuer is refused now rather than at someone's sign-in:

A message in the console: Could not add Custom OIDC provider. https://id.acme.example/realms/acme/.well-known/openid-configuration does not resolve to a public address BuildBase can reach.

  1. With a working issuer, the dialog says One step left and shows the Redirect URL. Press Copy, then Done.

After Create: One step left, and the Redirect URL box with the address to add to the allowed redirect URLs of the application in your identity provider

  1. In your provider, paste the URL into the client's allowed redirect URLs (Keycloak: Valid redirect URIs) and save.

The redirect URL has this shape:

https://api.console.buildbase.app/api/v1/auth/social/custom/<orgId>/<methodId>/callback

It starts with your BuildBase server, https://api.console.buildbase.app{/* @endstat */} on BuildBase's cloud.

The URL checks

  • Discovery or three addresses. Saving with neither an issuer nor all three addresses is refused with "Give an issuer URL, or all three endpoints."
  • Every address BuildBase calls must be public. The issuer, and the token, JWKS (signing keys) and user info addresses, including the ones the discovery document names, must resolve to a public address. One that does not is refused with "... does not resolve to a public address BuildBase can reach." A provider on a private network has to be reachable at a public address first. A self-hosted BuildBase may use addresses on its own network; cloud metadata and link-local addresses are refused everywhere.
  • The authorization URL is not checked. BuildBase never calls it; it only sends the person's browser there.
  • The discovery document must name the same issuer it was fetched for, and list authorization, token and JWKS addresses. Otherwise the save is refused with "The discovery document names a different issuer" or "The discovery document has no authorization, token or JWKS endpoint."
  • https:// only on a BuildBase server running in production: a plain http:// address is refused with, for example, "Issuer URL must be an https:// URL." Icon URL is always https://, everywhere: "Icon URL must be an https:// URL."

3. Attach it to your app

  1. Open the Settings tab and press Edit on Acme Analytics.
  2. Under Methods, tick the provider, listed by its Title, here Acme ID, and press Update.

The Edit auth setting sheet for Acme Analytics, at Methods: Acme ID ticked beside Email and password

4. Check the preview

The Preview on the Sign-in page tab shows your look with sample GitHub and Google buttons, not your methods. Press Open the live page for Acme Analytics above it to see the real page with your button.

5. What people see

The hosted sign-in and register pages now have a button with your Button label. With no Icon URL, it shows a key.

The hosted sign-in page Sign in to Acme with an Acme ID button, showing a key icon because no Icon URL is set

On a phone it runs the full width under the form:

The sign-in page on a phone 390 pixels wide, with the Acme ID button across the full width under the form

Here is a whole first sign-in, for Riley Chen, who has no account yet:

  1. Riley presses Acme ID. Their browser goes to the provider's own page, which asks them to sign in, and the first time to share their name and email address with your app.

The identity provider page, here a test provider named Acme ID: signed in as Riley Chen, riley.chen@acme.example, asking to share name and email with Acme Analytics, with Allow and Cancel

  1. The provider sends them back, and BuildBase makes their account with the name and address the provider sent. Trust is still Never, so BuildBase has not taken the address on the provider's word: the sign-in page asks Riley to type a 6-digit code sent to it. With When it sends email_verified: true and a provider that says so, this step is skipped.

Back on the hosted page: Verify your email, with a box for the 6-digit code sent to riley.chen@acme.example

  1. After the usual Trust this device? question (and the passkey offer, if your app offers passkeys), Riley is in your app.

The app page Acme Analytics after the round trip: Signed in as Riley Chen, riley.chen@acme.example

Riley now appears under Users in the console, with Sam Rivera from the next section:

The Users list in the console with Riley Chen, who signed up through Acme ID, and Sam Rivera, who already had an account

Trusting its email addresses

Trust decides whether this provider may sign someone into an account that already has the same email address. BuildBase knows nothing about a provider you added, so it starts at Never.

SettingWhat it means
Never (default)It cannot sign into an account that already exists with the same address. It can create new accounts, which confirm their address with an emailed code, and it finds them again by the provider's own user ID.
When it sends email_verified: trueIt can sign into an existing account when, on that sign-in, it says the address is verified. Keycloak, Okta and Auth0 send email_verified.
AlwaysEvery address it sends is treated as verified.

Trust a provider only if it checks that people own the addresses it sends. Trusting one where anyone can type in any address lets anyone sign into anyone's account.

With Never, Sam Rivera, who already has an account at [email protected], is turned away when he first tries the button:

The sign-in page back from Acme ID with the message This email is already registered. Sign in the way you did before, then connect this provider from your account's security settings.

Sam can sign in as before and connect Acme ID from the Security screen of your app. Or, for a provider that checks addresses, change the trust: press the edit button on the provider's row in Methods, choose When it sends email_verified: true, and press Save.

Editing Acme ID: Trust its email addresses, with When it sends email_verified: true chosen instead of Never or Always

Sam's next press of the button signs him into his own account and connects Acme ID to it, so later sign-ins find him by the provider's user ID:

The app page after Sam Rivera signs in with Acme ID once trust allows it: Signed in as Sam Rivera, sam.rivera@acme.example, his existing account

What BuildBase reads from your provider

With discovery, BuildBase checks the ID token: its signature against the provider's JWKS (RS256, ES256 or PS256), the issuer, the audience (your client ID) and the sign-in's nonce. The request carries a PKCE challenge, and the client signs in at the token address with HTTP Basic, unless the provider supports only client_secret_post. Without discovery, the profile comes from the user info address, and the client secret goes in the form.

FieldUsed for
sub, or id from a plain OAuth 2.0 user infoThe provider's user ID, which finds the account on every later sign-in
emailThe account's address. Without one, no account can be made
email_verifiedWhether the address is proven, when trust is When it sends email_verified: true
name, else given_name and family_name, else preferred_usernameThe name on a new account

Edit, do not replace. People who signed in with the provider are linked to it. A new custom provider starts with no links, so after deleting and adding one again, those people are found only by email, which needs the trust above. Change the issuer, keys or label with the edit button instead.

In your app

Nothing changes in your code. People connect and remove the provider on the SDK's Security screen, listed under its button label: see Account security screens.

import { useSaaSAuth } from '@buildbase/sdk/react';

export function SecurityButton() {
  const { openWorkspaceSettings } = useSaaSAuth();
  return (
    <button onClick={() => openWorkspaceSettings('security')}>Security</button>
  );
}

Check it works

  • Saving the provider succeeded, so BuildBase could read its discovery document.
  • The redirect URL is in the client's allowed redirect URLs at the provider.
  • The provider is ticked on your app, and Open the live page shows your button.
  • A new person can sign in and lands in your app; they appear under Users.
  • Trust its email addresses is what you decided, not left at Never by accident if your provider verifies addresses.

Things to watch out for

  • ID tokens must be signed with RS256, ES256 or PS256. A client set to sign with a shared secret (HS256) fails every sign-in with "Sign-in with that provider failed." In Auth0, keep the application's JSON Web Token signature algorithm on RS256.
  • The issuer must match the provider's own. Copy the issuer value from the discovery document. In Keycloak it ends with /realms/ and the realm's name. A slash at the end is ignored, anything else is not.
  • Trusting addresses can hand over accounts. Always and When it sends email_verified: true let the provider sign into any existing account with the same address. Choose them only for a provider that checks people own the addresses it sends.
  • Deleting the provider unlinks everyone. People are linked to this one provider. A new one starts with no links, so edit the existing provider to change its issuer, keys or label.
  • Changes at your provider can take an hour to reach BuildBase. BuildBase keeps the discovery document it read for up to an hour.
  • Finish at your provider within 10 minutes. A sign-in left open on your provider's page for more than 10 minutes cannot finish: BuildBase sends the browser to your sign-in page, with a note that the sign-in expired. Start again from your app's Sign in button.

If something goes wrong

What you seeWhyWhat to do
In the console: Give an issuer URL, or all three endpoints.Neither an issuer nor the three addressesGive the issuer, or the authorization, token and user info URLs.
In the console: ... does not resolve to a public address BuildBase can reach.The address is mistyped, or the provider is on a private network (url_not_public)Check the address; make the provider reachable at a public address.
In the console: BuildBase could not read ... /.well-known/openid-configuration. Check the issuer URL.The discovery document is not at that address (discovery_failed)Paste the issuer exactly as the provider names it, without the /.well-known part.
In the console: The discovery document names a different issuer (...).The issuer you typed differs from the one the provider states, often by a trailing partCopy the issuer value from the discovery document.
In the console: This app already has a custom provider. Edit that one instead.There is one already (custom_provider_exists)Edit the existing one.
In the console: Your plan does not include custom OIDC sign-in provider.Your plan withholds the featureUpgrade.
The provider's page says the redirect URI is not validThe redirect URL is not in the client's allowed listPaste it from the console.
Sign-in with that provider failed. Please try again.BuildBase could not finish: a wrong client secret, an ID token it could not verify, or the provider unreachable (provider_failed)Check the secret and the client's settings at the provider.
Sign-in with that provider did not finish. Please try again.The person cancelled at the provider, or it answered with an error (provider_error)Try again.
This email is already registered. Sign in the way you did before, then connect this provider from your account's security settings.An account has this address and trust does not allow the provider to sign into it (email_unverified)The person connects the provider from the Security screen, or you change the trust.
This provider did not share an email address, so an account cannot be created. Allow access to your email address and try again.The provider sent no email (email_missing)Keep the email scope, and check the provider releases the address to this client.
Sign-up is invite-only here, and there is no account for that address yet. Ask the team for an invitation, or sign in with an account you already have.Public sign-up is off (signup_disabled)Invite the person, or open sign-up. See who can sign up.
This app is not taking new sign-ups right now.Your plan's end-user limit is reached (signup_unavailable)Upgrade, or remove users you no longer need.

Next steps