Skip to main content

Server-Side Verification

Web3Auth's server-side verification feature enables developers to securely authenticate users on the backend by validating the ownership of a wallet address. This process involves the use of a JSON Web Token (JWT) issued upon user authentication, which contains claims about the end user, including proof of ownership over a wallet public address.

Upon a user's successful connection of their wallet, Web3Auth generates a JWT, signed with a private key using the ES256 algorithm, that is unique to your application. This token serves as a verifiable credential that you can use to authenticate the user on your backend.

Implementing Server-Side Verification

To implement server-side verification with Web3Auth:

  1. Retrieve the JWT (idToken): When a user logs in through Web3Auth, a JWT (JSON Web Token) can be generated to prove that the user owns the wallet, and you can verify it on your backend server.

    Different Flows for Different Wallets:

    • Social Logins: These tokens include a public key linked to the user’s wallet.
    • External Wallets: These tokens include the wallet address instead of the public key.
  2. Verify the JWT: Use the public key/address provided by Web3Auth to verify the JWT's authenticity. This step confirms that the token is valid and the information it contains about the user can be trusted.

  3. Authenticate the User: Based on the verified claims within the JWT, including wallet address ownership, authenticate the user in your backend system.

Getting the ID Token

After a user is logged in using one of the supported Wallets, One can obtain the idToken by calling the authenticateUser function of Web3Auth.

await web3auth.authenticateUser();

Returns

{
"idToken": "JWT_TOKEN_ISSUED_BY_WEB3AUTH"
}
Extending Beyond Social Logins to External Wallets

Web3Auth's server-side verification extends not only to users authenticated via the social/federated logins, but also to those using external wallets. This flexibility ensures that dApps can authenticate Web3Auth users in their backend systems, regardless of the authentication method used on the frontend.

Sample ID Token payload

warning

The idToken payload for the External Wallet is different from the one used in Social logins. For Social logins, we receive the user's public_key, while for External Wallets we obtain the address.

It's important to note that not all social logins will provide all user information, such as email, name, and profileImage. If your application relies on this information, make sure you handle cases where these fields are not present. We recommend using the value of oAuthIdToken as it will be more accurate, based on the specific oAuth provider you're working with. Please note that oAuthIdToken is only available when using our Custom Authentication feature.

{
"iat": 1722394613,
"aud": "BHgArYmWwSeq21czpcarYh0EVq2WWOzflX-NTK-tY1-1pauPzHKRRLgpABkmYiIV_og9jAvoIxQ8L3Smrwe04Lw",
"iss": "https://api-auth.web3auth.io",
"nonce": "036256d2a4055cea253e250c3e3158bf86576477d944fd5ee23f92454e13098267",
"email": "xyz@xyz.com", // this field may not be present for all social providers
"name": "John Doe", // this field may not be present for all social providers
"profileImage": "https://lh3.googleusercontent.com/a/AATXAJx3lnGmHiM4K97uLo9Rb0AxOceH-dQCBSRqGbck=s96-c", // this field may not be present for all social providers
"verifier": "web3auth",
"verifierId": "xyz@xyz.com",
"aggregateVerifier": "web3auth-google-sapphire-devnet",
"exp": 1722481013,
"wallets": [
{
// This is your PnP Key
"public_key": "578970a884b44bbe0cf479d633fe7fa20cfc92dd4ac05eabdb298f4a6413064c",
"type": "web3auth_app_key",
"curve": "secp256k1" // or "ed25519"
},
{
// This is your CoreKit Key
"public_key": "218a3a9eb7febe18daa126902080a5a84c3952810d86d6f4d54c283007f2f868",
"type": "web3auth_threshold_key",
"curve": "secp256k1" // or "ed25519"
}
]
}
ParameterTypeDescription
iatnumberThe "iat" (issued at) claim identifies the time at which the JWT was issued.
audstringThe "aud" (audience) claim identifies the recipients that the JWT is intended for. (Here, it's the dapp client_id)
issstringThe "iss" (issuer) claim identifies who issued the JWT. (Here, it's Web3Auth's https://api-auth.web3auth.io)
emailstringThe email address of the user (optional). This field may not be present for all social providers
namestringThe name of the user (optional). This field may not be present for all social providers
profileImagestringThe profile image of the user (optional). This field may not be present for all social providers
verifierstringWeb3auth's verifier used while user login
verifierIdstringUnique user id given by OAuth login provider
aggregateVerifierstringName of the verifier if you are using a single id verifier (aggregateVerifier) (optional)
expnumberThe "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.
walletsarrayA list of wallets for which this token is issued:
  • curve "secp256k1" (default) or "ed25519"
  • type "web3auth_app_key" or "web3auth_threshold_key" in case of social logins
  • public_key compressed public key derived based on the specified curve

To get the ed25519 curve based public_key, you can specify in the loginSettings of the AuthAdapter when working with Web SDK. For mobile SDKs, specify the curve in the LoginParams.
Changing the Public Key Curve

If you want to encode the public_key in ed25519 format then you have to pass the curve parameter in AuthAdapter as:

const authAdapter = new AuthAdapter({
loginSettings: {
curve: "ed25519", // allowed values "secp256k1" (default) or "ed25519"
},
});
NOTE

By default, Web3Auth returns an idToken which contains the public_key in the secp256k1 format.

Verifing the ID Token

  • JWT Validation: The associated public key, available on a hosted endpoint by Web3Auth, allows you to verify the token's authenticity and integrity. A JWT that can be successfully verified with this public key confirms that the information it contains is trustworthy and has not been altered.

  • Proof of Wallet Ownership: The JWT includes a claim that certifies the user's ownership of a specific wallet's public address, enabling a reliable method of user authentication on the server side.

    JWKS endpoints by Web3Auth

    Please note, the JWKS endpoint for External wallets is different from the Social logins.

Social Login Verification

In order to verify the JWT Token(idToken) you'll need the compressed app_pub_key (derived from app_scoped_privkey) and the idToken obtained from the authenticateUser function in your frontend.

Getting app_pub_key and idToken

Get the app_pub_key and idToken in the frontend of your application.

import { getPublicCompressed } from "@toruslabs/eccrypto";

const app_scoped_privkey = await web3auth.provider?.request({
method: "eth_private_key", // use "private_key" for other non-evm chains
});

const app_pub_key = getPublicCompressed(
Buffer.from(app_scoped_privkey.padStart(64, "0"), "hex"),
).toString("hex");
/* Assuming the user is logged in, get the userInfo containing idToken */
const user = await web3auth.getUserInfo();

/*
OR
const token = await web3auth.authenticateUser();
*/

// Verify idToken at your backend server
const res = await fetch("/api/verify", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + user.idToken, // or token.idToken
},
body: JSON.stringify({ appPubKey: app_pub_key }),
});

Verifying idToken

Verify the JWT Token(idToken) on the backend and match the app_pub_key obtained from the frontend.

JWKS endpoints by Web3Auth

Please note that the JWKS endpoint for the External wallets is different from the Social logins.

import * as jose from "jose"

// idToken passed from the frontend in the Authorization header
const idToken = req.headers.authorization?.split(' ')[1];

// app_pub_key passed from the frontend in the request body
const app_pub_key = req.body.appPubKey;

// Get the JWKS used to sign the JWT issued by Web3Auth
const jwks = jose.createRemoteJWKSet(new URL("https://api-auth.web3auth.io/jwks")); // for social logins

// Verify the JWT using Web3Auth's JWKS
const jwtDecoded = await jose.jwtVerify(idToken, jwks, { algorithms: ["ES256"] });

// Checking `app_pub_key` against the decoded JWT wallet's public_key
// use type === web3auth_threshold_key when working with CoreKitKey
if ((jwtDecoded.payload as any).wallets.find((x: { type: string }) => x.type === "web3auth_app_key").public_key.toLowerCase() === app_pub_key.toLowerCase()) {
// Verified
res.status(200).json({name: 'Verification Successful'})
} else {
res.status(400).json({name: 'Verification Failed'})
}

External Wallet Verification

To verify the JWT Token(idToken) you need the wallet address and the idToken obtained from the authenticateUser() function in your frontend.

Getting address and idToken

Get the address and idToken in the frontend of your application.

// Get user's Ethereum public address
const address = (await web3.eth.getAccounts())[0];
// Get user's Solana public address
const address = await solanaWallet.requestAccounts();

// For other chains, please refer to https://web3auth.io/docs/connect-blockchain/

/* Assuming user is logged in, get the token containtaing idToken */
const token = await web3auth.authenticateUser();

// Verify idToken at your backend server
const res = await fetch("/api/verify", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token.idToken,
},
body: JSON.stringify({ public_address: address }),
});

Verifying idToken

Verify the JWT Token(idToken) on the backend and match the public_address obtained from the frontend.

JWKS endpoints by Web3Auth

Please note that the JWKS endpoint for the External wallets is different from the Social logins.

import * as jose from "jose"

// passed from the frontend in the Authorization header
const idToken = req.headers.authorization?.split(' ')[1];

// passed from the frontend in the request body
const publicAddress = req.body.public_address;

// Get the JWK set used to sign the JWT issued by Web3Auth
const jwks = jose.createRemoteJWKSet(new URL("https://authjs.web3auth.io/jwks"));

// Verify the JWT using Web3Auth's JWKS
const jwtDecoded = await jose.jwtVerify(idToken, jwks, { algorithms: ["ES256"] });

// Incase of Non-Torus Users
// Checking Wallet's `publicAddress` against the decoded JWT wallet's address
// use type === ethereum for Ethereum wallets, solana for Solana wallets, default is ethereum
if((jwtDecoded.payload as any).wallets.find((x: { type: string }) => x.type === "ethereum").address.toLowerCase() === publicAddress.toLowerCase()) {
// Verified
res.status(200).json({name: 'Verification Successful'})
} else {
res.status(400).json({name: 'Verification Failed'})
}

Things to Remember

  1. Use the Correct JWKS Endpoint: Always ensure that you are using the correct JWKS endpoint corresponding to the user’s login method (social login vs. external wallet). This will prevent verification failures.
  2. Understand the JWT Structure: Familiarize yourself with the differences in JWT payloads between social logins and external wallets. Ensure your backend logic can handle these differences correctly.
  3. Verify the Key Curve: Double-check that your backend is configured to handle the specific key curve used by Web3Auth (either secp256k1 or ed25519). This will prevent verification errors and improve security.
  4. Handle Expired Tokens: Implement proper checks for the exp claim to ensure tokens are not used beyond their expiration. Consider adding custom logic to refresh tokens when necessary.
  5. Match the Audience: Verify that the aud claim in the JWT matches the expected audience on your backend. This ensures that the JWT is intended for your application and prevents potential misuse.

By following these, you can ensure a smoother integration and reduce the likelihood of errors during server-side verification with Web3Auth. Still have questions? Feel free to reach out to our Community Forum for further assistance.