Skip to content

Instantly share code, notes, and snippets.

@codebender828
Last active August 6, 2025 13:25
Show Gist options
  • Save codebender828/7d506e70a7bd97d77d301c1b303da151 to your computer and use it in GitHub Desktop.
Save codebender828/7d506e70a7bd97d77d301c1b303da151 to your computer and use it in GitHub Desktop.
Donut Wallet Service Access Token Verification
bun run verify-access-token.ts
// ============================
Token is valid: {
aid: "cmberb48q023yjo0leuwu63tw",
att: "pat",
sid: "cmd4ibkcy000ljz0my8vpmm2d",
iss: "privy.io",
iat: 1753864509,
aud: "https://auth.privy.io",
sub: "did:privy:cmc7kmnba00palf0lmakg3lge",
exp: 1753868109,
}
results {
verifyPrivyTokenResult: {
aid: "cmberb48q023yjo0leuwu63tw",
att: "pat",
sid: "cmd4ibkcy000ljz0my8vpmm2d",
iss: "privy.io",
iat: 1753864509,
aud: "https://auth.privy.io",
sub: "did:privy:cmc7kmnba00palf0lmakg3lge",
exp: 1753868109,
},
verifyPrivyTokenWithValidationsResult: {
aid: "cmberb48q023yjo0leuwu63tw",
att: "pat",
sid: "cmd4ibkcy000ljz0my8vpmm2d",
iss: "privy.io",
iat: 1753864509,
aud: "https://auth.privy.io",
sub: "did:privy:cmc7kmnba00palf0lmakg3lge",
exp: 1753868109,
},
}
import jwt, { JwtPayload } from "jsonwebtoken";
const publicKey = `<REPLACE_WITH_PUBLIC_KEY_FROM_PRIVY_DASHBOARD>`;
interface PrivyTokenPayload extends JwtPayload {
sub?: string; // Subject (user ID)
iss?: string; // Issuer
aud?: string | string[]; // Audience
exp?: number; // Expiration time
iat?: number; // Issued at
// You can add other Privy-specific claims as needed
}
function verifyPrivyToken(accessToken: string): PrivyTokenPayload | null {
try {
const decoded = jwt.verify(accessToken, publicKey, {
// NOTe: This is important! Privy uses ES256 for signing
algorithms: ["ES256"],
}) as PrivyTokenPayload;
console.log("Token is valid:", decoded);
return decoded;
} catch (error) {
if (error instanceof Error) {
console.error("Token verification failed:", error.message);
}
return null;
}
}
// With additional validations
function verifyPrivyTokenWithValidations(
accessToken: string,
options?: {
audience?: string;
issuer?: string;
}
): PrivyTokenPayload | null {
try {
const verifyOptions: jwt.VerifyOptions = {
algorithms: ["ES256"],
...(options?.audience && { audience: options.audience }),
...(options?.issuer && { issuer: options.issuer }),
};
const decoded = jwt.verify(
accessToken,
publicKey,
verifyOptions
) as PrivyTokenPayload;
if (!decoded.sub) {
throw new Error("Token missing subject (user ID)");
}
return decoded;
} catch (error) {
if (error instanceof jwt.TokenExpiredError) {
console.error("Token has expired");
} else if (error instanceof jwt.JsonWebTokenError) {
console.error("Invalid token:", error.message);
} else if (error instanceof Error) {
console.error("Token verification failed:", error.message);
}
return null;
}
}
// PLEASE REPLACE WUTH YOUR ACCESS TOKEN IN PRODUCTION
const ACCESS_TOKEN = `eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlZwVzdkYmtra1RpTWpWWV9xOFB6Sjd5ZkZ6blVGdWhjaWJLUEFhU3FoM1kifQ.eyJhaWQiOiJjbWJlcmI0OHEwMjN5am8wbGV1d3U2M3R3IiwiYXR0IjoicGF0Iiwic2lkIjoiY21kNGlia2N5MDAwbGp6MG15OHZwbW0yZCIsImlzcyI6InByaXZ5LmlvIiwiaWF0IjoxNzUzODY0NTA5LCJhdWQiOiJodHRwczovL2F1dGgucHJpdnkuaW8iLCJzdWIiOiJkaWQ6cHJpdnk6Y21jN2ttbmJhMDBwYWxmMGxtYWtnM2xnZSIsImV4cCI6MTc1Mzg2ODEwOX0.C68Apx09WrjBf4gwrXpz7P3rI3kRdSmIKsnSPY7yHr9Ekas7bhRMQoBTn00jp64hEfyE8NIZO3P1rIlenhWmhw`;
const verifyPrivyTokenResult = verifyPrivyToken(ACCESS_TOKEN);
const verifyPrivyTokenWithValidationsResult =
verifyPrivyTokenWithValidations(ACCESS_TOKEN);
console.log("results", {
verifyPrivyTokenResult,
verifyPrivyTokenWithValidationsResult,
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment