Link and unlink GitHub or Google accounts
A signed-in user can add a GitHub or Google login to an existing account, then sign in with it later. These routes come from MapExternalAccountEndpoints in the AuthEndpoints.External.OAuth preview package. The facade does not map them.
Map the account routes
Register the providers as in Register with GitHub or Google. Then map the account routes on the same group as the sign-in routes:
var external = app.MapGroup("/auth/external");
external.MapGitHubAuthEndpoints<AppUser>();
external.MapGoogleAuthEndpoints<AppUser>();
external.MapExternalAccountEndpoints<AppUser>();
MapExternalAccountEndpoints maps one link/{scheme} route pair for each registered provider. The schemes are GitHub and Google.
| Task | Endpoint | Needs a signed-in user | CSRF | ReAuth |
|---|---|---|---|---|
| Start a link | GET /auth/external/link/{scheme}?returnUrl= | Yes | No | No |
| Finish a link | GET /auth/external/link/{scheme}/callback | Yes | No | No |
| List linked logins | GET /auth/external/logins | Yes | No | No |
| Unlink a login | DELETE /auth/external/logins/{loginProvider}/{providerKey} | Yes | Yes | Yes |
Link a login
- While the user is signed in, send the browser to the link route with a top-level navigation:
window.location.assign('/auth/external/link/GitHub?returnUrl=' + encodeURIComponent('/settings/logins')); - The provider sends the user back to the callback. The callback checks that the provider login came from the same signed-in user.
- On success, the callback links the login and returns a
302toreturnUrl.
When RequireVerifiedEmail is true (the default), the provider email must be verified. A failed link redirects to ErrorPath?error=...&error_description=.... The codes are provider_mismatch, email_unverified, login_link_failed, and external_login_info_missing.
Linking does not compare the provider email with the account email. The user proves both identities by being signed in and completing the provider sign-in.
List linked logins
Send GET /auth/external/logins. The response is an array of { "loginProvider", "providerKey", "providerDisplayName" }.
Unlink a login
- Complete step-up. See Require step-up before sensitive actions.
- Get a CSRF token from
GET /identity/csrfToken. - Send
DELETE /auth/external/logins/<loginProvider>/<providerKey>with theRequestVerificationTokenheader. Take both values from the list response. - Check the response:
204: the login is unlinked.401: the user has no ReAuth proof.404: the user has no such login.400with titlelast_signin_method: the login is the last way to sign in. The user must keep a password, a passkey, or another external login.