@dofe/sso-nestjs
v0.1.72
Published
NestJS integration for sso.dofe.ai internal service APIs.
Readme
@dofe/sso-nestjs
NestJS integration for sso.dofe.ai internal service APIs.
Install
pnpm add @dofe/sso-nestjs @dofe/sso-node @nestjs/axiosUsage
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { SsoClientModule } from '@dofe/sso-nestjs';
@Module({
imports: [
SsoClientModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
baseUrl: config.getOrThrow('SSO_INTERNAL_API_URL'),
internalSecret: config.getOrThrow('INTERNAL_API_SECRET'),
serviceName: 'agents.dofe.ai',
}),
}),
],
})
export class AppModule {}import { Injectable } from '@nestjs/common';
import { SsoClientService } from '@dofe/sso-nestjs';
@Injectable()
export class ExampleService {
constructor(private readonly sso: SsoClientService) {}
async loadUser(userId: string) {
return this.sso.getUser(userId);
}
}For low-level access, use the underlying @dofe/sso-node client:
await this.sso.client.users.get(userId);
await this.sso.client.teams.getMembers(teamId);
await this.sso.client.permissions.check({ userId, teamId, permission });For migration from existing local SSO clients, SsoClientService also exposes flat helper methods:
await this.sso.getUser(userId);
await this.sso.getUserTeams(userId);
await this.sso.checkPermission(userId, permission, teamId);
await this.sso.getTeamMembers(teamId);
await this.sso.getUserTeamRole(teamId, userId);
await this.sso.getTenantMembers(tenantId);
await this.sso.getUserTenantRole(tenantId, userId);For permission checks, prefer SsoPermissionService over hand-written HTTP
clients in consuming projects:
import { Injectable } from '@nestjs/common';
import { SsoPermissionService } from '@dofe/sso-nestjs';
@Injectable()
export class PermissionService {
constructor(private readonly ssoPermissions: SsoPermissionService) {}
checkLoopPermission(userId: string, action: string, teamId?: string) {
return this.ssoPermissions.checkModulePermission(userId, 'vibecoding', 'loops', action, teamId);
}
}AuthGuard base
Consumer AuthGuards extend DofeSsoAuthGuardBase, delegating the standard
SSO token-verify / blacklist / local-user / admin flow to the SDK and keeping
only project-specific logic in SsoAuthGuardHooks + a subclass.
import { Injectable } from '@nestjs/common';
import {
DofeSsoAuthGuardBase,
type SsoAuthGuardHooks,
type SsoAuthenticatedRequest,
} from '@dofe/sso-nestjs';
@Injectable()
export class MyAuthHooks implements SsoAuthGuardHooks {
async resolveLocalUser(ssoSub, claims) {
/* find/create local user */
}
resolveIsAdmin(localUser, claims) {
return localUser.isAdmin;
}
async isBlacklisted?(token) {
/* optional token blacklist */
}
async shouldBypass?(req, ctx) {
/* optional: health/internal bypass */
}
}
@Injectable()
export class AuthGuard extends DofeSsoAuthGuardBase {
/* project-specific checks only */
}Dev-only bypass (e.g.
MODE_USER_ID) is intentionally NOT in the base — implement it in your subclass orshouldBypasshook.
OIDC Relying Party
SsoOidcRelyingPartyModule + SsoOidcRelyingPartyService own the PKCE
authorize / callback / exchange / refresh / logout flow. Production must
inject a Redis-backed state store (the default InMemoryStateStore loses
state on restart and does not share across instances):
import {
SsoOidcRelyingPartyModule,
OIDC_RP_STATE_STORE,
createRedisOidcStateStore,
} from '@dofe/sso-nestjs';
SsoOidcRelyingPartyModule.forRootAsync({
inject: [ConfigService, RedisService],
useFactory: (config, redis) => ({
clientId: config.getOrThrow('SSO_CLIENT_ID'),
clientSecret: config.getOrThrow('SSO_CLIENT_SECRET'),
issuerUrl: config.getOrThrow('SSO_ISSUER'),
internalIssuerUrl: config.get('SSO_INTERNAL_API_URL'),
redirectPath: '/auth/oidc/callback',
successPath: '/auth/oidc/success',
serviceName: 'agents.dofe.ai',
}),
stateStore: {
provide: OIDC_RP_STATE_STORE,
inject: [RedisService],
useFactory: (redis) => createRedisOidcStateStore(redis, { keyPrefix: 'dofe:oidc:' }),
},
});createRedisOidcStateStore is driver-agnostic (duck-typed ioredis surface) —
the SDK does NOT depend on any redis driver; pass your app's existing client.
OIDC helpers (DOFE_RF_COOKIE, classifyRefreshError, resolveOidcUrls,
isSecureCookieRequired, getCookieDomain, getDefaultCookieOptions) are
framework-free and can be imported on their own.
Boundary
This package wraps @dofe/sso-node with a Nest module and HttpService fetcher. It does not include Redis event subscribers, Prisma, migrations, seeds, or database connection logic.
