Authentication
Protect routes with AuthMiddleware, your own token verifier and a cookie, a Bearer header or both.
AuthMiddleware reads a token from the request and asks your verifier whether it is valid. RiserBob does not decide what a token is, so you can use JWTs, session ids or anything else.
Protect a route
import { AuthMiddleware } from 'riser-bob' const auth = new AuthMiddleware(async (token) => { // Return the user for a valid token, or null for an invalid one. const payload = await verifyMyToken(token) return payload ? { id: payload.userId, role: payload.role } : null}) bob.spongGet( '/me', async (spongReq, spongRes) => { return spongRes.json({ user: spongReq.user }) }, auth.middleware)On success the middleware sets spongReq.user and spongReq.auth, then continues the chain.
Where the token comes from
The second constructor argument chooses the source of the token.
new AuthMiddleware(verifier, { mode: 'cookie', // 'cookie' | 'bearer' | 'both' cookieName: 'token', // default: 'token'})| Mode | Reads the token from |
|---|---|
'bearer' | The Authorization: Bearer <token> header only. |
'cookie' | The cookie named cookieName only. |
'both' (default) | The Authorization header when it is present, otherwise the cookie. |
- The
Bearerscheme is matched without regard to case. - In
'both'mode, a request that has anAuthorizationheader is judged by that header alone. If it is not a validBearer <token>, the answer is401and the cookie is not tried. - The cookie is read with a plain
get. If you set it withsetSigned, verify the signature in your own code.
Browser apps usually use mode: 'cookie' with an httpOnly cookie, because JavaScript on the page cannot read it. Mobile apps and other APIs usually use mode: 'bearer'. See JWT and cookies for a complete cookie flow.
What the client sees
| Situation | Response |
|---|---|
No token, or a malformed Authorization header | 401 with { success: false, message: 'Authentication token is missing or invalid.' } |
The verifier returns null or undefined | 401 with { success: false, message: 'Token is invalid or expired.' } |
| The verifier throws | Logged on the server, then handled by the central error handler: an HttpError keeps its status, anything else becomes 500. |
The token verifier
type VerifiedUser = { id: string; [key: string]: unknown } type TokenVerifier = ( token: string) => Promise<VerifiedUser | null> | VerifiedUser | nullWhatever your verifier returns is stored on spongReq.user, so it must have an id. Add anything else you want handlers to read, such as a role. Return null to reject the token.
Read the user in a handler
readonly me: SpongHandler = async (spongReq, spongRes) => { const authUser = spongReq.user if (!authUser) { return spongRes.SpongUnauthorized({ success: false, message: "Not authenticated" }) } return spongRes.SpongOK({ success: true, data: { id: authUser.id } })}The middleware already rejects requests without a user, but TypeScript still sees spongReq.user as optional, so the check keeps the types honest.
In a module
In a generated module the controller owns verifyToken, and the module hands it to AuthMiddleware. See Modules.