BuildBaseBuildBase

Social Providers

Set up sign-in with Google, LinkedIn, GitHub, Microsoft, Apple, or an OpenID Connect provider of your own.

A social provider puts a button on your hosted sign-in and register pages. You create an OAuth app at the provider, paste its credentials into the console, and give the provider the callback URL the console shows you. Nothing changes in your app's code.

Step by step, with screenshots: Google, GitHub, Microsoft, LinkedIn, Apple, a custom OpenID Connect provider and Google One Tap.

Adding a provider

  1. Create an OAuth app at the provider. Each provider's section below says where.
  2. In the console, add the provider under User Management → Access → Authentication → Methods and fill in the fields it asks for.
  3. Save. The console then shows the callback URL to register with the provider (for Apple, a domain as well). Add it to the app you created in step 1.
  4. Tick the provider on your client in the Settings tab, as with any other method.

Every callback URL has this shape:

https://<your BuildBase server>/api/v1/auth/social/<slug>/<orgId>/<methodId>/callback

Copy it from the console rather than building it by hand. Providers check it exactly, so a trailing slash or a different host fails the sign-in. A method's callback URL never changes once it is set up, so a Google, LinkedIn, GitHub or Microsoft method you added earlier needs nothing new at the provider.

  • Secrets are write-only. Client secrets and Apple's private key are stored encrypted and never shown again. The console shows a saved client secret as its first and last five characters, and a private key only as saved. Leave the field as it is to keep the stored value, or paste a new one to replace it.
  • Scopes are editable on every provider. Each starts with the defaults listed below, and a provider saved with no scopes uses those. Keep the scopes that return the person's email address: a new account is created with it.
  • Every provider counts toward your plan's social provider limit, Apple and the custom provider included.

Google

Create the app atThe console asks forDefault scopes
Google Cloud console, CredentialsClient ID, client secrethttps://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile, openid

Create an OAuth client ID of type Web application, and add the callback URL under Authorized redirect URIs.

Google One Tap

One Tap lets someone who is already signed in to Google in their browser sign in to your app with one click, without leaving your sign-in page. It is off until you turn it on.

  1. In the console, edit your Google method and set Google One Tap to On.
  2. In Google Cloud, open the same OAuth client and add your sign-in page's origin under Authorized JavaScript origins. The console shows it beside the callback URL on the Google method. With a verified custom auth domain it is https:// and that domain; on the shared domain it is your sign-in page's address up to /v1/login. It is not your BuildBase server's URL. If you verify a custom auth domain later, add that origin too.

The hosted sign-in and register pages then offer One Tap as well as the Google button, which stays where it was. Nothing changes in your app's code.

  • It is Google's prompt, not ours. Google draws it, and only for someone signed in to Google in that browser. For anyone else nothing appears, and the Google button works as it always has. Someone who closes the prompt is not shown it again straight away; Google decides when.
  • FedCM. The page asks for the browser's own sign-in prompt (FedCM) where the browser has it, so in Chrome and other browsers with FedCM the prompt is the browser's account chooser and does not depend on third-party cookies. Where a browser has neither FedCM nor third-party cookies, Google may not show the prompt at all.
  • HTTPS only. Google shows One Tap on HTTPS pages, and on localhost while you test.
  • The same sign-in as the button. A One Tap sign-in finds the account the way the Google button does (see How sign-in finds an account) and carries on exactly as a Google sign-in does: a second step if your two-step sign-in policy asks social sign-ins for one, then the "Trust this device?" screen and the passkey offer.

Each prompt carries a single-use nonce that lasts 10 minutes, which ties Google's answer to that page. When a One Tap sign-in is refused, the sign-in page says why above its form, and the Google button is still there:

reasonMeaning
invalid_credentialGoogle's answer could not be verified for your client ID.
nonce_invalidThe prompt was used already, or it was shown more than 10 minutes ago. Reloading the page starts again.
blocked, email_unverified, signup_disabled, signup_unavailableThe same refusals as any social sign-in, listed under When a sign-in is refused.

LinkedIn

Create the app atThe console asks forDefault scopes
LinkedIn developer appsClient ID, client secretopenid, profile, email

Add the Sign In with LinkedIn using OpenID Connect product to the app, which grants those scopes, and add the callback URL under Authorized redirect URLs for your app on the Auth tab.

GitHub

Create the app atThe console asks forDefault scopes
GitHub developer settingsClient ID, client secretuser:email, read:user

Create an OAuth App and put the callback URL in Authorization callback URL.

Microsoft

Create the app atThe console asks forDefault scopes
Azure portal, App registrationsClient ID, client secret, tenantopenid, profile, email, User.Read

Register an application with the callback URL as a Web redirect URI, then add a client secret under Certificates & secrets.

Tenant decides whose accounts can sign in. The default, common, accepts work, school and personal Microsoft accounts. A directory ID or domain accepts accounts from that directory only.

Sign in with Apple

Create the app atThe console asks forDefault scopes
Apple developer, IdentifiersServices ID, Team ID, Key ID, private key (.p8)name, email

This needs an Apple Developer Program membership. Then, under Certificates, Identifiers & Profiles:

  1. Register an App ID with Sign in with Apple enabled, if you do not have one already.
  2. Register a Services ID. Its identifier, such as com.example.signin, is the Services ID the console asks for. It is the client ID for the web, and it is not the App ID.
  3. Under Keys, create a key with Sign in with Apple enabled, linked to that App ID. Download the .p8 file, which Apple lets you do once, and note the key's Key ID.
  4. Your Team ID is under Membership details in your developer account.
  5. Add Apple in the console with those four values and save. Then open the Services ID, enable Sign in with Apple, and under Configure pick the App ID and add the domain and the return URL the console shows.

Paste the whole .p8 file into Private key, from -----BEGIN PRIVATE KEY----- to the end. There is no client secret field: Apple's client secret is a token signed with your key, and BuildBase signs it for you.

It needs a public HTTPS callback. Apple accepts only HTTPS return URLs on a public domain, never localhost or an IP address, so Sign in with Apple cannot be tested on a server running on your laptop. Test it on a deployed server, such as a staging one. Apple also returns to the callback with a form POST rather than a redirect, so anything in front of a self-hosted server must let POST requests through to that path.

The name arrives once. Apple sends the person's name only the first time they authorize your Services ID, and BuildBase saves it then. Later sign-ins carry an ID and an email address, and no name. To see the first authorization again while testing, remove your app under Sign in with Apple in the settings of the Apple Account you test with.

Hidden email addresses. A person can choose Hide My Email, and your app then gets an address at privaterelay.appleid.com that forwards to their real inbox. Two things follow:

  • Apple forwards mail to that address only from senders you register with it. Under Certificates, Identifiers & Profiles → Services → Sign in with Apple for Email Communication, register the domain or address your app's email is sent from. Until you do, magic links, sign-in codes and every other email to those people bounce.
  • The relay address matches no account the person made another way, so their first Apple sign-in does not find an account they already have. Where sign-up is open, it makes a new one.

A custom OpenID Connect or OAuth 2.0 provider

For an identity provider BuildBase has not built, such as Keycloak or one of your own, add a custom provider. It is one button on your sign-in page, for a provider that speaks OpenID Connect, or OAuth 2.0 with a user info endpoint.

  • One per app. A second one is refused with "This app already has a custom provider. Edit that one instead."
  • A plan feature, checked when you add it. The custom provider is the auth-custom-oidc plan feature. A plan that switches it off refuses the add with a 402. The check fails open: while your plan does not name the feature at all, or BuildBase cannot read your plan, the add goes through. Sign-in never checks the plan, so a provider already added keeps working.
FieldWhat to give it
Button labelRequired. What the button on your sign-in page says, such as "Acme ID"
Issuer URLFor an OpenID Connect provider. BuildBase reads its endpoints from the issuer's /.well-known/openid-configuration
Client ID, client secretRequired. From the client you register with the provider
Authorization URLOnly for a provider without discovery, with the next two
Token URLOnly for a provider without discovery
User info URLOnly for a provider without discovery
Icon URLA square image over HTTPS. Without one, or if it fails to load, the button shows a generic key
Trust its email addressesnever by default. See below

The default scopes are openid, profile and email.

Discovery or three endpoints. Give the issuer URL, and BuildBase reads the rest from the provider's discovery document. For a provider without discovery, such as a plain OAuth 2.0 server, leave the issuer empty and give the authorization, token and user info URLs instead. Saving with neither is refused with "Give an issuer URL, or all three endpoints." If the discovery document cannot be read, the save is refused with discovery_failed.

Every URL BuildBase calls must be public. BuildBase refuses an issuer, token, JWKS or user info URL that resolves to a private or internal address, including the ones a discovery document names, with url_not_public. The authorization URL is not checked: BuildBase never calls it, only sends the person's browser there. A provider on a private network has to be reachable at a public address before it can be used. A self-hosted install may use addresses on its own private network; cloud metadata and link-local addresses are refused everywhere.

What BuildBase reads from it. With discovery, the id_token is checked first: 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 authorization request carries a PKCE challenge, and the client authenticates at the token endpoint with HTTP Basic unless the provider supports only client_secret_post. Without discovery, the profile comes from the user info URL, with the access token, and the client secret goes in the form.

From the id_token or user info, BuildBase reads:

FieldUsed for
sub, or id for a plain OAuth 2.0 providerThe provider's own user ID, which finds the account on every later sign-in
emailThe account's address. A provider that sends none cannot create an account
email_verifiedWhether the address is proven, when trust is set to "When it sends email_verified"
name, else given_name and family_name, else preferred_usernameThe name on a new account

When the id_token carries no email, user info is asked as well, and it must name the same sub. A provider that uses other field names for these cannot be used yet.

Email trust decides whether this provider can sign someone into an account that already exists with the same email address:

SettingWhat it means
Never (default)It cannot sign into an account that already exists with the same email. It can create new accounts, and once someone has signed in with it, it finds that account again by the provider's own user ID.
When it sends email_verifiedIt can sign into an existing account when it says, on that sign-in, that the address is verified (email_verified: true).
AlwaysEvery address it sends is treated as verified.

Trust a provider only if it checks that people own the addresses it sends. Trusting one that lets anyone type in any address lets anyone sign into anyone's account. never is the default for that reason: BuildBase knows nothing about a provider you added yourself.

Edit the custom provider rather than deleting it and adding a new one. People who signed in with it are linked to that provider, and a new one starts with no links, so those people are found again only by email, which needs the trust above.

How sign-in finds an account

When someone comes back from a provider, BuildBase looks for their account in this order:

  1. By the provider's own user ID. Anyone who has signed in with this provider before is found this way, whatever their email address is now, here or at the provider.
  2. By a verified email address. If no account has that ID linked yet, BuildBase looks for an account with the same email, but only when the provider vouches for the address: a built-in provider when it says the address is verified, and a custom provider as its email trust setting says. A match links the provider to the account, so the next sign-in is found by step 1.
  3. A new account, when neither finds one, if sign-up is open or the sign-in started from an invitation, and your plan has room for another user.

Because step 1 comes first, a person who signs in with a social provider can change their email address in your app and still reach the same account next time.

When a sign-in is refused

A sign-in that fails after the provider sends the person back returns to the page it started from, with error and reason added to the URL. The hosted sign-in page shows the message, with the form under it so the person can try again.

reasonMeaning
provider_errorThe provider answered with an error, or the person cancelled there.
provider_failedThe provider sent the person back, but BuildBase could not finish with it: exchanging the code or reading the profile failed.
email_unverifiedAn account with this email already exists, and this provider cannot sign into it, because it does not vouch for the address.
email_missingThe provider shared no email address, so there is no account to find and none can be created.
blockedThe account is blocked.
signup_disabledThere is no account for this address and sign-up is invite-only. See Closing public sign-up.
signup_unavailableThere is no account for this address and the app cannot take new users right now.
signup_restrictedThere is no account for this address, and it is blocked or not on the allowlist. See Who can sign up.
waitlist_requiredThere is no account for this address, sign-up is by waitlist, and the address has no approved entry.

A sign-in that waited too long is the exception. Each one is good for 10 minutes and answers one callback, so a provider that sends the person back later, or a second time, finds nothing left to finish, nor the page it started from. BuildBase then sends the browser to your hosted sign-in page, /v1/login?org=<orgId>&reason=session_expired, on your sign-in domain (your verified custom domain, or BuildBase's), and that page says the sign-in expired, so the person starts again from your app. Nothing about the expired attempt is in the address. A callback carrying a sign-in that is still live but belongs to another org, method or provider is refused with a plain 401: that is a tampered link or a provider app shared between methods, not a slow sign-in.

When provider_failed keeps happening, check the client secret first: a mistyped secret, or one that has expired at the provider (Microsoft's do), fails the code exchange. For Apple, check the key, Key ID and Team ID.

Next steps

  • Overview - every sign-in method and how they fit together.
  • Sign In & Sign Out - useSaaSAuth() return values and usage patterns.
  • Bot Protection - Turnstile on the hosted sign-up and sign-in forms.
  • Connected Accounts - let people connect more providers to their account, and remove one.
  • Importing Users - bring users over from another provider with their social sign-ins.