Auth guides
12 · Sign in with Google

Sign in with Google.

About ten minutes, most of it in Google Cloud. The console walks you through it in five steps and tells you straight away whether Google accepts what you pasted, so you find a typo now instead of from a user.


1

Open the guided setup

In the console, open Auth, pick your app, then Settings. Under Federated sign-in providers, select Set up on the Google row.

The page opens on Set up Google sign-in: five numbered steps with the exact values for your app filled in. Keep it open next to Google Cloud. Each step turns green as you finish it:

  1. Create the client at Google
  2. Paste the redirect URI
  3. Scopes
  4. Paste the credentials and save
  5. Try sign-in

You need a Google account that can create OAuth clients in a Google Cloud project (Owner or Editor on the project).


2

Create the OAuth client in Google Cloud

Step 1 links straight to Google Cloud Credentials. Pick or create the project your app belongs to.

2a · The consent screen, once per project

If Google asks you to configure the consent screen first, do that: choose audience External (anyone with a Google account) unless only people in your own Google Workspace should sign in, then fill in the app name, a support email and a developer contact email. This is what your users see when Google asks them to continue to your app.

Under Data access, add the three scopes Auth asks for (step 3 in the console lists them too):

  • openid: proves who the user is.
  • email: their email address and whether Google verified it.
  • profile: their name and picture.

Google does not review apps that ask for these three only. While the app is in Testing, only the test users you list can sign in; select Publish app when you are ready for everyone.

2b · The OAuth client

  1. Select Create credentials, then OAuth client ID.
  2. Application type: Web application. Other types (Android, iOS, Desktop) cannot be used for the website sign-in, and Google rejects their secret.
  3. Under Authorized redirect URIs, add the URL from step 2 in the console. Use the Copy button: Google compares it character by character. It looks like this, with your app's slug:
Authorized redirect URI
https://api.auth.productcraft.co/<your-app-slug>/v1/auth/oauth/google/callback

Leave Authorized JavaScript origins empty: the sign-in runs through Auth, not through JavaScript on your page. Select Create. Google shows the Client ID (ending in .apps.googleusercontent.com) and the Client secret (starting with GOCSPX-).


3

Paste the credentials and save

Step 4 points at the form under the steps. Paste the Client ID and Client secret and select Configure.

When you save, Auth asks Google whether it knows this client: it sends Google's token endpoint a code that cannot work and reads the answer. No one is signed in and nothing is created. The answer shows on step 4 straight away:

  • Accepted: Google knows the client and the secret matches. Go on to Try sign-in.
  • Rejected (invalid_client): Google does not know this Client ID, or the secret belongs to another client. Open the client in Google Cloud and copy both values again. If the error is unauthorized_client, the client is not of type Web application: create one that is. Sign-in with Google stays blocked until this is fixed.
  • Check (redirect URI): Google accepts the credentials but the redirect URI registered there is different from step 2. Paste it again exactly.
  • Could not check: Google could not be reached. Nothing is blocked; select Check again in a minute.

The secret is stored encrypted and never shown again. To change it later, paste a new one in Replace client secret; leave the field empty to keep the one on file.


4

Try sign-in, then switch it on

Step 5 runs the whole round trip in a small window: you sign in at Google and the console shows who would have signed in to your app.

Try sign-in creates no account and issues no token, and it works before the provider is switched on. If something is wrong, the result names the fix. The most common one is redirect_uri_mismatch, with the exact URL to paste into Google Cloud.

When the test passes:

  1. Switch on Enabled at the bottom of the page and save.
  2. On the app's Settings, add the site your users sign in from under Allowed redirect origins (for example https://app.acme.com). Without at least one, Auth refuses every web sign-in and the provider shows Blocked.

The Google row then shows Ready: checked by the server with the same rule sign-in uses.


5

Add the button to your site

A link to Auth, then one server-side call. Tokens never travel through a URL.

Your sign-in page
<a href="https://api.auth.productcraft.co/<your-app-slug>/v1/auth/oauth/google/authorize?return_to=https%3A%2F%2Fapp.acme.com%2Flogin%2Fdone">
  Sign in with Google
</a>

Auth sends the user to Google, Google sends them back to Auth, and Auth sends them to your return_to with a one-time code: https://app.acme.com/login/done?heimdall_code=…. The origin of return_to must be one of your allowed redirect origins. Your backend swaps the code for tokens:

Your backend: code to tokens
const res = await fetch(
  'https://api.auth.productcraft.co/<your-app-slug>/v1/auth/oauth/exchange',
  {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ code }), // the heimdall_code query value
  },
);
if (!res.ok) throw new Error('Exchange failed'); // 401: unknown, expired or used
const { access_token, refresh_token, expires_in } = await res.json();

The code works once, for 60 seconds, for this app only. When sign-in fails, the user comes back with heimdall_error instead of a code:

  • provider_error: the user cancelled at Google or Google refused the request.
  • token_invalid: the ID token from Google failed a check (signature, audience, nonce or hosted domain).
  • link_required or account_exists_with_different_provider: an account with this email already exists and your linking policy does not link it automatically (see the Apple guide, section 5; the policy is the same for every provider).

The access token's amr claim is ["oauth", "google"], and the user.signup / user.signin webhooks carry "provider": "google".


6

Optional settings

Both live on the same Google page in the console.

  • Hosted domain restriction: list Google Workspace domains (for example acme.com) and only accounts in those domains can sign in. Others are refused with token_invalid. Leave it empty to accept any Google account.
  • Native sign-in (iOS and Android): add the client IDs of your native Google clients. Your app gets an ID token from the Google Sign-In SDK (pass the SHA-256 hex of a random nonce to Google) and posts it with the raw nonce to POST /<your-app-slug>/v1/auth/oauth/google as { "id_token": "…", "nonce": "…" }. With no native client IDs, that endpoint refuses every request with a 400.