@eaccess/auth 0.1.21 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @prsm/easy-auth
1
+ # @eaccess/auth
2
2
 
3
3
  An Express authentication middleware specifically designed for Postgres that provides complete authentication functionality without being tied to any specific ORM, query builder, or user table structure. Comprehensive auth without overwhelming complexity. A clean separation of concerns -- not conflating authentication with user management.
4
4
 
@@ -17,7 +17,7 @@ An Express authentication middleware specifically designed for Postgres that pro
17
17
  ## Installation
18
18
 
19
19
  ```bash
20
- npm install @prsm/easy-auth express-session
20
+ npm install @eaccess/auth express-session
21
21
  ```
22
22
 
23
23
  ## Quick Start
@@ -26,7 +26,7 @@ npm install @prsm/easy-auth express-session
26
26
  import express from 'express';
27
27
  import session from 'express-session';
28
28
  import { Pool } from 'pg';
29
- import { createAuthMiddleware, createAuthTables } from '@prsm/easy-auth';
29
+ import { createAuthMiddleware, createAuthTables } from '@eaccess/auth';
30
30
 
31
31
  const app = express();
32
32
  const pool = new Pool({ connectionString: 'postgresql://...' });
@@ -93,7 +93,7 @@ app.get('/profile', (req, res) => {
93
93
  if (!req.auth.isLoggedIn()) {
94
94
  return res.status(401).json({ error: 'Not logged in' });
95
95
  }
96
-
96
+
97
97
  res.json({
98
98
  email: req.auth.getEmail(),
99
99
  status: req.auth.getStatusName(),
@@ -113,7 +113,7 @@ Easy-auth supports OAuth providers (GitHub, Google, Azure) with a clean, extensi
113
113
  import express from 'express';
114
114
  import session from 'express-session';
115
115
  import { Pool } from 'pg';
116
- import { createAuthMiddleware, createAuthTables, type OAuthUserData } from '@prsm/easy-auth';
116
+ import { createAuthMiddleware, createAuthTables, type OAuthUserData } from '@eaccess/auth';
117
117
 
118
118
  const app = express();
119
119
  const pool = new Pool({ connectionString: 'postgresql://...' });
@@ -131,11 +131,11 @@ const authConfig = {
131
131
  name: userData.name || userData.username,
132
132
  email: userData.email,
133
133
  }).returning();
134
-
134
+
135
135
  return user.id; // Return the new user's ID
136
136
  },
137
137
  tablePrefix: 'auth_',
138
-
138
+
139
139
  // OAuth provider configuration
140
140
  providers: {
141
141
  github: {
@@ -217,7 +217,7 @@ app.get('/auth/google/callback', async (req, res) => {
217
217
  ### OAuth Flow Explained
218
218
 
219
219
  1. **User clicks "Login with GitHub"** → Browser goes to `/auth/github`
220
- 2. **Server redirects to GitHub** → User sees GitHub's login page
220
+ 2. **Server redirects to GitHub** → User sees GitHub's login page
221
221
  3. **User authorizes your app** → GitHub redirects to `/auth/github/callback?code=abc123`
222
222
  4. **Server processes callback** → `handleCallback()` does:
223
223
  - Exchange code for access token
@@ -278,16 +278,16 @@ app.get('/auth/github/callback', async (req, res) => {
278
278
  try {
279
279
  // Get user data without logging in
280
280
  const userData = await req.auth.providers.github.getUserData(req);
281
-
281
+
282
282
  // Your custom logic here
283
283
  const existingUser = await findUserByEmail(userData.email);
284
284
  if (existingUser && !existingUser.allowOAuth) {
285
285
  throw new Error('OAuth disabled for this account');
286
286
  }
287
-
287
+
288
288
  // Then complete the OAuth flow manually
289
289
  await req.auth.providers.github.handleCallback(req);
290
-
290
+
291
291
  res.json({ success: true, user: userData });
292
292
  } catch (error) {
293
293
  res.status(400).json({ error: error.message });
@@ -306,7 +306,7 @@ Enable MFA in your auth config:
306
306
  ```typescript
307
307
  const authConfig = {
308
308
  db: pool,
309
-
309
+
310
310
  twoFactor: {
311
311
  enabled: true,
312
312
  requireForOAuth: false, // Skip MFA for OAuth users (optional)
@@ -377,7 +377,7 @@ After receiving `SecondFactorRequiredError`, verify the second factor:
377
377
  app.post('/verify-2fa', async (req, res) => {
378
378
  try {
379
379
  const { code, method } = req.body;
380
-
380
+
381
381
  // Verify based on method
382
382
  switch (method) {
383
383
  case 'totp':
@@ -397,7 +397,7 @@ app.post('/verify-2fa', async (req, res) => {
397
397
  await req.auth.twoFactor.verify.otp(code);
398
398
  break;
399
399
  }
400
-
400
+
401
401
  // Complete login
402
402
  await req.auth.completeTwoFactorLogin();
403
403
  res.json({ success: true });
@@ -417,9 +417,9 @@ Users can enroll in multiple MFA methods:
417
417
  app.post('/setup-totp', async (req, res) => {
418
418
  try {
419
419
  const { secret, qrCode, backupCodes } = await req.auth.twoFactor.setup.totp();
420
-
420
+
421
421
  // Show QR code to user for scanning with authenticator app
422
- res.json({
422
+ res.json({
423
423
  secret, // Manual entry secret
424
424
  qrCode, // QR code URL for scanning
425
425
  backupCodes // One-time backup codes
@@ -502,7 +502,7 @@ app.get('/mfa-status', async (req, res) => {
502
502
  // Disable MFA method
503
503
  app.delete('/mfa/:method', async (req, res) => {
504
504
  try {
505
- const mechanism = req.params.method === 'totp' ? 1 :
505
+ const mechanism = req.params.method === 'totp' ? 1 :
506
506
  req.params.method === 'email' ? 2 : 3;
507
507
  await req.auth.twoFactor.disable(mechanism);
508
508
  res.json({ success: true });
@@ -534,15 +534,15 @@ The auth library maintains its own auth tables (accounts, roles, sessions) that
534
534
  app.post('/register', async (req, res) => {
535
535
  // Option 1: Let easy-auth auto-generate a UUID (simplest)
536
536
  const account = await req.auth.register(req.body.email, req.body.password);
537
-
537
+
538
538
  // Option 2: Link to your existing user table
539
539
  const user = await db.insert(users).values({
540
540
  name: req.body.name,
541
541
  email: req.body.email
542
542
  }).returning();
543
-
543
+
544
544
  const account = await req.auth.register(req.body.email, req.body.password, user.id);
545
-
545
+
546
546
  res.json({ success: true, userId: user.id });
547
547
  });
548
548
  ```
@@ -559,7 +559,7 @@ const authConfig = {
559
559
  name: userData.name || userData.username,
560
560
  email: userData.email,
561
561
  }).returning();
562
-
562
+
563
563
  return user.id; // This will be stored as user_id in auth tables
564
564
  }
565
565
  }
@@ -584,7 +584,7 @@ app.post('/login', async (req, res) => {
584
584
 
585
585
  throw error;
586
586
  }
587
-
587
+
588
588
  res.json({ success: true });
589
589
  });
590
590
  ```
@@ -605,25 +605,26 @@ declare module "express-session" {
605
605
  interface AuthConfig {
606
606
  // PostgreSQL connection pool
607
607
  db: Pool;
608
-
608
+
609
609
  // Optional OAuth new user creation function
610
610
  createUser?: (userData: OAuthUserData) => string | number | Promise<string | number>; // Called when OAuth user doesn't exist in your system
611
-
611
+
612
612
  // Optional settings
613
613
  tablePrefix?: string; // default: 'user_'
614
+ roles?: Record<string, number>; // custom roles from defineRoles(), default: AuthRole
614
615
  minPasswordLength?: number; // default: 8
615
616
  maxPasswordLength?: number; // default: 64
616
617
  rememberDuration?: string; // default: '30d'
617
618
  rememberCookieName?: string; // default: 'remember_token'
618
619
  resyncInterval?: string; // default: '30s'
619
-
620
+
620
621
  // OAuth provider configuration
621
622
  providers?: {
622
623
  github?: GitHubProviderConfig;
623
624
  google?: GoogleProviderConfig;
624
625
  azure?: AzureProviderConfig;
625
626
  };
626
-
627
+
627
628
  // Multi-factor authentication
628
629
  twoFactor?: {
629
630
  enabled?: boolean; // default: false
@@ -733,14 +734,14 @@ CREATE TABLE user_accounts (
733
734
  - `generateNewBackupCodes(): Promise<string[]>`
734
735
  - `getContact(mechanism): Promise<string | null>`
735
736
 
736
- ### Admin Manager (`req.authAdmin`)
737
+ ### Admin Functions (also on `req.auth`)
737
738
 
738
739
  #### User Management
739
740
  - `createUser(credentials, callback?): Promise<AuthAccount>`
740
741
  - `loginAsUserBy(identifier): Promise<void>`
741
742
  - `deleteUserBy(identifier): Promise<void>`
742
743
 
743
- #### Role Management
744
+ #### Role Management
744
745
  - `addRoleForUserBy(identifier, role): Promise<void>`
745
746
  - `removeRoleForUserBy(identifier, role): Promise<void>`
746
747
  - `hasRoleForUserBy(identifier, role): Promise<boolean>`
@@ -753,7 +754,7 @@ CREATE TABLE user_accounts (
753
754
  ### Schema Utilities
754
755
 
755
756
  ```typescript
756
- import { createAuthTables, dropAuthTables, cleanupExpiredTokens, getAuthTableStats } from '@prsm/easy-auth';
757
+ import { createAuthTables, dropAuthTables, cleanupExpiredTokens, getAuthTableStats } from '@eaccess/auth';
757
758
 
758
759
  // Setup tables
759
760
  await createAuthTables(config);
@@ -769,20 +770,68 @@ console.log(`${stats.accounts} accounts, ${stats.expiredRemembers} expired token
769
770
  await dropAuthTables(config);
770
771
  ```
771
772
 
773
+ ## Custom Roles
774
+
775
+ The default `AuthRole` enum provides 21 predefined roles, but most apps need their own. Use `defineRoles` to create a custom role set:
776
+
777
+ ```typescript
778
+ import { defineRoles } from '@eaccess/auth';
779
+
780
+ const Roles = defineRoles('owner', 'editor', 'viewer');
781
+ // { owner: 1, editor: 2, viewer: 4 }
782
+
783
+ const authConfig = {
784
+ db: pool,
785
+ roles: Roles, // getRoleNames() will use these names
786
+ };
787
+
788
+ // usage
789
+ await req.auth.addRoleForUserBy({ email: 'user@example.com' }, Roles.owner | Roles.editor);
790
+ req.auth.getRoleNames(); // ['owner', 'editor']
791
+ await req.auth.hasRole(Roles.editor); // true
792
+ ```
793
+
794
+ Names are preserved exactly as provided - no transformation. Max 31 roles (Postgres INTEGER is 32-bit signed). If you don't set `config.roles`, the default `AuthRole` enum is used.
795
+
796
+ The admin panel (`@eaccess/admin`) reads `config.roles` from the backend automatically, so custom roles show up in the user management UI without any extra configuration.
797
+
798
+ ## Standalone Auth (no request context)
799
+
800
+ For server-side operations outside of Express routes (scripts, workers, cron jobs), use `createAuthContext`:
801
+
802
+ ```typescript
803
+ import { createAuthContext } from '@eaccess/auth';
804
+
805
+ const auth = createAuthContext(authConfig);
806
+
807
+ await auth.createUser({ email: 'user@example.com', password: 'password123' });
808
+ await auth.addRoleForUserBy({ email: 'user@example.com' }, Roles.editor);
809
+ await auth.resetPassword('user@example.com', '1h', 3, (token) => sendResetEmail(token));
810
+ ```
811
+
812
+ For WebSocket or raw HTTP authentication:
813
+
814
+ ```typescript
815
+ import { authenticateRequest } from '@eaccess/auth';
816
+
817
+ const result = await authenticateRequest(authConfig, req, sessionMiddleware);
818
+ // result: { account: AuthAccount | null, source: 'session' | 'remember' | null }
819
+ ```
820
+
772
821
  ## Constants
773
822
 
774
823
  ```typescript
775
- import { AuthStatus, AuthRole } from '@prsm/easy-auth';
824
+ import { AuthStatus, AuthRole } from '@eaccess/auth';
776
825
 
777
826
  // User statuses
778
827
  AuthStatus.Normal // 0
779
- AuthStatus.Archived // 1
828
+ AuthStatus.Archived // 1
780
829
  AuthStatus.Banned // 2
781
830
  AuthStatus.Locked // 3
782
831
  AuthStatus.PendingReview // 4
783
832
  AuthStatus.Suspended // 5
784
833
 
785
- // User roles (bitmask)
834
+ // Default roles (bitmask) - or use defineRoles() for custom roles
786
835
  AuthRole.Admin // 1
787
836
  AuthRole.Author // 2
788
837
  AuthRole.Collaborator // 4
@@ -792,13 +841,13 @@ AuthRole.Collaborator // 4
792
841
  ## Error Handling
793
842
 
794
843
  ```typescript
795
- import {
796
- EmailTakenError,
797
- InvalidPasswordError,
844
+ import {
845
+ EmailTakenError,
846
+ InvalidPasswordError,
798
847
  UserNotFoundError,
799
848
  SecondFactorRequiredError,
800
849
  InvalidTwoFactorCodeError
801
- } from '@prsm/easy-auth';
850
+ } from '@eaccess/auth';
802
851
 
803
852
  app.post('/register', async (req, res) => {
804
853
  try {
@@ -840,7 +889,7 @@ app.post('/login', async (req, res) => {
840
889
  ```typescript
841
890
  import { Pool } from 'pg';
842
891
 
843
- const pool = new Pool({
892
+ const pool = new Pool({
844
893
  connectionString: 'postgresql://user:password@localhost:5432/dbname'
845
894
  });
846
895
 
@@ -857,17 +906,17 @@ app.get('/admin', async (req, res) => {
857
906
  if (!req.auth.isLoggedIn()) {
858
907
  return res.status(401).json({ error: 'Not logged in' });
859
908
  }
860
-
909
+
861
910
  if (!await req.auth.hasRole(AuthRole.Admin)) {
862
911
  return res.status(403).json({ error: 'Admin access required' });
863
912
  }
864
-
913
+
865
914
  // Admin-only content
866
915
  });
867
916
 
868
917
  // Add role to user
869
- await req.authAdmin.addRoleForUserBy(
870
- { email: 'user@example.com' },
918
+ await req.auth.addRoleForUserBy(
919
+ { email: 'user@example.com' },
871
920
  AuthRole.Admin | AuthRole.Editor
872
921
  );
873
922
  ```