@authaction/node-sdk
v0.1.0
Published
AuthAction JWT verification SDK for Node.js — Express, Fastify, NestJS, and Apollo GraphQL
Maintainers
Readme
@authaction/node-sdk
JWT verification SDK for Node.js backends. Validates AuthAction access tokens via JWKS — handles key fetching, caching, and rotation automatically.
Works with Express, Fastify, NestJS, and Apollo GraphQL.
Installation
npm install @authaction/node-sdkImport paths
| Path | Framework |
|---|---|
| @authaction/node-sdk | Core verifier (any Node.js app) |
| @authaction/node-sdk/express | Express middleware |
| @authaction/node-sdk/fastify | Fastify plugin / hook |
| @authaction/node-sdk/nestjs | NestJS module + guard |
| @authaction/node-sdk/apollo | Apollo GraphQL context |
Core
import { createVerifier } from '@authaction/node-sdk';
const verifier = createVerifier({
domain: process.env.AUTHACTION_DOMAIN!, // e.g. myapp.eu.authaction.com
audience: process.env.AUTHACTION_AUDIENCE!, // e.g. https://api.myapp.com
});
// Verify a raw token — throws JWTExpired / JWTClaimValidationFailed on failure
const payload = await verifier.verifyToken(token);
// Verify from Authorization header — returns null on missing/invalid, never throws
const payload = await verifier.verifyRequest(req);Express
import express from 'express';
import { createVerifier } from '@authaction/node-sdk';
import { jwtMiddleware, requireAuth } from '@authaction/node-sdk/express';
const app = express();
const verifier = createVerifier({ domain, audience });
// Protect all routes under /api
app.use('/api', jwtMiddleware(verifier));
// Protect a single route
app.get('/protected', requireAuth(verifier), (req, res) => {
res.json({ sub: req.user?.sub });
});
// Optional auth (passes through without token, sets req.user if present)
app.use(jwtMiddleware(verifier, { required: false }));req.user is populated with the decoded JWT payload on success.
Fastify
import Fastify from 'fastify';
import { createVerifier } from '@authaction/node-sdk';
import { jwtPlugin, jwtHook } from '@authaction/node-sdk/fastify';
const app = Fastify();
const verifier = createVerifier({ domain, audience });
// Option A — global plugin (protects all routes)
await app.register(jwtPlugin(verifier));
// Option B — per-route hook
app.get('/protected', { onRequest: jwtHook(verifier) }, (req, reply) => {
reply.send({ sub: (req as any).user?.sub });
});
// Option C — optional auth
app.addHook('onRequest', jwtHook(verifier, { required: false }));NestJS
// app.module.ts
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthActionModule, AuthActionGuard } from '@authaction/node-sdk/nestjs';
@Module({
imports: [
AuthActionModule.forRoot({
domain: process.env.AUTHACTION_DOMAIN!,
audience: process.env.AUTHACTION_AUDIENCE!,
}),
],
providers: [
{ provide: APP_GUARD, useClass: AuthActionGuard }, // protect all routes globally
],
})
export class AppModule {}// messages.controller.ts
import { Controller, Get } from '@nestjs/common';
import { Public, CurrentUser } from '@authaction/node-sdk/nestjs';
import type { TokenPayload } from '@authaction/node-sdk';
@Controller('messages')
export class MessagesController {
@Get('public')
@Public() // opt out of auth
publicMessage() {
return { message: 'Public' };
}
@Get('protected')
protectedMessage(@CurrentUser() user: TokenPayload) {
return { message: 'Protected', sub: user.sub };
}
}Apollo GraphQL
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { createVerifier } from '@authaction/node-sdk';
import { apolloContext, requireUser } from '@authaction/node-sdk/apollo';
const verifier = createVerifier({ domain, audience });
const server = new ApolloServer({ typeDefs, resolvers });
await startStandaloneServer(server, {
context: apolloContext(verifier), // injects { user: TokenPayload | null }
});// In a resolver
const resolvers = {
Query: {
me(_parent, _args, context) {
const user = requireUser(context); // throws UNAUTHENTICATED if user is null
return { sub: user.sub };
},
},
};Token payload
interface TokenPayload {
sub: string; // user or M2M client identifier
iss: string; // issuer
aud: string | string[]; // audience
exp: number; // expiry (Unix seconds)
iat: number; // issued-at (Unix seconds)
scope?: string; // space-separated scopes
[key: string]: unknown; // any additional claims
}Environment variables
AUTHACTION_DOMAIN=your-tenant.eu.authaction.com
AUTHACTION_AUDIENCE=https://api.your-app.comHow JWKS caching works
- Public keys are fetched from
https://<domain>/.well-known/jwks.jsonon first use - Cached in-process with a 1-hour TTL (configurable via
jwksCacheMaxAge) - Automatically re-fetched when an unknown
kidis encountered (key rotation)
License
MIT
