Verisco
Documentation

VERISCO Shield Integrate

Read the signed-in user

Gateway modeConnector mode

After sign-in your application receives a signed statement of who the user is. Verify the signature, then trust what it says.

What arrives with each request#

Name Contains
Cookie verisco_session The signed assertion. Set by the gateway on your hostname.
Header X-Verisco-Claims The same signed assertion, added by the gateway to every forwarded request.
Header X-Verisco-User The user's identifier, as plain text. A convenience for logs.
Header X-Verisco-Verified 1 when the gateway validated the session.

Verify before you trust

The cookie and the claims header carry the same RS256 token. Verify its signature against your tenant's public key and reject an expired one. Never act on the plain-text headers alone: a header is only as trustworthy as the network path it arrived on.

The claims#

JSON
{
  "user": {
    "id": "1042",
    "email": "amina@example.com",
    "roles": ["manager"]
  },
  "iat": 1790000000,
  "exp": 1790028800
}
Claim Meaning
exp Expires at (epoch seconds) โ€” the backend MUST reject an expired assertion
iat Issued at (epoch seconds)
user.email Email address, when the tenant's schema has one
user.id Stable identifier from the tenant's own user table
user.roles List of role names, sourced from the tenant's role column

user also carries the other columns of the user's row in your own table, except the password column. The token also carries iss and sub, and has no aud claim, so a verifier does not need to be given an audience.

Get your public key#

Shell
verisco-analyzer download-key --api-key <your analyzer key> --output verisco_public.pem

Or without the CLI:

Shell
curl -fsS "https://your-verisco-host/api/v1/public-key?api_key=<your analyzer key>" -o verisco_public.pem

Verify it#

Express
const { verisco, protect } = require('@verisco/verisco-middleware');

// Mount before your routes. Populates req.user from the
// verisco_session cookie, verified with your RS256 public key.
app.use(verisco({ publicKeyFile: './config/verisco_public.pem' }));

// Require a signed-in user on a route:
app.get('/dashboard', protect(), (req, res) => res.json(req.user));
Express (ESM)
import { verisco, protect } from '@verisco/verisco-middleware';

// Mount before your routes. Populates req.user from the
// verisco_session cookie, verified with your RS256 public key.
app.use(verisco({ publicKeyFile: './config/verisco_public.pem' }));

// Require a signed-in user on a route:
app.get('/dashboard', protect(), (req, res) => res.json(req.user));
Fastify
const verisco = require('@verisco/verisco-middleware');

// Register before your routes. Populates request.user from the
// verisco_session cookie, verified with your RS256 public key.
app.register(verisco.fastify, { publicKeyFile: './config/verisco_public.pem' });

// Require a signed-in user on a route:
app.get('/dashboard', { onRequest: [verisco.fastifyProtect] },
  async (request) => request.user);
Python
import jwt
from fastapi import FastAPI, HTTPException, Request

app = FastAPI()
PUBLIC_KEY = open("./config/verisco_public.pem").read()


def current_user(request: Request) -> dict:
    token = request.headers.get("X-Verisco-Claims")
    if not token:
        raise HTTPException(status_code=401)
    try:
        claims = jwt.decode(token, PUBLIC_KEY, algorithms=["RS256"])
    except jwt.PyJWTError:
        raise HTTPException(status_code=401)
    return claims["user"]


@app.get("/dashboard")
def dashboard(request: Request):
    return current_user(request)
PHP
<?php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

$publicKey = file_get_contents(__DIR__ . '/config/verisco_public.pem');
$token = $_SERVER['HTTP_X_VERISCO_CLAIMS'] ?? '';

try {
    $claims = JWT::decode($token, new Key($publicKey, 'RS256'));
    $user = $claims->user;
} catch (Throwable $e) {
    http_response_code(401);
    exit;
}
Go
func currentUser(r *http.Request, key *rsa.PublicKey) (map[string]any, error) {
	token, err := jwt.Parse(r.Header.Get("X-Verisco-Claims"),
		func(t *jwt.Token) (any, error) { return key, nil },
		jwt.WithValidMethods([]string{"RS256"}))
	if err != nil || !token.Valid {
		return nil, errors.New("unauthorised")
	}
	claims := token.Claims.(jwt.MapClaims)
	return claims["user"].(map[string]any), nil
}

The Node middleware reads the cookie; the other examples read the header. Either is correct, because both carry the same token.

Middleware options#

Option Meaning
publicKey The public key itself, as a PEM string. Use it when the key comes from an environment variable.
publicKeyFile Path to your public key file.

The package exports fastify, fastifyProtect, protect, verisco.

Requiring a signed-in user#

protect() redirects a request with no verified user to /verisco/login, with returnTo set to the page they asked for, so they land back on it after signing in.

Roles#

user.roles comes from the role column you mapped during onboarding. Map it to your own permissions in your application:

JavaScript
function requireRole(role) {
  return (req, res, next) =>
    req.user && req.user.roles.includes(role) ? next() : res.status(403).end();
}

app.get('/admin', protect(), requireRole('admin'), adminHandler);

Key rotation#

When your tenant's signing key is rotated, download the new public key and deploy it. The token's header carries a kid, so a verifier that holds both keys can pick the right one during the changeover.

In SDK mode#

None of this applies. Your application is the sign-in client and the middleware sets req.user from its own session. See SDK mode.

Something missing or wrong on this page? Tell us and we will fix it.

↑↓ to move Enter to open Esc to close