Manual Verification
Verify a token string directly - useful for WebSockets, message queues, or any flow outside Express middleware.
auther.verify(token)
Accepts a raw token string and returns a typed result object. Use this when you have the token but are not in a standard Express request/response cycle.
const result = await auther.verify(rawToken);
if (result.ok) {
console.log(result.user.email);
console.log(result.user.id);
console.log(result.user.name); // 'Jane Doe' - or null if never given
} else {
console.error(result.message); // e.g. 'Invalid or expired token'
}Offline verification
verify() asks Auther about every token. auther.verifyLocal() checks the signature against our published public keys instead, so there is no round trip and it keeps working if Auther is unreachable. Needs @auther-sdk/node 1.3.0 or later.
// No network call after the first request
const result = await auther.verifyLocal(rawToken);
if (result.ok) {
console.log(result.user.id); // the token's subject
console.log(result.user.projectId);
}Choose on freshness, not speed
verify() sees a revocation immediately. Use local checks on high-volume read paths, and keep verify() anywhere "this user was just banned" has to take effect now.It also returns only what the token itself asserts, so there is no email or emailVerified on the result: those live on the account, and returning a cached copy would be stale data dressed up as fresh. The first call falls back to a normal API verification to learn your project id, then every call after it is offline.
Verifying without our SDK
Tokens are signed with RS256 and our public keys are published at https://oautherbackend.ziloris.com/.well-known/jwks.json, so any standard JWT library can verify them. Match the token's kid header to a key in that document.
Two things to get right. Pin the algorithm to RS256 rather than trusting the token's own alg, or someone can sign HS256 with the public key you just downloaded and your verifier will accept it. And check that the pid claim matches your project id: every project's tokens are signed by the same keys, so a signature check on its own would accept a token minted for somebody else's project.
WebSocket example
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', async (ws, req) => {
// Token passed as query param: ws://host?token=...
const token = new URL(req.url!, 'http://x').searchParams.get('token');
if (!token) { ws.close(1008, 'Unauthorized'); return; }
const result = await auther.verify(token);
if (!result.ok) { ws.close(1008, result.message); return; }
// Authenticated - attach user to socket
(ws as any).user = result.user;
ws.send(JSON.stringify({ type: 'connected', userId: result.user.id }));
});Return type
type VerifyResult =
| { ok: true; user: AutherUser }
| { ok: false; status: number; message: string };Same verification, different interface
auther.verify() calls the exact same Auther backend endpoint as auther.protect(). The only difference is that you handle the result yourself instead of the middleware doing it.