Sign in with GitHub.
Five minutes. Register an OAuth app at GitHub, paste one URL there and two values back, and the console tells you on save whether GitHub accepts them. Web only: GitHub has no native ID token.
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 GitHub row.
The page opens on Set up GitHub sign-in: five numbered steps with your app's values filled in. Each step turns green as you finish it: create the client at GitHub, paste the redirect URI, scopes, paste the credentials and save, Try sign-in.
2
Register an OAuth app at GitHub
Step 1 links to New OAuth app under your personal Developer settings. For an app owned by your company, register it under the organization instead: Organization settings, Developer settings, OAuth Apps.
Choose an OAuth app, not a GitHub App. GitHub Apps are for acting on repositories; OAuth apps are for signing people in.
- Application name and Homepage URL: your product. GitHub shows both on the consent screen.
- Authorization callback URL: the URL from step 2 in the console. Use the Copy button. It looks like this, with your app's slug:
https://api.auth.productcraft.co/<your-app-slug>/v1/auth/oauth/github/callbackLeave Enable Device Flow off and select Register application. On the next page, copy the Client ID, then select Generate a new client secret. GitHub shows the secret once: copy it now.
Scopes
Nothing to set at GitHub. OAuth apps ask for scopes when the user signs in, and Auth asks for two:
read:user: their GitHub profile (a stable user id, login, name and avatar).user:email: their email addresses, so Auth can use the primary verified one even when the user keeps it private on their profile.
3
Paste the credentials and save
Step 4 points at the form under the steps. Paste the Client ID and the client secret and select Configure.
When you save, Auth asks GitHub whether it knows this client: it sends GitHub's token endpoint a code that cannot work and reads the answer. No one is signed in. The answer shows on step 4:
- Accepted: GitHub knows the client and the secret matches. Go on to Try sign-in.
- Rejected (
incorrect_client_credentials): GitHub does not recognise this Client ID and secret pair. The usual causes are a secret that was deleted or regenerated at GitHub, or a Client ID from a different OAuth app. Open the app at GitHub, copy the Client ID, generate a new secret and paste both. If GitHub has never heard of the Client ID at all (a typo, or the ID of a GitHub App), the error isunknown_client. Sign-in with GitHub stays blocked until this is fixed. - Could not check: GitHub could not be reached. Nothing is blocked; select Check again in a minute.
The secret is stored encrypted and never shown again. To rotate it, generate a new secret at GitHub, paste it into Replace client secret and save, then delete the old secret at GitHub.
4
Try sign-in, then switch it on
Step 5 runs the whole round trip in a small window: you authorize at GitHub and the console shows who would have signed in.
Try sign-in creates no account and issues no token, and works before the provider is switched on. A wrong callback URL shows as redirect_uri_mismatch with the exact URL to paste at GitHub.
When the test passes:
- Switch on Enabled at the bottom of the page and save.
- On the app's Settings, add the site your users sign in from under Allowed redirect origins. Without at least one, Auth refuses every web sign-in and the provider shows Blocked.
5
Add the button to your site
Same two pieces as every provider: a link to Auth and one server-side call.
<a href="https://api.auth.productcraft.co/<your-app-slug>/v1/auth/oauth/github/authorize?return_to=https%3A%2F%2Fapp.acme.com%2Flogin%2Fdone">
Sign in with GitHub
</a>The user comes back to your return_to with ?heimdall_code=… (the origin must be one of your allowed redirect origins). Your backend swaps it for 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. On failure the user comes back with heimdall_error: provider_error when they cancel at GitHub, token_invalid when the user's primary verified email is outside your allowed domains, and link_required / account_exists_with_different_provider when an account with that email already exists and your linking policy does not link it.
The access token's amr claim is ["oauth", "github"], and the user.signup / user.signin webhooks carry "provider": "github".
6
Optional: allowed email domains
On the same GitHub page in the console.
List domains (for example acme.com) and only users whose primary verified GitHub email is in one of them can sign in. Leave it empty to accept any GitHub user. A user with no verified email on GitHub signs in without an email address.
GitHub has no native sign-in: it does not issue ID tokens, so mobile apps use the same web flow in a browser tab.