@schemavaults/auth-server-sdk
v0.32.7
Published
TypeScript SDK for building authenticated endpoints/middlewares for the Auth Server and Resource Servers
Readme
@schemavaults/auth-server-sdk
TypeScript SDK for SchemaVaults Auth Server and Resource Server API backends.
CLI
bunx @schemavaults/auth-server-sdk codegen [options]Generates the auth pages (login, register, logout, authorize, error)
and the auth-provider.tsx into your Next.js App Router project.
Options
--client-output-dir <path>— Custom output directory for the generated clientauth/files. Defaults to<app>/auth(e.g.src/app/authorapp/auth). When set, the path is treated as the auth directory itself, so you can place the generated files inside a Next.js route group:bunx @schemavaults/auth-server-sdk codegen --client-output-dir src/app/\(client\)/authRelative paths are resolved against the current working directory.
--templates-dir <path>— Use a custom codegen templates directory instead of the templates bundled with the SDK.--debug— Enable debug logging.--help,-h— Show help.--version,-v— Show package name and version.
Accepting resource-URL token audiences
Access tokens are normally minted with your API server id as their aud.
A client that requested its token with an RFC 8707 resource URL (MCP
clients do, and so do clients registered through RFC 7591 dynamic client
registration) receives a token whose aud is that URL instead. To accept
such tokens, list the URL(s) your server is known by:
new RouteGuardFactory({ environment, accepted_audiences: ["https://mcp.example.com/mcp"] });
// or, for the middleware:
new SchemaVaultsServerMiddleware({ ..., accepted_audiences: ["https://mcp.example.com/mcp"] });Keysets are still looked up by the API server id; only the aud comparison
changes, and only for tokens whose header names one of the listed URLs
(resolveExpectedTokenAudience()). Tokens with any other aud fail as
before.
Introspecting access tokens (RFC 7662)
Local verification (route guards, decodeJWTsWithKeyManager) proves a token is
authentic and unexpired, but cannot see what happened after it was issued: a
logout, a password reset, a disabled account, the user de-authorizing the client
app, or the app being disconnected from your API server. introspectToken() asks
the auth server, authenticating with your JWKS access key (private_key_jwt: a
fresh single-use JWKS access assertion per call):
import { introspectToken, loadJwksAccessPrivateKey, getAuthServerUrl } from "@schemavaults/auth-server-sdk";
const result = await introspectToken({
auth_server_url: getAuthServerUrl(),
api_server_id: process.env.SCHEMAVAULTS_API_SERVER_ID!,
jwks_access_private_key: await loadJwksAccessPrivateKey(),
token: accessToken,
});
if (result.active) {
result.uid; // platform user id
result.sub; // OIDC subject `<auth_server_app_id>|<uid>`
result.client_id; // the client app the token was issued to
result.scope; // granted scope, if any
}Only access tokens minted for your API server (its id, or one of its RFC 8707
resource URLs, as aud) can come back active; everything else is { active: false }.
A refused request (e.g. a JWKS access key that is not your active one) throws.
Each call is a round trip to the auth server, so reserve it for requests where
revocation must be honoured immediately.
UserData.sub
UserData.sub is the OIDC subject <auth_server_app_id>|<uid> (the value the
id_token, userinfo and introspection report); use uid for the platform user id.
The access tokens themselves still carry the bare uid as sub, so older SDK
versions keep verifying them; userDataSchema upgrades a bare-uuid sub with
SCHEMAVAULTS_AUTH_SERVER_APP_ID (default schemavaults-auth).
Auth resolvers for @schemavaults/openapi-operations
Resource servers that declare their API with
@schemavaults/openapi-operations need credential
resolvers for the SchemaVaults access-token schemes
(schemaVaultsAccessTokenBearerScheme, schemaVaultsAccessTokenCookieScheme(...)).
createSchemaVaultsAuthResolvers() returns them, keyed by scheme name, built on the
same RouteGuardFactory + remote JWKS verification withAuthenticatedApiRouteGuard
uses. @schemavaults/openapi-operations is an optional peer dependency: install it
alongside this package to use the sub-export.
// src/lib/api/app.ts
import { createSchemaVaultsAuthResolvers } from "@schemavaults/auth-server-sdk/openapi-operations";
import { createOperationsAppFactory } from "@schemavaults/openapi-operations";
export const api = createOperationsAppFactory<ApiContext, UserData>({
operations,
authResolvers: createSchemaVaultsAuthResolvers<ApiContext>({
// all optional; environment-derived defaults are read on first request
apiServerId, // default SCHEMAVAULTS_API_SERVER_ID
environment, // default getAppEnvironment()
acceptedAudiences: ["https://mcp.example.com/mcp"], // RFC 8707 resource URLs (see above)
isTokenRevoked, // optional revocation hook
}),
});Per request:
Authorization: Bearer <token>(schemavaults-access-token): a malformed header is 401invalid_requestwithWWW-Authenticate; no header → the next scheme is tried.- The access-token cookie
access_token_<api_server_id>(schemavaults-access-token-cookie): accepts the JSON{ token, exp, ... }value the auth provider writes (skipped onceexphas passed) and a raw JWT. - A token that fails verification is 401
invalid_token; a revoked one (isTokenRevoked) 401token_revoked; a disabled account 403account_disabled. - A missing
SCHEMAVAULTS_AUTH_JWKS_ACCESS_PRIVATE_KEY(or API server id / auth server URL) is 500auth_not_configuredwith a message naming it, never a silent pass. With no credential at all the operations runtime still answers 401 +WWW-Authenticate. - The principal is
{ scheme, user, isAdmin: user.admin === true, scope, getOrganizationRole };getOrganizationRolevalidates the id and asks the auth server (isUserInOrganization) with the JWKS access key.
AccessTokenCookieName(apiServerId) / AccessTokenExpiryCookieName(apiServerId) are
exported from the package index too, so the cookie scheme can be declared without a
direct dependency on @schemavaults/auth-common.
