Refresh Tokens
AuthCore 0.10 introduces refresh tokens with rotation and server-side revocation. Use them to keep JWTs short-lived (15m) while still giving users long sessions (30d default), and to gain the ability to revoke a compromised session before the JWT expires.
How it works
- Login / register / accept-invitation return
{ user, token, refreshToken }. The JWT is the short-lived access token; the refresh token is a long-lived opaque secret. - The refresh token is stored in the DB as a SHA-256 hash (same pattern as the other AuthCore tokens). The raw value is shown to the user once and never again.
POST /auth/refreshvalidates the refresh token, deletes the row, and issues a fresh JWT + a new refresh token. This is "rotation" — using a refresh token invalidates it. Stolen refresh tokens become useless the next time the legitimate user refreshes.POST /auth/revokedeletes a single refresh token (idempotent).auth.revokeAll(userId)deletes every refresh token for a user — "log out everywhere". Useful after a password change or a security incident.
Recommended defaults
ts
const auth = createAuth({
db: prismaAdapter(prisma),
session: {
strategy: 'jwt',
secret: process.env.AUTH_SECRET!,
expiresIn: '15m', // short-lived JWT
refreshExpiresIn: '30d', // long-lived refresh (default)
},
})The client SDKs (@authcore/core-web, @authcore/react) automatically call /refresh when a request returns 401, retry, and surface failure as isAuthenticated: false. No app code needed.
API surface
Server (@authcore/core)
ts
auth.refresh(rawRefreshToken) // → { user, token, refreshToken }; throws 401 on invalid
auth.revoke(rawRefreshToken) // idempotent
auth.revokeAll(userId) // log out everywhereHTTP routes (added to all three framework adapters)
| Method | Path | Body | Description |
|---|---|---|---|
| POST | /auth/refresh | { refreshToken } (or cookie) | Returns { user, token, refreshToken }; sets cookies if useCookies |
| POST | /auth/revoke | { refreshToken } (or cookie) | Revoke a single token; idempotent |
Client (@authcore/react)
tsx
const { refresh, revokeSession } = useAuth()
await refresh() // explicit refresh (the client also auto-refreshes on 401)
await revokeSession() // server-side revoke + clear local stateCookie mode vs api mode
- api mode (Bearer): the refresh token lives in
localStorageunder${storageKey}_refreshwhenpersistSession: true. - cookie mode: the refresh token is set as an httpOnly cookie
${cookieName}_refreshand the browser carries it automatically. The server reads it fromreq.cookies.
When to revoke all sessions
- After a successful password reset (
callbacks.onPasswordResetis a good place to callauth.revokeAll(user.id)). - After an admin-flagged compromise.
- When a user actively requests "log me out everywhere" from your UI.
Token cleanup
Expired refresh tokens are kept until db.deleteExpiredTokens() is called. Schedule it as a cron / background job. The Prisma adapter implementation is deleteMany({ where: { expiresAt: { lt: new Date() } } }).