Grouped connections not working

Good morning.

I’m facing an issue with the grouped connections feature, from the documentation we have that:

Group Connections enable multiple login methods to be linked to the same on-chain user identity. This means that users logging in with different authentication providers (e.g., Google and Email Passwordless) can still access the same wallet address—ensuring a unified user experience.

I have two separated projects, BE that uses the Node SDK and FE that uses the React SDK, I set up the same project in both, I set up the google login like in the documentation and created a Custom JWT following this documentation, and then, grouped those two login methods into one Grouped connection.

When I log in using the google login modal I get 2 wallets, the address for the solana wallet is different to the one I get when I log in to web3auth using the Node SDK and my custom JWT, even tho in token I specifcy to use the correct settings to be able to log in through the group connection.

I just set up the examples for Node and React, with the same configuration and I get two different wallet addresses:

React

// IMP START - Quick Start
import { type Web3AuthContextConfig } from '@web3auth/modal/react'
import { WEB3AUTH_NETWORK } from '@web3auth/base';
import { WALLET_CONNECTORS, type Web3AuthOptions } from '@web3auth/modal'
// IMP END - Quick Start

// IMP START - Dashboard Registration
const clientId = <<MY_CLIENT_ID>>; // get from https://dashboard.web3auth.io
// IMP END - Dashboard Registration

// IMP START - Instantiate SDK
const web3AuthOptions: Web3AuthOptions = {
    clientId,
    web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_DEVNET,
    modalConfig: {
        connectors: {
            [WALLET_CONNECTORS.AUTH]: {
                label: "auth",
                loginMethods: {
                    google: {
                        name: "Google Login",
                        authConnectionId: "rampa-w3a-google",
                        groupedAuthConnectionId: "rampa-w3a-group-connection",
                    },
                },
            },
        },
    },
}

const web3AuthContextConfig: Web3AuthContextConfig = {
    web3AuthOptions,
}
// IMP END - Instantiate SDK

export default web3AuthContextConfig;

Node

const jwt = require(‘jsonwebtoken’);

const { LAMPORTS_PER_SOL, Connection, PublicKey } = require(“@solanasolanasolanasolana/web3.js”);

const fs = require(“fs”).promises;

const { createSignableMessage } = req@solanasolanaire(‘@solana/signers’);

// IMP START - Quick Start

const { Web3Auth } = require(“/node-sdk”);

// IMP END - Quick Start

// IMP START - Blockchain Calls

// Get Solana Accounts

const getAccounts = async (signer) => {

try {

const publicKey = signer.address;

console.log(“\x1b[33m%s\x1b[0m”, “Public Key:”, publicKey);

} catch (error) {

console.error(“\x1b[33m%s\x1b[0m”, “Error fetching accounts:”, error);

}

};

// Get Solana Balance

const getBalance = async (signer) => {

try {

const publicKey = new PublicKey(signer.address);

const connection = new Connection(“https://api.devnet.solana.com”);

const balance = await connection.getBalance(publicKey) / LAMPORTS_PER_SOL;

console.log(“\x1b[33m%s\x1b[0m”, “Balance:”, balance, “SOL”);

} catch (error) {

console.error(“\x1b[33m%s\x1b[0m”, “Error fetching balance:”, error);

}

};

// Sign Solana Message

const signMessage = async (signer, message) => {

try {

const messageBytes = new TextEncoder().encode(message);

// Create a SignableMessage object from Uint8Array

const signableMessage = createSignableMessage(messageBytes);

const [signature] = await signer.signMessages([signableMessage]);

console.log(“\x1b[33m%s\x1b[0m”, “Signed Message:”, signature);

} catch (error) {

console.error(“\x1b[33m%s\x1b[0m”, “Error signing message:”, error);

}

};

// IMP END - Blockchain Calls

// Perform JWT Login and MFA Setup

const initAndLogin = async () => {

// IMP START - Dashboard Registration

const clientId = <<MY_CLIENT_ID>>;

// IMP END - Dashboard Registration

// IMP START - Verifier Creation

const authConnectionId = “rampa-w3a-jwt”;

// IMP END - Verifier Creation

// IMP START - SDK Initialization

const web3auth = new Web3Auth({

clientId,

web3AuthNetwork: “sapphire_devnet”,

});

await web3auth.init();

// IMP END - SDK Initialization

// IMP START - Auth Provider Login

const privateKey = await fs.readFile(“privateKey.pem”, “utf8”);

// CORRECTED: Use the correct kid and sub values

var idToken = jwt.sign(

{

sub: ‘stvnbvsts@gmail.com’, 

name: ‘Steven Bustos’, 

email: ‘stvnbvsts@gmail.com’,

aud: ‘urn:rampa-web3auth’,

iss: ‘ https://auth.rampa.local’,

iat: Math.floor(Date.now() / 1000),

exp: Math.floor(Date.now() / 1000) + 60 * 60,

},

privateKey,

{ algorithm: ‘RS256’, keyid: ‘key-v1’ } // Match Web3AuthNodeService

);

console.log(“\x1b[33m%s\x1b[0m”, “JWT Token:”, idToken);

// IMP END - Auth Provider Login

// IMP START - Login

const result = await web3auth.connect({

authConnectionId: “rampa-w3a-jwt”,

idToken,

groupedAuthConnectionId: “rampa-w3a-group-connection”, // Match your Google token

isUserIdCaseSensitive: true,

userId: “stvnbvsts@gmail.com”, // Use email

userIdField: “sub”, // Use sub field

});

console.log(“\x1b[33m%s\x1b[0m”, “Login successful. \nConnected Chain Namespace:”, result.chainNamespace);

// IMP START - Blockchain Interaction

await getAccounts(result.signer); // Get Solana Accounts

await getBalance(result.signer); // Get Solana Balance

await signMessage(result.signer, “Hello Web3Auth!”); // Sign a Message

// IMP END - Blockchain Interaction

console.log(“\x1b[33m%s\x1b[0m”, “Blockchain interactions done.”);

};

// Call the initialization and login function

initAndLogin().catch(console.error);

This is my google connection configuration:

This is the custom JWT:

And this is the grouped connection set up:

what I’m missing here? I use the same email / name to log in and it creates different wallet address

Hi @Steven_Bustos ,

The problem could be linked to the verifierIdField not being set.

When verifierIdField is not explicitly set, Web3Auth will use the default field, which is "sub". But for google I think it should be "email"

So:

  • JWT flow: Uses sub field (which contains email) :white_check_mark:
  • Google OAuth flow: Uses sub field (which contains Google’s internal user ID, NOT the email) :cross_mark:

This means the two flows are using completely different identifiers:

  • JWT: stvnbvsts@gmail.com (from sub field)
  • Google: some-google-user-id (from sub field)

Try to explicitly set verifierIdField: "email" for the Google OAuth flow so both flows use the same identifier:

React Configuration:

const web3AuthOptions: Web3AuthOptions = {
  clientId,
  web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_DEVNET,
  modalConfig: {
    connectors: {
      [WALLET_CONNECTORS.AUTH]: {
        label: "auth",
        loginMethods: {
          google: {
            name: "Google Login",
            authConnectionId: "rampa-w3a-google",
            groupedAuthConnectionId: "rampa-w3a-group-connection",
            verifierIdField: "email", // ADD THIS - use email field from Google token
          },
        },
      },
    },
  },
}

React Login Call:

await connectTo(WALLET_CONNECTORS.AUTH, {
  groupedAuthConnectionId: "rampa-w3a-group-connection",
  authConnectionId: "rampa-w3a-google",
  authConnection: AUTH_CONNECTION.GOOGLE,
  extraLoginOptions: {
    isUserIdCaseSensitive: false,
    verifierIdField: "email", // ADD THIS
  },
});

Node.js Configuration (keep as is):

const result = await web3auth.connect({
  authConnectionId: "rampa-w3a-jwt",
  idToken,
  groupedAuthConnectionId: "rampa-w3a-group-connection",
  isUserIdCaseSensitive: false,
  userId: "stvnbvsts@gmail.com", // This comes from JWT sub field
  userIdField: "sub", // JWT uses sub field
});

Both flows need to extract the same user identifier (the email) to get the same wallet address in grouped connections.

Hope this helps you solve your issue.

Hi @khanti42

Thank you very much for your answer.

I tried your approach and keep having the same issue, for some reason both address are different.

I was checking the Analytics section in the Web3Auth dashboard, I found out that if I log in using Google and my custom JWT, the # of active wallets is 1, if then I log in via google to another account the # increases to 2, if I in node log in using this same email the # stays in 2.

So it seems the # of active wallets does not increase.

For React I use: const { accounts, solanaWallet, connection } = useSolanaWallet();

and then <div>{accounts?.[0]}</div>, to get the address.

For Node I use: result.signer.address