A brokered social sign-in has two redirect hops, and therefore two callback URLs registered in two different places. Almost every setup problem comes from confusing them.
The flow, step by step
1. user clicks "Sign in with LinkedIn" in your app 2. → AuthRobo (start) 3. → LinkedIn (user approves) 4. → AuthRobo /api/v1/auth/linkedin/callback ← callback #1 5. → your app https://yourapp.com/auth/callback ← callback #2 (+ AuthRobo code) 6. your app calls exchange() → tokens
Callback #1 — provider → AuthRobo
Registered inside your LinkedIn/Google/GitHub app. It must be AuthRobo's endpoint:
https://authrobo.com/api/v1/auth/<provider>/callback
This is where the provider hands its code to AuthRobo, which holds the secret and does the token exchange. It is the same value whether you use a shared or bring-your-own app.
Callback #2 — AuthRobo → your app
Registered in the AuthRobo dashboard, in your app's allowed redirect URIs. This is *your* URL:
https://yourapp.com/auth/callback
This is where AuthRobo sends its own one-time code after the provider dance finishes. Your backend exchanges that code for tokens.
Callback #2 is per environment
Each environment (production, staging, sandbox, development) is its own set of credentials with its own allowed redirect URIs, edited on its own tab under Environments → Callback URLs. A new environment starts from production's list, and diverges the moment you edit it — changing staging's callbacks never touches production's.
production https://mycv.com/auth/callback staging https://staging.mycv.com/auth/callback development http://localhost:3000/auth/callback
A callback must be https, in every environment — an authorization code delivered over plaintext is a code anyone on the network can steal. The one exception is loopback: http://localhost and http://127.0.0.1 are accepted anywhere, because a loopback callback can't be intercepted remotely and it's how native and CLI clients sign in. Development is simply where loopback is expected rather than unusual.
Side by side
| Callback #1 | Callback #2 | |
|---|---|---|
| Registered in | Your provider app (LinkedIn, …) | AuthRobo dashboard |
| Value | https://authrobo.com/api/v1/auth/<provider>/callback | https://yourapp.com/… |
| Who receives the code | AuthRobo | Your app |
| Which secret exchanges it | Provider secret (AuthRobo holds) | Your client_secret |
Worked example: MYCV
You're adding LinkedIn to MYCV with your own LinkedIn credentials:
- In the LinkedIn app, set the authorized redirect URL to
https://authrobo.com/api/v1/auth/linkedin/callback(callback #1). Not a MYCV URL. - In the AuthRobo dashboard, add
https://mycv.com/auth/callbackto MYCV's allowed redirect URIs (callback #2).
If you instead point the LinkedIn app at https://mycv.com/…, LinkedIn hands its code straight to MYCV, AuthRobo is bypassed, and none of the broker's benefits (uniform tokens, user records, Stripe linkage) apply.
auth.mycv.com as a CNAME to AuthRobo). The code is still served by AuthRobo — only the hostname changes.