This guide continues from Add sessions and adds the blacklist feature.
Session revocation invalidates refresh tokens, but existing access tokens remain valid until they expire. The blacklist feature closes that gap by rejecting access tokens whose session has been revoked.
When a session is revoked, its session id is added to the blacklist using the configured access-token lifetime as the TTL. Access-token protected requests then perform one lightweight Redis lookup.
By the end of this guide, signing out or revoking a session will also prevent the related access token from accessing protected routes.
Prerequisites
Section titled “Prerequisites”- A project completed from Add sessions
- A running Redis instance
Add the blacklist feature
Section titled “Add the blacklist feature”Add the feature
Run the CLI command:
brkpt auth add blacklistThe CLI adds a new features/blacklist/ folder.
Implement BlacklistAdapter
The blacklist adapter only needs to store revoked session ids and check whether a session id is blacklisted.
Create blacklist.adapter.ts and implement the Redis-backed blacklist adapter:
Directorysrc/
Directorybrkpt-auth/
Directoryadapters/
- blacklist.adapter.ts
import { Inject, Injectable } from '@nestjs/common';import { type RedisClientType } from 'redis';
import { BlacklistPort } from '../features/blacklist/blacklist.port';
@Injectable()export class BlacklistAdapter implements BlacklistPort { constructor( @Inject('REDIS_CLIENT') private readonly redis: RedisClientType, ) {}
private key(sessionId: string) { return `blacklist:${sessionId}`; }
async add(sessionId: string, ttlMs: number): Promise<void> { await this.redis.set(this.key(sessionId), '1', { expiration: { type: 'PX', value: ttlMs }, }); }
async exists(sessionId: string): Promise<boolean> { return !!(await this.redis.exists(this.key(sessionId))); }}Register the feature
Update features.ts and pass BlacklistAdapter to blacklistFeature:
import { BlacklistAdapter } from './adapters/blacklist.adapter';import { CoreAdapter } from './adapters/core.adapter';import { CredentialsAdapter } from './adapters/credentials.adapter';import { SessionAdapter } from './adapters/session.adapter';import { FeatureConfig } from './common/interfaces';import { blacklistFeature } from './features/blacklist/blacklist.feature';import { coreFeature } from './features/core/core.feature';import { credentialsFeature } from './features/credentials/credentials.feature';import { sessionFeature } from './features/session/session.feature';
export const features: FeatureConfig[] = [ coreFeature(CoreAdapter), blacklistFeature(BlacklistAdapter), credentialsFeature(CredentialsAdapter), sessionFeature(SessionAdapter),];BlacklistAdapter also depends on REDIS_CLIENT. If you completed Add sessions, RedisModule is already imported by BrkptAuthModule, so no module import is needed here.
Run and verify
Section titled “Run and verify”Start the application:
pnpm start:devnpm run start:devyarn start:devUse an existing account from Get started, or create a new one with /auth/sign-up. Sign in and keep the returned access token.
Sign out
Call /auth/sign-out with that access token:
POST /auth/sign-outAuthorization: Bearer <access-token>Reuse the access token
Call /auth/me with the same access token:
GET /auth/meAuthorization: Bearer <access-token>The request should be rejected because the access token belongs to a blacklisted session id.