Google / GitHub Social Login — Operational Setup Guide
The "Continue with Google" / "Continue with GitHub" buttons on the sign-in page
(resources/js/components/auth/oauth-buttons.tsx) are backed by a real
Socialite-based implementation (app/Http/Controllers/Auth/OAuthController.php).
The buttons themselves are always visible (there's no frontend check for
whether credentials are configured) — but until the env vars below are set,
clicking one redirects back to /login with a friendly
"Google/GitHub sign-in is not configured on this server" error instead of
reaching the provider. Nothing crashes either way; nobody can actually sign
in with them until you set these up.
Credentials you'll end up with
| Env variable | What it is | Where it comes from |
|---|---|---|
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET |
OAuth2 app identity for Google sign-in | Google Cloud Console → Credentials |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET |
OAuth2 app identity for GitHub sign-in | GitHub → Developer settings → OAuth Apps |
Both are read by config/services.php and consumed by
Laravel\Socialite\Facades\Socialite in OAuthController. No refresh tokens
or offline access are needed here — this is a standard "sign in with X" login
flow, not a data-access integration like Google Ads.
Redirect URIs — get these exactly right
Socialite builds the callback URL from config('services.{provider}.redirect'),
which is a relative path resolved against APP_URL:
| Provider | Redirect path (already set in config/services.php) |
Full URI to register |
|---|---|---|
/auth/callback/google |
{APP_URL}/auth/callback/google |
|
| GitHub | /auth/callback/github |
{APP_URL}/auth/callback/github |
Register one redirect URI per environment you use (local, staging,
production) — e.g. for local dev with APP_URL=http://localhost:8000:
http://localhost:8000/auth/callback/google. Mismatched redirect URIs are the
single most common cause of OAuth failures (redirect_uri_mismatch).
Google — create an OAuth client
- Go to https://console.cloud.google.com/ → select or create a project
(you can reuse the same project as
docs/google-ads-api-setup.mdif one already exists, or create a dedicated one, e.g.answerix-auth). - APIs & Services → OAuth consent screen:
- User type: External → Create.
- Fill in the app name (e.g. "AnswerixAI"), support email, and developer contact email.
- Scopes: no extra scopes needed — Socialite requests
openid email profileby default, which needs no verification. - Add your own Google account as a test user while the app is in Testing status (needed to sign in yourself before publishing).
- APIs & Services → Credentials → Create Credentials → OAuth client ID:
- Application type: Web application.
- Name: e.g. "AnswerixAI Web Login".
- Authorized redirect URIs: add the full callback URL(s) from the
table above, one per environment (e.g. both the
localhost:8000and production URLs, as separate entries — Google allows multiple). - Click Create → copy the Client ID and Client Secret.
- When you're ready for real users (not just yourself as a test user), go
back to OAuth consent screen → Publish App. For the basic
openid/email/profilescopes this app uses, Google does not require the lengthy verification review — publishing is immediate.
GitHub — create an OAuth App
- Go to https://github.com/settings/developers (or, for an organization-owned
app,
https://github.com/organizations/{org}/settings/applications) → OAuth Apps → New OAuth App. - Fill in:
- Application name: e.g. "AnswerixAI".
- Homepage URL: your
APP_URL(e.g.https://app.answerixai.com). - Authorization callback URL: the full GitHub callback URL from the
table above (e.g.
https://app.answerixai.com/auth/callback/github). GitHub only allows one callback URL per OAuth App — if you need both a local-dev and a production callback, create two separate OAuth Apps (e.g. "AnswerixAI (dev)" and "AnswerixAI (prod)") with their own client ID/secret pairs, and use the dev one's credentials in your local.env.
- Click Register application.
- Copy the Client ID. Click Generate a new client secret and copy it immediately — GitHub only shows it once.
Configure and verify
Fill the block in .env (already present, commented out, at the bottom of
.env.example):
GITHUB_CLIENT_ID=Iv1.xxxxxxxxxxxxxxxx
GITHUB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
GOOGLE_CLIENT_ID=xxxxxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxx
No php artisan config:cache clear is needed in local dev; in production,
run php artisan config:clear && php artisan config:cache after changing
.env as usual.
Verify end-to-end:
- Visit
/login— the Google/GitHub buttons are already visible (they always render; there's no config check on the frontend). - Click a button → you should now land on the real Google/GitHub consent
screen, instead of bouncing back to
/loginwith a "not configured" error. - Approve → you should land back on
/dashboard, signed in. Checkphp artisan tinker --execute 'dd(App\Models\User::latest()->first());'to confirm a user row was created withemail_verified_atset and theavatar_urlpopulated from the provider. - Sign out and click the same button again with the same account — you should log into the same user row (matched by email), not create a duplicate.
How account matching works
OAuthController::callback() looks up an existing User by
case-insensitive email match before creating a new one. This means:
- If someone already has a password-based account with
[email protected]and later signs in with "Continue with Google" using the same Google account's email, they land in the same account — no duplicate, no password required for that login. - Brand-new email addresses get a new
Userrow with a random 40-character password (nobody knows it — the account is only reachable via OAuth or a password reset) andemail_verified_atset immediately, since the OAuth provider already verified that address.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
Clicking a button redirects straight back to /login with "sign-in is not configured on this server" |
GOOGLE_CLIENT_ID / GITHUB_CLIENT_ID is empty in the running environment — check .env and that config cache was cleared after editing it. Note the buttons themselves are always visible regardless of configuration, so this is the actual signal that credentials are missing. |
redirect_uri_mismatch (Google) or The redirect_uri MUST match the registered callback URL (GitHub) |
The registered redirect URI doesn't exactly match {APP_URL}/auth/callback/{provider} — check for http vs https, trailing slash, or wrong port. |
Redirects back to /login with a generic sign-in error after approving on the provider |
Something threw inside Socialite::driver($provider)->user() — check storage/logs/laravel.log; common causes are a wrong/rotated client secret, or the OAuth consent screen being in Testing mode with your account not added as a test user (Google only). |
| "Your Google/GitHub account has no email address" | The provider account has no public/verified email associated (rare for Google; GitHub accounts can hide their email). GitHub scope requested is user:email via Socialite's default, which should surface the primary email even if hidden from the public profile — if this still fails, the account likely has no verified email at all on GitHub's side. |
| New account created every time instead of reusing an existing one | Email casing or a leading/trailing space mismatch — the app does an exact lower(email) comparison; if the provider ever returns a differently-formatted address for the same person, it won't match. Not expected in normal use. |
Operational notes
- No refresh tokens, no offline scope — this is login-only. There's nothing to rotate or re-consent beyond the client ID/secret themselves.
- Rotating a client secret: generate a new one in Google Cloud Console / GitHub OAuth App settings, update the env var, redeploy. The old secret stops working immediately — do this during a low-traffic window if you're worried about in-flight logins (the window is typically seconds).
- Multiple environments: use separate OAuth apps/clients per environment (dev/staging/prod) rather than reusing one with multiple redirect URIs — GitHub in particular only supports one callback URL per app, and keeping environments on separate credentials makes it easy to revoke a compromised dev credential without touching production.
- Whitelabel deployments: the current implementation reads one global
GOOGLE_CLIENT_ID/GITHUB_CLIENT_IDpair fromconfig/services.php— it is not per-whitelabel like Stripe. All whitelabel domains share the same OAuth app, so the redirect URI registered with Google/GitHub must cover whichever domain(s) actually serve/auth/callback/*. If a specific whitelabel agency needs their own OAuth branding (their app name shown on the Google consent screen instead of "AnswerixAI"), that would require extendingOAuthController/config/services.phpto resolve per-whitelabel credentials the same way Stripe does — out of scope for the current setup.