Skip to content

Custom authentication in React Native SDK #140

Description

@tiago138

v9 headless login (connectTo with idToken) hardcodes the user_id JWT claim — breaks non-Firebase custom connections, verifierIdField is ignored

SDK version

@web3auth/react-native-sdk@9.0.0 (latest at time of writing)

Summary

When connectTo() is called with a top-level idToken (the headless / SFA-style login path), the SDK derives the verifier ID from the JWT with a hardcoded claim lookup:

// dist/lib.esm/Web3Auth.js
getUserIdFromJWT(token) {
  const decoded = jwtDecode(token);
  return decoded["user_id"];
}

user_id is a Firebase-specific claim. Standard OIDC idTokens — Google Sign-In (native @react-native-google-signin), Sign in with Apple, Auth0, Cognito, and most other BYO-JWT providers — carry the user identifier in sub and have no user_id claim. For those tokens getUserIdFromJWT returns undefined, which is then used as verifierId in getNodeDetails() / retrieveShares(), and the login fails.

extraLoginOptions.verifierIdField (shown in the migration docs' custom-JWT examples) does not help: it is never consulted on this code path. grep -rn 'verifierIdField' node_modules/@web3auth/react-native-sdk/dist/ returns zero matches — extraLoginOptions is only forwarded to the hosted auth page in the browser-redirect flows.

The docs' Firebase example works only coincidentally: Firebase idTokens contain both sub and user_id (with identical values), which masks the hardcoded lookup.

How we call it today (working, @web3auth/single-factor-auth@9.5.0)

We use native Google Sign-In / Sign in with Apple to obtain the idToken on-device, then log in headlessly through a grouped (aggregate) verifier. SFA accepts the verifier ID explicitly, so any JWT shape works:

// idToken from native sign-in:
//   Google: GoogleSignin.signIn() -> data.idToken, verifierId = data.user.id (= token's `sub`)
//   Apple:  identityToken,            verifierId = decoded.sub / credential.user

await web3authSFAuth.connect({
  verifier: aggregateVerifier,        // grouped/aggregate verifier
  verifierId,                          // passed explicitly — the token's `sub`
  idToken,
  subVerifierInfoArray: [
    {
      verifier: subVerifier,           // per-provider sub-verifier
      idToken,
    },
  ],
});

What the v9 migration should look like (fails)

Per the migration guide, the equivalent grouped-connection call on v9 is:

await connectTo({
  authConnection: AUTH_CONNECTION.CUSTOM,
  authConnectionId: subVerifier,           // was subVerifierInfoArray[0].verifier
  groupedAuthConnectionId: aggregateVerifier, // was `verifier`
  idToken,                                  // top-level, headless path
  // no way to pass verifierId explicitly, and
  // extraLoginOptions: { verifierIdField: 'sub' } is ignored on this path
});

Internally this reproduces the same aggregate flow as SFA (connectTo builds the subVerifierInfoArray itself), except the verifier ID: instead of the explicit verifierId SFA accepted, it calls getUserIdFromJWT(idToken) — which returns undefined for Google/Apple tokens, so verifierParams.verifier_id is undefined and share retrieval fails.

Steps to reproduce

  1. Configure a custom grouped verifier whose JWT verifier ID field is sub — e.g. a Google connection used with the native Google Sign-In SDK.
  2. Obtain a Google OIDC idToken on-device. Decode it: it has sub, no user_id.
  3. Call connectTo as shown above.
  4. getUserIdFromJWT(idToken) returns undefinedverifierParams.verifier_id is undefined → share retrieval fails.

Expected behavior

One (ideally both) of:

  1. connectTo honors extraLoginOptions.verifierIdField (matching the documented API surface) when deriving the verifier ID in the headless path, e.g. decoded[verifierIdField ?? 'user_id'].
  2. A sane default fallback: decoded.user_id ?? decoded.subsub is the standard OIDC subject claim, and Firebase tokens keep working since they carry both claims with equal values.

Why this matters for migrations

This blocks the documented migration path from @web3auth/single-factor-auth, which accepted an explicit verifierId and therefore worked with any JWT shape. Apps using native Google/Apple sign-in (i.e. anything that isn't Firebase) currently cannot move their SFA flows to v9 connectTo without monkey-patching Web3Auth.prototype.getUserIdFromJWT. Because the wallet key is derived from (verifier, verifierId), the fix must produce the same sub value users logged in with via SFA — otherwise existing users lose access to their wallets.

Environment

  • React Native (Expo), @web3auth/react-native-sdk@9.0.0
  • Bundled @web3auth/auth@10.8.0
  • Grouped custom connection (aggregate verifier + sub-verifiers), tokens from native Google Sign-In and Sign in with Apple

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions