Link accounts from React through API server

Hey folks,

I’m looking to implement account linking using the Auth0 React SDK and I’m running into a hesitancy on my part.

Context

I’ve read through most, if not all of the community posts and documentation concerning linking accounts:

  • https://community.auth0.com/t/user-account-linking-with-react/74828
  • https://community.auth0.com/t/how-to-link-accounts-via-api-using-the-auth0-js-sdk-in-react/27699
  • https://auth0.com/docs/manage-users/user-accounts/user-account-linking
  • https://auth0.com/docs/customize/extensions/account-link-extension
  • https://auth0.com/docs/manage-users/user-accounts/user-account-linking/user-initiated-account-linking-client-side-implementation
  • https://github.com/auth0-samples/auth0-link-accounts-sample
  • https://auth0.com/docs/manage-users/user-accounts/user-account-linking/suggested-account-linking-server-side-implementation

I have a React app that uses the <Auth0Provider /> similar to the following:

<Auth0Provider
  domain={AUTH0_DOMAIN}
  clientId={AUTH0_CLIENT_ID}
  authorizationParams={{
    redirect_uri: AUTH0_REDIRECT_URL,
    audience: AUTH0_CUSTOM_API_AUDIENCE
  }}>
  {children}
</Auth0Provider>

where the audience is a custom API for my application configured via the Auth0 dashboard (not the default Auth0 Management API).

The User-Initiated Account Linking: Client-Side Implementation tutorial relies on changing the default authorization audience/scopes and appears to use ID Tokens which look like they’re being deprecated.

I don’t want to use the full example from the User Account Linking: Server-Side Implementation since it seems more appropriate for a background job and relies on email addresses to be the same across social logins.

Strategy

I came up with a strategy that works with Access Tokens but it feels wrong so I’m essentially looking for a sanity check.

  1. User logs into my SPA application via Google Social Connection which I will treat as the primary account.
  2. I provide a settings page which lists additional accounts they can link to, one of which being the Github Social Connection. The user cannot get to this page without authenticating and even if they do, any API calls to my server will 403.
  3. They click a button to link their primary account (Google) with a secondary account (Github) which performs the following:
// The `preLoginToken` is an Access Token for the primary account (Google)
const preLoginToken = await getAccessTokenSilently();

// The `provider` is 'github'
await loginWithPopup({ authorizationParams: { connection: provider } }); 

// The `postLoginToken` is an Access Token for the secondary account (Github)
const postLoginToken = await getAccessTokenSilently();

fetch("<my-server>", { 
  method: "POST",
  headers: {
    "Authorization": `Bearer ${preLoginToken}`
  },
  body: JSON.stringify({ token: postLoginToken, provider })
}); 
  1. Server receives the primary account (Google) Access Token as the Bearer and the secondary account (Github) Access Token in a body payload.
  2. Server calls the Link Account Auth0 Management API with the following payload:
POST /api/v2/users/<bearer-sub-claim>/identities
Authorization: "Bearer <api-server-token>"
{
  "provider": "github",
  "user_id": "<token-sub-claim>"
}

where:

  • <bearer-sub-claim> - the parsed sub claim from the JWT on the Authorization header, in this case the primary account ID (Google).
  • <api-server-token> - the private custom API server token.
  • <token-sub-claim> - the parsed sub claim from the JWT in the payload, in this case the secondary account ID (Github).

Concerns

The above flow works. My concerns are primarily the security of this approach.

When I send the link request to my server, the primary access token (Google) is, in my eyes, stale. Calling getAccessTokenSilently() before an authorization event (login) with the sole purpose of re-using that token in a request because the authorization event would fundamentally change its value feels wrong.

The official “User-Initiated Account Linking: Client-Side Implementation” guide does exactly this but that tutorial looks out of date (plus the ID Token usage).

The other concern is sending/validating an Access Token in a request body. My server uses the same JWT verification code for both the Bearer token and this request body token. I don’t have a particular reason why this feels wrong, I’m used to seeing JWTs exclusively in auth headers.

Apologies for the novel but I feel it’s import to include full detail. I should also note this approach requires a page reload, otherwise you’re stuck on the secondary auth (Github).