@reltio/auth
v1.4.1
Published
Reltio Auth — BFF authentication and OAuth orchestration for Reltio applications. Express and Next.js App Router adapters over a framework-agnostic Web Fetch API core.
Maintainers
Keywords
Readme
@reltio/auth
@reltio/auth is the BFF authentication package for Reltio applications — Express and Next.js App Router adapters over a framework-agnostic Web Fetch API core.
It implements the Reltio OAuth Authorization Code flow used by every customer-facing Reltio application: login redirect to the central Reltio Login Page, callback exchange, refresh token rotation, and token introspection against /oauth/checkToken. It is the modern replacement for the legacy auth-middleware git-installed package.
- Single npm package, dual runtime (ESM + CJS), Node ≥20.
- First-class TypeScript types, full
.d.tsshipped. - Native
fetchand Web Crypto — nonode-fetch, nonode:crypto. - Same
ssoRedirectcallback signature in Express and Next.js — write the callback once, deploy it on any Web Fetch runtime.
Installation
npm install @reltio/authExpress applications also need express itself as a peer dependency; Next.js App Router applications need next >=13. Both peer dependencies are marked optional — you install only the one your application uses.
Usage — Next.js App Router (canonical)
Create a catch-all route file at app/auth/[...auth]/route.ts:
import { createNextAuth } from "@reltio/auth/next";
export const { GET, POST } = createNextAuth({
oauthPath: process.env.OAUTH_PATH!,
loginPath: process.env.LOGIN_PATH!,
clientId: process.env.CLIENT_ID!,
clientSecret: process.env.CLIENT_SECRET!,
}).handlers;That's all. The five endpoints (/auth/login, /auth/logout, /auth/callback, /auth/refreshToken, /auth/checkToken) are now live.
The [...auth] catch-all parameter name is arbitrary — @reltio/auth dispatches by the last URL segment, not by Next.js's params. Name it [...slug], [...path], or anything else; the only requirement is that the LAST URL segment matches one of the five reserved names above.
Usage — Express (legacy / hybrid Next.js + custom server)
import express from "express";
import { createExpressAuth } from "@reltio/auth/express";
const app = express();
app.use(
"/auth",
createExpressAuth({
oauthPath: process.env.OAUTH_PATH!,
loginPath: process.env.LOGIN_PATH!,
clientId: process.env.CLIENT_ID!,
clientSecret: process.env.CLIENT_SECRET!,
}),
);The router is mount-point agnostic — /auth, /api/auth, or any other path works the same.
Path-based tenant routing
GET /login and GET /logout (or /auth/login / /api/auth/login depending on your mount path) accept two optional query parameters — ?tenant= and ?returnTo= — that take precedence over the Referer header. This lets consumers whose tenant lives in the URL path (e.g. /ui/<tenant>/...) drive the auth flow without relying on the browser's Referer:
// HUB UI pattern — tenant in the URL path, mounted at /
const returnTo = `${window.location.origin}/ui/${tenant}/dashboard`;
const href = `/login?tenant=${tenant}&returnTo=${encodeURIComponent(returnTo)}`;
<a href={href}>Sign in</a>See the Setup → Express or Setup → Next.js App Router guide for a full worked example using HUB UI's path shape.
Subpath exports
@reltio/auth has no bare entry point — every import goes through a named subpath. This matches the platform convention enforced by @reltio/design.
| Subpath | What's in it |
|---|---|
| @reltio/auth/types | TypeScript type declarations: AuthConfig, SsoRedirect, SsoRedirectContext, TokenResponse, CheckTokenResponse. Use these to type your callback and config objects. No runtime code. |
| @reltio/auth/express | createExpressAuth(config) — Express Router factory. |
| @reltio/auth/next | createNextAuth(config) — returns { handlers: { GET, POST } } for Next.js App Router. |
| @reltio/auth/utils | The full set of framework-agnostic helpers the router itself uses. Token readers (getAccessToken, getRefreshToken, getBasicToken), cookie plumbing (parseCookies, serializeCookie, clearCookie, defaultCookieOptions, the CookieOptions type, and the ACCESS_TOKEN_COOKIE / REFRESH_TOKEN_COOKIE / STATE_COOKIE / AUTH_URL_COOKIE name constants), CSRF-state primitives (generateState, validateState), the request-shape adapter (readHeader, AnyRequest), and the redirect-param resolver (resolveRedirectParams, upgradeToHttps, RedirectParams). All accept Express Request, Next.js NextRequest, or Web Request uniformly where applicable. The dynamic Auth-server routing resolver is not here — it lives on the adapter return value as createExpressAuth(config).resolveAuthPath / createNextAuth(config).resolveAuthPath so it shares the router's once-derived HMAC key. |
Always use a subpath.
import x from "@reltio/auth"(no subpath) deliberately fails — there is nomain/exportstarget for the bare package name.
Customising the post-login redirect (ssoRedirect)
The optional ssoRedirect callback runs at the end of a successful /callback exchange. It receives a context object with the tokens and the requested redirect URL, and returns a Web Response:
import type { SsoRedirect } from "@reltio/auth/types";
export const ssoRedirect: SsoRedirect = ({ redirectUrl, request }) => {
const url = new URL(redirectUrl);
const tenant = new URL(request.url).searchParams.get("tenant");
if (tenant) {
url.searchParams.set("tenant", tenant);
}
return Response.redirect(url.href, 302);
};The same signature works in both Express and Next.js — no framework-specific adapter for the callback shape.
Configuration
type AuthConfig = {
/** URL of the Reltio OAuth server, e.g. `https://auth-stg.reltio.com/oauth`. */
oauthPath: string;
/** URL of the Reltio Login Page, e.g. `https://login-stg.reltio.com`. */
loginPath: string;
/** OAuth client id registered with the Reltio OAuth server. */
clientId: string;
/** OAuth client secret registered with the Reltio OAuth server. */
clientSecret: string;
/** Post-callback hook returning a Web `Response`. */
ssoRedirect?: SsoRedirect;
/** Set cookies with the `Secure` flag and force `https` in redirect URLs. Default `true`. */
secure?: boolean;
/** Append `notenant=true` to the Login Page URL. Default `false`. */
notenant?: boolean;
};All four required keys (oauthPath, loginPath, clientId, clientSecret) are enforced at compile time by TypeScript. Consumers reading config from environment variables, JSON files, or other untyped sources are responsible for their own validation at the boundary.
Reading the access token in your own routes
Need the access token in a BFF endpoint that proxies to Reltio API? Read it explicitly — never let middleware mutate the request:
import { getAccessToken } from "@reltio/auth/utils";
app.get("/api/profile", async (req, res, next) => {
const token = getAccessToken(req);
if (!token) {
res.sendStatus(401);
return;
}
const profile = await fetch("https://reltio.com/api/.../profile", {
headers: { Authorization: `Bearer ${token}` },
}).then((r) => r.json());
res.json(profile);
});getAccessToken accepts Express Request, Next.js NextRequest, or Web Request uniformly, reads from Authorization: Bearer first then the access_token cookie, and never mutates the request argument.
Seeding the CSRF state cookie from a custom pre-login handler
A custom pre-login endpoint (custom error page, tenant picker, MFA challenge) sometimes needs to redirect the user to the Reltio Login Page directly while still letting the packaged /callback validate the CSRF state parameter. To do that the handler must set the same state cookie the packaged /login would have set.
Three primitives from @reltio/auth/utils are the single source of truth for this contract — consumers MUST NOT replicate the cookie name, the cookie options, or the state token format inline:
import {
defaultCookieOptions,
generateState,
STATE_COOKIE,
} from "@reltio/auth/utils";
app.get("/error", (req, res) => {
const state = generateState();
res.cookie(
STATE_COOKIE,
state,
defaultCookieOptions(process.env.NODE_ENV === "production"),
);
const url = new URL("https://login.reltio.com/");
url.searchParams.set("state", state);
url.searchParams.set("callbackUrl", "https://app.example.com/auth/callback");
res.redirect(url.toString());
});generateState()returns a fresh CSRF state token in whatever format@reltio/authcurrently uses (today a v4 UUID; the format is an internal detail).STATE_COOKIEis the cookie name the packaged/callbackreads.defaultCookieOptions(secure)returns the full option vector applied at set time so the cookie matches what the packaged router would have produced. Passsecure: truein production andsecure: falseonly when serving over plain HTTP in local development.
The remaining cookie and state helpers (serializeCookie, clearCookie, parseCookies, validateState, ACCESS_TOKEN_COOKIE, REFRESH_TOKEN_COOKIE) are also exported from @reltio/auth/utils for adjacent BFF use cases — for example reading and stripping the access_token cookie before proxying a request upstream. They are part of the supported public contract; use them instead of carrying the magic-string "access_token" around.
Storybook documentation
- Setup → Express — full walkthrough including config object, router mount, and error handling.
- Setup → Next.js App Router — full walkthrough for
app/auth/[...auth]/route.ts. - Dynamic OAuth Routing — how the per-session
reltio_aurlcookie routes/checkTokenand/refreshTokento the right Auth Server cluster, and how to use the adapter'sresolveAuthPathin apps that bypass the BFF. - Migration → From auth-middleware — import-path mapping, before/after code, breaking changes.
License
Licensed under the Apache License, Version 2.0. See NOTICE for attribution.
