Skip to content

scryan7371/sdr-security

Repository files navigation

sdr-security

Reusable auth/security capability for API and app clients.

Surfaces

  • api: shared auth types and input validation helpers.
  • app: typed client for auth endpoints.

API Integration

Use shared helpers/types in your API controllers/services where useful:

  • sanitizeEmail
  • isValidEmail
  • isStrongPassword
  • AuthResponse, RegisterResponse, SafeUser
  • notifyAdminsOnEmailVerified
  • notifyUserOnAdminApproval

Nest Integration

Import the Nest surface from @scryan7371/sdr-security/nest.

import { Module } from "@nestjs/common";
import {
  SecurityWorkflowsModule,
  SECURITY_WORKFLOW_NOTIFIER,
} from "@scryan7371/sdr-security/nest";
import { EmailService } from "./notifications/email.service";

@Module({
  imports: [
    SecurityWorkflowsModule.forRoot({
      notifierProvider: {
        provide: SECURITY_WORKFLOW_NOTIFIER,
        useFactory: (emailService: EmailService) => ({
          sendAdminsUserEmailVerified: ({ adminEmails, user }) =>
            emailService.sendEmailVerifiedNotificationToAdmins(
              adminEmails,
              user,
            ),
          sendUserAccountApproved: ({ email }) =>
            emailService.sendAccountApproved(email),
        }),
        inject: [EmailService],
      },
    }),
  ],
})
export class AppModule {}

User Table Ownership Model

Consuming apps keep ownership of their own app_user table. sdr-security stores security/auth state in its own tables and links them by user id.

  • App-owned table:
    • app_user (at minimum: id, email, plus any app-specific columns)
  • sdr-security tables:
    • security_user (password hash, verified/approved/active flags)
    • security_identity (provider links such as Google subject)
    • security_role, security_user_role
    • refresh_token
    • security_password_reset_token

Link key:

  • security_* .user_id -> app_user.id

This lets each app evolve its user schema independently while reusing the same security workflows, guards, controllers, and migrations.

Typical app query pattern is a join when you need security state:

SELECT u.id, u.email, su.is_active, su.admin_approved_at, su.email_verified_at
FROM app_user u
LEFT JOIN security_user su ON su.user_id = u.id
WHERE u.id = $1;

Nest/TypeORM equivalent:

const row = await usersRepo
  .createQueryBuilder("user")
  .leftJoin("security_user", "securityUser", "securityUser.user_id = user.id")
  .select("user.id", "id")
  .addSelect("user.email", "email")
  .addSelect("securityUser.is_active", "isActive")
  .addSelect("securityUser.admin_approved_at", "adminApprovedAt")
  .addSelect("securityUser.email_verified_at", "emailVerifiedAt")
  .where("user.id = :id", { id: userId })
  .getRawOne();

Optional Swagger setup in consuming app:

import { setupSecuritySwagger } from "@scryan7371/sdr-security/nest";

setupSecuritySwagger(app); // default path: /docs/security

Routes exposed by the shared controller:

  • POST /security/auth/register
  • POST /security/auth/login
  • POST /security/auth/forgot-password
  • POST /security/auth/reset-password
  • GET /security/auth/verify-email?token=...
  • POST /security/auth/change-password (JWT required)
  • POST /security/auth/logout (JWT required)
  • POST /security/auth/refresh
  • GET /security/auth/me/roles (JWT required)
  • POST /security/workflows/users/:id/email-verified
    • marks email_verified_at and notifies admins.
  • PATCH /security/workflows/users/:id/admin-approval with { approved: boolean }
    • updates admin_approved_at and notifies user when approved (admin JWT required).
  • PATCH /security/workflows/users/:id/active with { active: boolean } (admin JWT required)
  • GET /security/workflows/roles (admin JWT required)
  • POST /security/workflows/roles (admin JWT required)
  • DELETE /security/workflows/roles/:role (admin JWT required)
  • GET /security/workflows/users/:id/roles (admin JWT required)
  • PUT /security/workflows/users/:id/roles (admin JWT required)
  • POST /security/workflows/users/:id/roles with { role: string } (admin JWT required)
  • DELETE /security/workflows/users/:id/roles/:role (admin JWT required)

Shared notification workflows

Use these helpers to standardize notification behavior across apps while still keeping app-specific email sending in your own services.

import { api as sdrSecurity } from "@scryan7371/sdr-security";

await sdrSecurity.notifyAdminsOnEmailVerified({
  user: {
    id: user.id,
    email: user.email,
  },
  listAdminEmails: () => usersService.listAdminEmails(),
  notifyAdmins: ({ adminEmails, user }) =>
    emailService.sendEmailVerifiedNotificationToAdmins(adminEmails, user),
});

await sdrSecurity.notifyUserOnAdminApproval({
  approved: body.approved,
  user: {
    email: user.email,
  },
  notifyUser: ({ email }) => emailService.sendAccountApproved(email),
});

App Integration

Create one client per app session and reuse it across screens:

import { app as sdrSecurity } from "@scryan7371/sdr-security";

const securityClient = sdrSecurity.createSecurityClient({
  baseUrl,
  getAccessToken: () => accessToken,
});

Methods:

  • register
  • login
  • loginWithGoogle
  • refresh
  • revoke
  • logout
  • requestEmailVerification
  • verifyEmail
  • requestPhoneVerification
  • verifyPhone

Publish (npmjs)

  1. Configure project-local npm auth (.npmrc):
registry=https://registry.npmjs.org/
@scryan7371:registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
  1. Set token, bump version, and publish:
export NPM_TOKEN=xxxx
npm version patch
npm publish --access public --registry=https://registry.npmjs.org --userconfig .npmrc
  1. Push commit and tags:
git push
git push --tags

CI Publish (GitHub Actions)

Tag pushes like sdr-security-v* trigger .github/workflows/publish.yml.

Required repo secret:

  • NPM_TOKEN (npm granular token with read/write + bypass 2FA for automation).

Install

Install a pinned version:

npm install @scryan7371/sdr-security@0.1.0

Database Integration Test

A sample Postgres integration test is included at:

  • src/integration/database.integration.test.ts

Run it with:

npm run test:db

Configuration resolution order:

  1. .env.test (if present)
  2. .env.dev (if present)
  3. existing process env

Supported env vars:

  • SECURITY_TEST_DATABASE_URL (preferred)
  • or DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME
  • optional fallback: DATABASE_URL
  • optional debug:
    • SECURITY_TEST_KEEP_SCHEMA=true (do not drop schema after test run)
    • SECURITY_TEST_SCHEMA=your_schema_name (use fixed schema name)

See .env.test.example for a template.

Release Script

You can automate version bump + tag + push with:

npm run release:patch
npm run release:minor
npm run release:major

What it does:

  1. Verifies clean git working tree
  2. Runs npm test
  3. Runs npm run build
  4. Bumps package.json + package-lock.json
  5. Commits as chore(release): vX.Y.Z
  6. Tags as sdr-security-vX.Y.Z
  7. Pushes commit and tag

This tag format triggers .github/workflows/publish.yml.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages