npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ikary/system-auth

v0.1.0

Published

Production-ready multi-tenant auth library for NestJS and PostgreSQL

Readme

Auth Lib (@ikary/system-auth)

IAM-focused authentication library for NestJS + PostgreSQL.

Scope of this package:

  • Identity and authentication only
  • Multi-tenant isolation (workspace_id)
  • JWT access + refresh with rotation
  • Classic email/password auth (v1)
  • Per-app public auth capability flags
  • Email verification (code or click)
  • Forgot/reset password
  • Optional magic link
  • Zod validation and raw SQL migrations

Out of scope by design:

  • Authorization policy engine and access-control rules
  • Access-control guards

Package Kind

This package is a server library. It exports Node/NestJS runtime functionality from the package root only.

1. Installation

cd libs/system-auth
pnpm install --ignore-workspace
pnpm build

Install in host app:

pnpm add @ikary/system-auth

This library intentionally uses pure-JS bcryptjs for bcrypt-format password hashing so local, CI, and production installs do not depend on native addon bindings.

2. Run Migrations

cd libs/system-auth
export DATABASE_URL='postgres://user:pass@localhost:5432/app'
pnpm migrate

Migrations are incremental and tracked in schema_migrations. Naming convention is NNN_<module>_<domain>_<action>.sql so ordering and ownership stay explicit across many modules. Migration files 004 and 005 are intentional no-op placeholders to keep versioned migration order stable.

Current v1.0.0 files:

  • 001_auth_user_create_users.sql
  • 002_auth_organization_create_organizations.sql
  • 003_auth_membership_create_memberships.sql
  • 004_auth_core_reserved_slot_004.sql
  • 005_auth_core_reserved_slot_005.sql
  • 006_auth_audit_create_audit_logs.sql

Current IAM schema tables:

  • users
  • workspaces
  • workspace_members
  • refresh_tokens
  • email_verification_tokens
  • password_reset_tokens
  • magic_link_tokens
  • audit_logs

3. AuthModule Configuration

import { Module } from '@nestjs/common';
import { AuthModule } from '@ikary/system-auth';

@Module({
  imports: [
    AuthModule.register({
      database: {
        connectionString: process.env.DATABASE_URL!,
        ssl: false,
        maxPoolSize: 20,
      },
      jwt: {
        accessTokenSecret: process.env.AUTH_ACCESS_TOKEN_SECRET!,
        refreshTokenSecret: process.env.AUTH_REFRESH_TOKEN_SECRET!,
        tokenHashSecret: process.env.AUTH_TOKEN_HASH_SECRET!,
        accessTokenTtlSeconds: 900,
        refreshTokenTtlSeconds: 60 * 60 * 24 * 14,
        issuer: 'your-saas',
        audience: 'your-saas-clients',
      },
      classic: {
        enabled: true,
        signup: true,
        resetPassword: true,
        magicLink: false,
        emailVerification: 'code',
        requireEmailVerification: true,
        passwordMinLength: 8,
        verificationCodeLength: 6,
        verificationTokenTtlMinutes: 20,
        resetPasswordTtlMinutes: 30,
        magicLinkTtlMinutes: 15,
      },
      github: { enabled: false, clientId: '' },
      google: { enabled: false, clientId: '' },
      sso: { enabled: false },
      okta: { enabled: false },
    }),
  ],
})
export class AppModule {}

Requested shape example:

AuthModule.register({
  database: { connectionString: process.env.DATABASE_URL! },
  jwt: {
    accessTokenSecret: process.env.AUTH_ACCESS_TOKEN_SECRET!,
    refreshTokenSecret: process.env.AUTH_REFRESH_TOKEN_SECRET!,
    tokenHashSecret: process.env.AUTH_TOKEN_HASH_SECRET!,
  },
  classic: {
    enabled: true,
    signup: true,
    resetPassword: true,
    magicLink: false,
    emailVerification: 'code',
    passwordMinLength: 8,
  },
  github: { enabled: false, clientId: '' },
  google: { enabled: false, clientId: '' },
  sso: { enabled: false },
  okta: { enabled: false },
});

4. Endpoints (Classic)

  • POST /auth/signup
  • POST /auth/login
  • POST /auth/refresh
  • POST /auth/forgot-password
  • POST /auth/reset-password
  • POST /auth/verify-email
  • POST /auth/magic-link/request
  • POST /auth/magic-link/consume

Capability flags:

  • classic.signup = false makes POST /auth/signup return 404
  • classic.resetPassword = false makes both POST /auth/forgot-password and POST /auth/reset-password return 404

This keeps the route shape stable in the shared controller while allowing each app to disable public self-service flows.

5. Multi-Tenant Isolation

  1. Protected requests require x-workspace-id.
  2. JWT contains user_id and workspace_id and guard verifies tenant match.
  3. Tenant-owned tables carry workspace_id and repository queries use tenant predicates.
  4. Active membership in memberships is required to access tenant context.

6. Protected Endpoint Example

import { Controller, Get, UseGuards, UseInterceptors } from '@nestjs/common';
import { JwtAuthGuard, WorkspaceGuard, AuditInterceptor, CurrentAuth } from '@ikary/system-auth';

@Controller('tenant')
@UseGuards(JwtAuthGuard, WorkspaceGuard)
@UseInterceptors(AuditInterceptor)
export class TenantController {
  @Get('me')
  me(@CurrentAuth() auth: { userId: string; workspaceId: string }) {
    return { ok: true, auth };
  }
}

7. Security Assumptions

  • Passwords are stored as bcrypt-format hashes via bcryptjs.
  • Refresh tokens are hashed before storage.
  • Verification/reset/magic tokens are hashed before storage.
  • Refresh token rotation revokes prior refresh token.
  • Password reset revokes refresh sessions for that tenant.
  • Production must run over HTTPS.

8. Extending Providers (GitHub/Google/SSO/Okta)

Provider extension contract:

interface AuthProvider {
  provider: 'classic' | 'github' | 'google' | 'sso' | 'okta';
  signup(input: unknown, context: AuthContext): Promise<SignupResult>;
  login(input: unknown, context: AuthContext): Promise<LoginResult>;
  refresh(input: unknown, context: AuthContext): Promise<LoginResult>;
  forgotPassword(input: unknown, context: AuthContext): Promise<void>;
  resetPassword(input: unknown, context: AuthContext): Promise<void>;
  verifyEmail(input: unknown, context: AuthContext): Promise<void>;
}

Add new providers under src/providers/<name>/ and register in AuthModule behind enabled config flags.

9. Trusted Provisioning

Trusted server-side provisioning is available for bootstrap flows that must not depend on public signup.

import { AuthProvisioningService } from '@ikary/system-auth';

await authProvisioningService.provisionClassicUser({
  email: '[email protected]',
  password: 'replace-with-a-strong-password',
  workspaceName: 'Ikary API',
  workspaceSlug: 'ikary-api',
  markEmailVerified: true,
});

Notes:

  • this service is internal/server-side only
  • it does not depend on classic.signup
  • it still honors the shared password policy
  • it is idempotent only for the exact same user + workspace target

10. Versioning and Breaking Changes

  • Migrations are versioned under migrations/vX.Y.Z/.
  • Existing migration files are immutable after release.
  • Breaking changes require major version bump.

Breaking examples:

  • Public TypeScript contract changes
  • Endpoint contract changes
  • JWT claim contract changes (user_id, workspace_id)
  • Tenant isolation invariant changes

11. Commands

cd libs/system-auth
pnpm typecheck
pnpm build
pnpm migrate