Skip to main content

Identity token

The identity token (ID token) issued by Embedded Wallets is a JSON Web Token (JWT) that contains verified identity claims about the authenticated user. This token is signed using Embedded Wallets' private key and cannot be spoofed, allowing developers to trust the identity information presented by the client.

Once a user successfully authenticates via Embedded Wallets, the platform issues an ID token which can then be used to authorize client-to-server requests or verify ownership of associated wallet addresses.

The ID token enables:

  • User identity verification: Ensures that the client user is who they claim to be.
  • Secure backend requests: The token is passed in API requests to validate sessions server-side.
  • Wallet ownership proof: Includes public wallet keys to prove a user owns a particular wallet.

When making a backend request from the frontend, the client must include this ID token to ensure the backend can verify the authenticated user.

Server-side verification

Server-side verification uses the ID token to authenticate users on your backend by validating the ownership of a wallet address. The flow has three steps:

  1. Retrieve the ID token on the client. After the user authenticates with Embedded Wallets, the SDK issues a JWT signed with the ES256 algorithm. Send this token to your backend with each authenticated request.
  2. Verify the token on your backend. Validate the JWT signature against the appropriate JWKS endpoint or your project's verification key, and check that the wallet claim matches the identity the client is asserting.
  3. Authenticate the user. Once the token is verified, treat the wallet address (or public key) inside it as a trusted user identifier and continue with your application logic.

The contents of the token differ based on how the user signed in:

  • Social logins: The token includes a public_key linked to the user's wallet.
  • External wallets: The token includes the wallet address instead of the public key.

The sections below describe the token format, how to retrieve it on the client, and how to verify it on your backend.

ID token format

Embedded Wallets (previously Web3Auth) issues tokens as ES256-signed JWTs containing various identity claims about the user.

A sample decoded token is shown below:

Sample ID Token
{
"iat": 1747727490,
"aud": "BJp5VGbuhg_mUA.....7B0SseDPBWabmYmEFXpfu8CGWSw",
"nonce": "030cb3f1ab9593d987b17cb....38afe331561105213",
"iss": "https://api-auth.web3auth.io",
"wallets": [
{
"public_key": "5771379329ae0f3b76........82f17373a13d8683561",
"type": "web3auth_app_key",
"curve": "ed25519"
},
{
"public_key": "020fda199e933b24a74...........6c9cc67a13c23d",
"type": "web3auth_app_key",
"curve": "secp256k1"
}
],
"email": "shahbaz@web3auth.io",
"name": "Mohammad Shahbaz Alam",
"profileImage": "https://lh3.googleusercontent.com/a/AcJD_Fs0...._xzcWYzT=s96-c",
"authConnection": "web3auth",
"userId": "shahbaz@web3auth.io",
"groupedAuthConnectionId": "web3auth-google-sapphire-mainnet",
"exp": 1747813890
}
NOTE

If the Return user data in identity token setting is disabled on the dashboard, personally identifiable information (PII) such as email, name, and profileImage will be omitted from the token.

Getting the ID Token

To retrieve the ID token on the client-side, use the getIdentityToken() method. This is typically called after the user logs in.

import { useIdentityToken } from '@web3auth/modal/react'

function IdentityTokenButton() {
const { getIdentityToken, loading, error, token } = useIdentityToken()

return (
<div>
<button onClick={() => getIdentityToken()} disabled={loading}>
{loading ? 'Authenticating...' : 'Get Identity Token'}
</button>
{token && <div>Token: {token}</div>}
{error && <div>Error: {error.message}</div>}
</div>
)
}

Verifying the ID Token

To validate an ID token server-side, use Web3Auth's JWKS endpoint or project-specific verification key. This process ensures the JWT was issued by Web3Auth and its contents have not been tampered with.

Using the JWKS endpoint

JWKS Endpoint: https://api-auth.web3auth.io/jwks

login/route.ts
import * as jose from 'jose'
import { NextRequest, NextResponse } from 'next/server'

export async function POST(req: NextRequest) {
try {
// Extract JWT token from Authorization header
const authHeader = req.headers.get('authorization')
const idToken = authHeader?.split(' ')[1]
// Get public key from request body
const { appPubKey } = await req.json()

if (!idToken) {
return NextResponse.json({ error: 'No token provided' }, { status: 401 })
}

if (!appPubKey) {
return NextResponse.json({ error: 'No appPubKey provided' }, { status: 400 })
}

// Verify JWT using Web3Auth JWKS
const jwks = jose.createRemoteJWKSet(new URL('https://api-auth.web3auth.io/jwks'))
const { payload } = await jose.jwtVerify(idToken, jwks, { algorithms: ['ES256'] })

// Find matching wallet in JWT
const wallets = (payload as any).wallets || []
const normalizedAppKey = appPubKey.toLowerCase().replace(/^0x/, '')

const isValid = wallets.some((wallet: any) => {
if (wallet.type !== 'web3auth_app_key') return false

const walletKey = wallet.public_key.toLowerCase()

// Direct key comparison for ed25519 keys
if (walletKey === normalizedAppKey) return true

// Handle compressed secp256k1 keys
if (
wallet.curve === 'secp256k1' &&
walletKey.length === 66 &&
normalizedAppKey.length === 128
) {
const compressedWithoutPrefix = walletKey.substring(2)
return normalizedAppKey.startsWith(compressedWithoutPrefix)
}

return false
})

if (isValid) {
return NextResponse.json({ name: 'Verification Successful' }, { status: 200 })
} else {
return NextResponse.json({ name: 'Verification Failed' }, { status: 400 })
}
} catch (error) {
console.error('Social login verification error:', error)
return NextResponse.json({ error: 'Verification error' }, { status: 500 })
}
}

Using the verification key

To manually verify the token, use your Verification Key available on the Project Settings page in the dashboard.

npm install jose
Example (JWT Verification using jose)
const verificationKey = await jose.importSPKI('insert-your-web3auth-verification-key', 'ES256')

const idToken = 'insert-the-users-id-token'

try {
const payload = await jose.jwtVerify(idToken, verificationKey, {
issuer: 'https://api-auth.web3auth.io',
audience: 'your-project-client-id',
})
console.log(payload)
} catch (error) {
console.error(error)
}
info

If the token is valid, the payload will contain identity claims (such as, userId). If invalid, an error is thrown.

Troubleshooting

  • The iss field in the token must be https://api-auth.web3auth.io.
  • The aud field must match your Project Client ID.
  • The exp field must be in the future.
  • The iat field must be in the past.