Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.polygon.technology/llms.txt

Use this file to discover all available pages before exploring further.

Verify an embedded wallet user on your backend by accepting an ID token from the client and checking its signature and claims against the staging issuer.
Start with the quickstart for your SDK first: TypeScript, Swift, or Kotlin.
This guide is for the staging issuer at https://d26giflyqapd29.cloudfront.net. Use the production issuer and JWKS URL when verifying production tokens.

1. Send the ID token to your backend

After the user authenticates in the app, request an ID token for the active wallet. Send only the returned idToken string to your backend over HTTPS.
await oms.wallet.startEmailAuth({ email: 'user@example.com' })
await oms.wallet.completeEmailAuth({ code: '123456' })

const idToken = await oms.wallet.getIdToken({
  ttlSeconds: 300,
})

await fetch('/api/wallet-session', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ idToken }),
})

2. Verify the token signature and claims

Fetch keys from the staging JWKS endpoint and use a JWT library to verify the token. The library should select the signing key from the JWT header kid. For staging, validate:
ClaimExpected value
isshttps://d26giflyqapd29.cloudfront.net
audContains your OMS project ID
expIs not in the past
import { createRemoteJWKSet, jwtVerify } from 'jose'

const STAGING_ISSUER = 'https://d26giflyqapd29.cloudfront.net'
const STAGING_JWKS = new URL(`${STAGING_ISSUER}/.well-known/jwks.json`)
const jwks = createRemoteJWKSet(STAGING_JWKS)

type VerifiedWalletUser = {
  walletAddress: string
  walletType: 'ethereum'
  walletId: string
  email?: string
}

export async function verifyWalletIdToken(
  idToken: string,
  projectId: string,
): Promise<VerifiedWalletUser> {
  const { payload } = await jwtVerify(idToken, jwks, {
    issuer: STAGING_ISSUER,
    audience: projectId,
  })

  if (payload.wallet_type !== 'ethereum') {
    throw new Error('Unexpected wallet_type claim')
  }

  if (typeof payload.wallet_address !== 'string' || payload.wallet_address.length === 0) {
    throw new Error('Missing wallet_address claim')
  }

  if (typeof payload.sub !== 'string' || payload.sub.length === 0) {
    throw new Error('Missing sub claim')
  }

  return {
    walletAddress: payload.wallet_address,
    walletType: payload.wallet_type,
    walletId: payload.sub,
    email: typeof payload.email === 'string' ? payload.email : undefined,
  }
}

3. Use the wallet claims

After verification succeeds, trust the claims from the verified JWT payload for the current request.
ClaimDescription
wallet_addressUser’s wallet address.
wallet_typeWallet type. This is always ethereum.
subInternal wallet ID.
emailUser’s email address, when available.
app.post('/api/wallet-session', async (req, res) => {
  const idToken = req.body.idToken
  const projectId = process.env.OMS_PROJECT_ID

  if (typeof idToken !== 'string' || !projectId) {
    res.status(400).json({ error: 'Missing token or project ID' })
    return
  }

  try {
    const user = await verifyWalletIdToken(idToken, projectId)

    res.json({
      walletAddress: user.walletAddress,
      walletId: user.walletId,
      email: user.email ?? null,
    })
  } catch {
    res.status(401).json({ error: 'Invalid wallet token' })
  }
})