What's the callback URL?
In OAuth, after the user authenticates with the provider (Google, Apple, GitHub), the provider redirects them back to your application — to a specific URL you configured ahead of time. This URL receives the authorization code (or token, depending on the flow) and is the bridge between "user logged in over there" and "user logged in over here".
That URL is the callback URL (sometimes "redirect URI" or "return URL").
1. user clicks "Sign in with Google" on https://your-app.com
2. browser → google.com/oauth/authorize?redirect_uri=https://your-app.com/callback&...
3. user signs in at google.com
4. google.com redirects → https://your-app.com/callback?code=...
5. your-app.com exchanges code for tokens
Why it must match exactly
The provider validates that the redirect_uri parameter matches one you pre-registered in their dashboard. Match means exact:
- scheme (
https://nothttp://) - host (
auth.example.comnotexample.com) - port (if non-default)
- path (
/callbacknot/oauth/callback) - trailing slash
If they don't match, the provider refuses to redirect and shows an error. This is a security feature — without it, an attacker could swap in their own URL and steal the auth code.
Why HTTPS is required
The auth code transits via the redirect URL. If the URL is HTTP, the code is in plain text on the wire and any network observer can grab it. Modern OAuth providers (Apple, Google, Auth0, Clerk) refuse to register HTTP URLs; some allow http://localhost as a special case for dev.
The localhost trap
Some providers allow http://localhost:3000/callback. Others don't. Apple Sign-In doesn't. Even ones that do, won't accept it from an iOS device — localhost on the phone is the phone's localhost, not your laptop.
Trying to develop against just http://localhost works for ~half of providers and fails confusingly for the other half.
The clean fix: tunnel + reserved subdomain
Use a reverse-tunnel service to expose your localhost on a real, certificate-valid public URL. Reserve a stable subdomain so you set the callback in the provider's dashboard once and never re-paste:
$ lrok reserve auth-dev
$ lrok http 3000 --hint auth-dev
Forwarding https://auth-dev.lrok.io → http://127.0.0.1:3000
In the provider's dashboard:
- Auth0: Allowed Callback URLs →
https://auth-dev.lrok.io/callback - Clerk: OAuth redirect URIs →
https://auth-dev.lrok.io/oauth/callback - Google: Authorized redirect URIs →
https://auth-dev.lrok.io/api/auth/callback/google - GitHub: Authorization callback URL →
https://auth-dev.lrok.io/api/auth/callback/github
The URL is permanent for as long as you keep the reservation. Across days, machines, restarts.
Multiple environments
Most providers accept multiple callback URLs per app. Convention:
https://your-app.com/callback ← prod
https://staging.your-app.com/callback ← staging
https://auth-dev.lrok.io/callback ← local dev
Configure all three. Your app reads its own host at runtime and builds the redirect; the provider matches against the registered list.
Per-provider walkthroughs
Or the broader use-case guide: /use-case/expose-nextjs (which has the OAuth + lrok specifics for Next.js).