Token Verification & Refresh
How to verify access_token, rotate via refresh_token, and handle expiry.
Token types
| Token | TTL | Format | Renewal |
|---|---|---|---|
access_token | 60 min | JWT (ES256-signed) | Reissue via refresh_token |
refresh_token | 30 days | Opaque (v1.<random>) | Rotated on every use |
3 ways to verify access_token (pick one)
1. JWKS signature verification (recommended, server-side)
Fastest and most idiomatic. Fetch the public key from the JWKS endpoint and verify the ES256 signature.
GET
https://oefybpntdguqyjrhurng.supabase.co/auth/v1/.well-known/jwks.jsonimport { jwtVerify, createRemoteJWKSet } from "jose";
const JWKS = createRemoteJWKSet(
new URL("https://oefybpntdguqyjrhurng.supabase.co/auth/v1/.well-known/jwks.json"),
);
export async function verify(token: string) {
const { payload } = await jwtVerify(token, JWKS, {
algorithms: ["ES256"],
});
return payload as {
sub: string; // user_id
email?: string;
exp: number;
iat: number;
};
}2. /auth/verify endpoint (simple but adds latency)
If integrating JWKS is too heavy, delegate the verification to CP-Auth.
POST
https://auth.cp-platform.com/auth/verifycurl -X POST https://auth.cp-platform.com/auth/verify \
-H "Content-Type: application/json" \
-d '{"access_token": "eyJhbGc..."}'{
"valid": true,
"user_id": "uuid",
"email": "user@example.com",
"exp": 1741200000
}3. Decode only (no verification — never do this)
JSON.parse(atob(token.split('.')[1])) does not verify the signature. Use only for debugging.
Refreshing with refresh_token
When the access_token is (or is about to) expire, swap it for a new pair using the refresh_token.
POST
https://auth.cp-platform.com/auth/refreshexport async function POST(req: Request) {
const cookies = req.headers.get("cookie") ?? "";
const rt = cookies.match(/refresh_token=([^;]+)/)?.[1];
if (!rt) return new Response(null, { status: 401 });
const res = await fetch("https://auth.cp-platform.com/auth/refresh", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refresh_token: rt }),
});
if (!res.ok) return new Response(null, { status: 401 });
const tokens = await res.json();
const response = Response.json({ ok: true });
response.headers.append(
"Set-Cookie",
`access_token=${tokens.access_token}; HttpOnly; Secure; SameSite=Lax; Max-Age=3600; Path=/`,
);
response.headers.append(
"Set-Cookie",
`refresh_token=${tokens.refresh_token}; HttpOnly; Secure; SameSite=Lax; Max-Age=2592000; Path=/`,
);
return response;
}Logout (blacklist)
To invalidate a token immediately, add it to the blacklist. TTL is 24 hours.
POST
https://auth.cp-platform.com/auth/logoutcurl -X POST https://auth.cp-platform.com/auth/logout \
-H "Authorization: Bearer <access_token>"After this, also clear the access_token / refresh_token cookies on your own domain.
Fetching user info
Look up user metadata with a verified token.
GET
https://auth.cp-platform.com/auth/mecurl https://auth.cp-platform.com/auth/me \
-H "Authorization: Bearer <access_token>"{
"user_id": "uuid",
"email": "user@example.com",
"name": "Hong Gildong",
"is_admin": false
}