JSPM

exguard-cached

1.3.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 40
  • Score
    100M100P100Q107506F
  • License MIT

ExGuard cached user data handling package for backend integration

Package Exports

  • exguard-cached
  • exguard-cached/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (exguard-cached) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

exguard-cached

NestJS backend package that caches user RBAC data (roles, groups, permissions) in Redis and provides guards/decorators for permission checks. Designed to work with the exguard-client frontend package.


Quick Start

npm install exguard-cached
npx exguard-cached setup

This generates:

  • src/redis/redis.module.ts — Redis connection (reads REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, REDIS_DB from .env)
  • src/guard/guard.module.ts — Imports ExGuardCacheModule.forRoot() with your Redis client
  • src/guard/guard.controller.ts — Redis proxy endpoints (/guard/redis/get, /guard/redis/set, /guard/redis/del, etc.)
  • Updates src/app.module.ts to import both modules
  • Updates .env with Redis defaults

Manual setup

1. Redis module

// src/redis/redis.module.ts
import { Module, Global } from '@nestjs/common';
import { createClient } from 'redis';

@Global()
@Module({
  providers: [
    {
      provide: 'REDIS_CLIENT',
      useFactory: async () => {
        const client = createClient({
          socket: {
            host: process.env.REDIS_HOST || 'localhost',
            port: parseInt(process.env.REDIS_PORT || '6379', 10),
          },
          password: process.env.REDIS_PASSWORD || undefined,
          database: parseInt(process.env.REDIS_DB || '0', 10),
        });
        await client.connect();
        return client;
      },
    },
  ],
  exports: ['REDIS_CLIENT'],
})
export class RedisModule {}

2. Guard module

// src/guard/guard.module.ts
import { Module } from '@nestjs/common';
import { ExGuardCacheModule } from 'exguard-cached';
import { GuardController } from './guard.controller';

@Module({
  imports: [
    ExGuardCacheModule.forRoot({
      redisClient: 'REDIS_CLIENT', // NestJS DI token from your Redis module
      keyPrefix: 'guard:',
      defaultTtl: 300,
    }),
  ],
  controllers: [GuardController],
})
export class GuardModule {}

3. Guard controller (Redis proxy)

Create a controller at src/guard/guard.controller.ts — or use the auto-generated one from npx exguard-cached setup:

import { Controller, Get, Post, Body, Query } from '@nestjs/common';
import { Inject } from '@nestjs/common';

@Controller('guard')
export class GuardController {
  constructor(@Inject('REDIS_CLIENT') private redis: any) {}

  @Post('redis/set')
  async redisSet(@Body() body: { key: string; value: unknown; ttl?: number }) {
    const ttl = body.ttl ?? 300;
    await this.redis.setEx(`guard:${body.key}`, ttl, JSON.stringify(body.value));
    return { success: true };
  }

  @Get('redis/get')
  async redisGet(@Query('key') key: string) {
    const val = await this.redis.get(`guard:${key}`);
    if (val === null) return { success: true, value: null };
    try {
      return { success: true, value: JSON.parse(val) };
    } catch {
      return { success: true, value: val };
    }
  }

  @Post('redis/del')
  async redisDel(@Body() body: { key: string }) {
    await this.redis.del(`guard:${body.key}`);
    return { success: true };
  }
}

4. App module

// src/app.module.ts
import { Module } from '@nestjs/common';
import { RedisModule } from './redis/redis.module';
import { GuardModule } from './guard/guard.module';

@Module({
  imports: [RedisModule, GuardModule],
})
export class AppModule {}

Caching User Data

When a user logs in or their roles/permissions change, store their data in Redis:

import { CachedUserService } from 'exguard-cached';

@Injectable()
export class AuthService {
  constructor(private cache: CachedUserService) {}

  async onLogin(cognitoSubId: string, userData: CachedUserData) {
    await this.cache.setCachedUser(cognitoSubId, userData);
  }

  async onPermissionsChanged(cognitoSubId: string) {
    await this.cache.invalidateCachedUser(cognitoSubId);
  }
}

Protecting Endpoints

Permission guard

import { RequireCachedUserPermission, CachedUser } from 'exguard-cached';
import type { CachedUserData } from 'exguard-cached';

@Controller('users')
export class UsersController {
  @Get()
  @RequireCachedUserPermission('exGUARD', 'user_groups:view_group')
  async getUsers(@CachedUser() user: CachedUserData) {
    return { roles: user.roles, groups: user.groups };
  }
}

The guard requires request.user.cognitoSubId to be set (e.g. by a JWT auth guard).


CachedUserService API

Method Description
getCachedUser(cognitoSubId) Get full CachedUserData from Redis
setCachedUser(cognitoSubId, data, ttl?) Store user data in Redis
invalidateCachedUser(cognitoSubId) Delete user from cache
hasPermission(cognitoSubId, module, action) Check if user has a specific permission
getUserPermissions(cognitoSubId) Return all permission strings
getUserRoles(cognitoSubId) Return all role names
getUserGroups(cognitoSubId) Return all group names
getUserModules(cognitoSubId) Return all module objects
getRaw(key) Read any key from Redis (prefixed)
setRaw(key, value, ttl?) Write any key to Redis (prefixed)
delRaw(key) Delete any key from Redis (prefixed)

CachedUserData Structure

interface CachedUserData {
  user: {
    id: string;
    cognitoSubId: string;
    username: string;
    email: string;
    givenName: string;
    familyName: string;
    employeeNumber: string;
    regionId: string | null;
    fieldOffice: { id: string; code: string; name: string } | null;
  } | null;
  groups: string[];
  roles: string[];
  modules: Array<{ key: string; name: string; permissions: string[] }>;
  fieldOffices: string[];
  permissions: string[];   // flattened: ["exGUARD:user_groups:view_group", ...]
  cacheInfo: { userId: string; cognitoSubId: string; cacheKeys: { access: string; permissions: string } };
}

CacheUtils (static helpers)

Method Description
hasPermission(user, module, action) Check permission against cached data
hasAnyPermission(user, pairs[]) Check if user has any of the given permissions
hasAllPermissions(user, pairs[]) Check if user has all given permissions
getPermissionsByModule(user, moduleKey) Filter permissions by module prefix
hasRole(user, role) Check if user has a role
isInGroup(user, group) Check if user is in a group
getFieldOfficeId(user) Get field office ID
validate(data) Type guard for CachedUserData

Environment Variables

REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0

Dependencies

  • redis ^4.6.0
  • @nestjs/common ^10.0.0 (peer)
  • @nestjs/core ^10.0.0 (peer)