IntegrateBackends
NestJS integration
Olympus auth in a NestJS application
NestJS uses Passport strategies for auth. We'll wire up a JWT strategy that validates Olympus access tokens via the introspection endpoint or local JWKS.
Install
npm i @nestjs/passport @nestjs/jwt passport passport-jwt jwks-rsaStrategy
// src/auth/jwt.strategy.ts
import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
import { passportJwtSecret } from "jwks-rsa";
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKeyProvider: passportJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 10,
jwksUri: `${process.env.HYDRA_PUBLIC_URL}/.well-known/jwks.json`,
}),
issuer: process.env.HYDRA_PUBLIC_URL,
algorithms: ["RS256"],
});
}
validate(payload: any) {
return { sub: payload.sub, scopes: payload.scp ?? [] };
}
}Module
// src/auth/auth.module.ts
import { Module } from "@nestjs/common";
import { PassportModule } from "@nestjs/passport";
import { JwtStrategy } from "./jwt.strategy";
@Module({
imports: [PassportModule],
providers: [JwtStrategy],
})
export class AuthModule {}Guard
// src/auth/jwt.guard.ts
import { Injectable } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
@Injectable()
export class JwtGuard extends AuthGuard("jwt") {}Use it
import { UseGuards, Controller, Get, Req } from "@nestjs/common";
import { JwtGuard } from "../auth/jwt.guard";
@Controller("orders")
@UseGuards(JwtGuard)
export class OrdersController {
@Get()
list(@Req() req: any) {
return { user: req.user.sub, scopes: req.user.scopes };
}
}Scope decorator
For scope checks:
// src/auth/scopes.decorator.ts
import { SetMetadata } from "@nestjs/common";
export const RequireScopes = (...scopes: string[]) => SetMetadata("scopes", scopes);
// src/auth/scopes.guard.ts
import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
@Injectable()
export class ScopesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(ctx: ExecutionContext) {
const required = this.reflector.get<string[]>("scopes", ctx.getHandler()) ?? [];
const req = ctx.switchToHttp().getRequest();
return required.every(s => req.user?.scopes.includes(s));
}
}@UseGuards(JwtGuard, ScopesGuard)
@RequireScopes("orders:write")
@Delete(":id")
delete(@Param("id") id: string) { /* ... */ }