@factiii/auth 0.5.5 → 0.5.6

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.
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/validators.ts
9
2
  import { z } from "zod";
10
3
  var usernameValidationRegex = /^[a-zA-Z0-9_]+$/;
@@ -84,7 +77,6 @@ function createSchemas(extensions) {
84
77
  }
85
78
 
86
79
  export {
87
- __require,
88
80
  signupSchema,
89
81
  loginSchema,
90
82
  oAuthLoginSchema,
@@ -0,0 +1,148 @@
1
+ /**
2
+ * ORM-agnostic database adapter interface for @factiii/auth.
3
+ * Implement this interface to use any database/ORM with the auth library.
4
+ */
5
+ interface AuthUser {
6
+ id: number;
7
+ status: string;
8
+ email: string;
9
+ username: string;
10
+ password: string | null;
11
+ twoFaEnabled: boolean;
12
+ oauthProvider: string | null;
13
+ oauthId: string | null;
14
+ tag: string;
15
+ verifiedHumanAt: Date | null;
16
+ emailVerificationStatus: string;
17
+ otpForEmailVerification: string | null;
18
+ isActive: boolean;
19
+ }
20
+ interface AuthSession {
21
+ id: number;
22
+ userId: number;
23
+ socketId: string | null;
24
+ twoFaSecret: string | null;
25
+ browserName: string;
26
+ issuedAt: Date;
27
+ lastUsed: Date;
28
+ revokedAt: Date | null;
29
+ deviceId: number | null;
30
+ }
31
+ interface AuthOTP {
32
+ id: number;
33
+ code: number;
34
+ expiresAt: Date;
35
+ userId: number;
36
+ }
37
+ interface AuthPasswordReset {
38
+ id: string;
39
+ createdAt: Date;
40
+ userId: number;
41
+ }
42
+ interface CreateUserData {
43
+ username: string;
44
+ email: string;
45
+ password: string | null;
46
+ status: string;
47
+ tag: string;
48
+ twoFaEnabled: boolean;
49
+ emailVerificationStatus: string;
50
+ verifiedHumanAt: Date | null;
51
+ oauthProvider?: string;
52
+ oauthId?: string;
53
+ }
54
+ interface CreateSessionData {
55
+ userId: number;
56
+ browserName: string;
57
+ socketId: string | null;
58
+ [key: string]: unknown;
59
+ }
60
+ type SessionWithUser = AuthSession & {
61
+ user: {
62
+ status: string;
63
+ verifiedHumanAt: Date | null;
64
+ };
65
+ };
66
+ type SessionWithDevice = {
67
+ twoFaSecret: string | null;
68
+ deviceId: number | null;
69
+ device: {
70
+ pushToken: string;
71
+ } | null;
72
+ };
73
+ interface DatabaseAdapter {
74
+ user: {
75
+ findByEmailInsensitive(email: string): Promise<AuthUser | null>;
76
+ findByUsernameInsensitive(username: string): Promise<AuthUser | null>;
77
+ findByEmailOrUsernameInsensitive(identifier: string): Promise<AuthUser | null>;
78
+ findByEmailOrOAuthId(email: string, oauthId: string): Promise<AuthUser | null>;
79
+ findById(id: number): Promise<AuthUser | null>;
80
+ findActiveById(id: number): Promise<AuthUser | null>;
81
+ create(data: CreateUserData): Promise<AuthUser>;
82
+ update(id: number, data: Partial<Omit<AuthUser, 'id'>>): Promise<AuthUser>;
83
+ };
84
+ session: {
85
+ /** Find session by ID with user status and verifiedHumanAt joined. */
86
+ findById(id: number): Promise<SessionWithUser | null>;
87
+ create(data: CreateSessionData): Promise<AuthSession>;
88
+ update(id: number, data: Partial<Pick<AuthSession, 'revokedAt' | 'lastUsed' | 'twoFaSecret' | 'deviceId'>>): Promise<AuthSession>;
89
+ /** Update lastUsed and return session with user's verifiedHumanAt. */
90
+ updateLastUsed(id: number): Promise<AuthSession & {
91
+ user: {
92
+ verifiedHumanAt: Date | null;
93
+ };
94
+ }>;
95
+ /** Set revokedAt on a single session. */
96
+ revoke(id: number): Promise<void>;
97
+ /** Find active (non-revoked) sessions for a user, optionally excluding one. */
98
+ findActiveByUserId(userId: number, excludeSessionId?: number): Promise<Pick<AuthSession, 'id' | 'socketId' | 'userId'>[]>;
99
+ /** Revoke all active sessions for a user, optionally excluding one. */
100
+ revokeAllByUserId(userId: number, excludeSessionId?: number): Promise<void>;
101
+ /** Get twoFaSecret from all sessions that have one for a user. */
102
+ findTwoFaSecretsByUserId(userId: number): Promise<{
103
+ twoFaSecret: string | null;
104
+ }[]>;
105
+ /** Clear twoFaSecret on sessions for a user, optionally excluding one. */
106
+ clearTwoFaSecrets(userId: number, excludeSessionId?: number): Promise<void>;
107
+ /** Find session with device relation for TOTP verification. */
108
+ findByIdWithDevice(id: number, userId: number): Promise<SessionWithDevice | null>;
109
+ /** Revoke other sessions that share a device push token. */
110
+ revokeByDevicePushToken(userId: number, pushToken: string, excludeSessionId: number): Promise<void>;
111
+ /** Clear deviceId on all sessions for a user+device pair. */
112
+ clearDeviceId(userId: number, deviceId: number): Promise<void>;
113
+ };
114
+ otp: {
115
+ findValidByUserAndCode(userId: number, code: number): Promise<AuthOTP | null>;
116
+ create(data: {
117
+ userId: number;
118
+ code: number;
119
+ expiresAt: Date;
120
+ }): Promise<AuthOTP>;
121
+ delete(id: number): Promise<void>;
122
+ };
123
+ passwordReset: {
124
+ findById(id: string): Promise<AuthPasswordReset | null>;
125
+ create(userId: number): Promise<AuthPasswordReset>;
126
+ delete(id: string): Promise<void>;
127
+ deleteAllByUserId(userId: number): Promise<void>;
128
+ };
129
+ device: {
130
+ findByTokenSessionAndUser(pushToken: string, sessionId: number, userId: number): Promise<{
131
+ id: number;
132
+ } | null>;
133
+ upsertByPushToken(pushToken: string, sessionId: number, userId: number): Promise<void>;
134
+ findByUserAndToken(userId: number, pushToken: string): Promise<{
135
+ id: number;
136
+ } | null>;
137
+ disconnectUser(deviceId: number, userId: number): Promise<void>;
138
+ hasRemainingUsers(deviceId: number): Promise<boolean>;
139
+ delete(id: number): Promise<void>;
140
+ };
141
+ admin: {
142
+ findByUserId(userId: number): Promise<{
143
+ ip: string;
144
+ } | null>;
145
+ };
146
+ }
147
+
148
+ export type { AuthOTP as A, CreateSessionData as C, DatabaseAdapter as D, SessionWithDevice as S, AuthPasswordReset as a, AuthSession as b, AuthUser as c, CreateUserData as d, SessionWithUser as e };
@@ -0,0 +1,148 @@
1
+ /**
2
+ * ORM-agnostic database adapter interface for @factiii/auth.
3
+ * Implement this interface to use any database/ORM with the auth library.
4
+ */
5
+ interface AuthUser {
6
+ id: number;
7
+ status: string;
8
+ email: string;
9
+ username: string;
10
+ password: string | null;
11
+ twoFaEnabled: boolean;
12
+ oauthProvider: string | null;
13
+ oauthId: string | null;
14
+ tag: string;
15
+ verifiedHumanAt: Date | null;
16
+ emailVerificationStatus: string;
17
+ otpForEmailVerification: string | null;
18
+ isActive: boolean;
19
+ }
20
+ interface AuthSession {
21
+ id: number;
22
+ userId: number;
23
+ socketId: string | null;
24
+ twoFaSecret: string | null;
25
+ browserName: string;
26
+ issuedAt: Date;
27
+ lastUsed: Date;
28
+ revokedAt: Date | null;
29
+ deviceId: number | null;
30
+ }
31
+ interface AuthOTP {
32
+ id: number;
33
+ code: number;
34
+ expiresAt: Date;
35
+ userId: number;
36
+ }
37
+ interface AuthPasswordReset {
38
+ id: string;
39
+ createdAt: Date;
40
+ userId: number;
41
+ }
42
+ interface CreateUserData {
43
+ username: string;
44
+ email: string;
45
+ password: string | null;
46
+ status: string;
47
+ tag: string;
48
+ twoFaEnabled: boolean;
49
+ emailVerificationStatus: string;
50
+ verifiedHumanAt: Date | null;
51
+ oauthProvider?: string;
52
+ oauthId?: string;
53
+ }
54
+ interface CreateSessionData {
55
+ userId: number;
56
+ browserName: string;
57
+ socketId: string | null;
58
+ [key: string]: unknown;
59
+ }
60
+ type SessionWithUser = AuthSession & {
61
+ user: {
62
+ status: string;
63
+ verifiedHumanAt: Date | null;
64
+ };
65
+ };
66
+ type SessionWithDevice = {
67
+ twoFaSecret: string | null;
68
+ deviceId: number | null;
69
+ device: {
70
+ pushToken: string;
71
+ } | null;
72
+ };
73
+ interface DatabaseAdapter {
74
+ user: {
75
+ findByEmailInsensitive(email: string): Promise<AuthUser | null>;
76
+ findByUsernameInsensitive(username: string): Promise<AuthUser | null>;
77
+ findByEmailOrUsernameInsensitive(identifier: string): Promise<AuthUser | null>;
78
+ findByEmailOrOAuthId(email: string, oauthId: string): Promise<AuthUser | null>;
79
+ findById(id: number): Promise<AuthUser | null>;
80
+ findActiveById(id: number): Promise<AuthUser | null>;
81
+ create(data: CreateUserData): Promise<AuthUser>;
82
+ update(id: number, data: Partial<Omit<AuthUser, 'id'>>): Promise<AuthUser>;
83
+ };
84
+ session: {
85
+ /** Find session by ID with user status and verifiedHumanAt joined. */
86
+ findById(id: number): Promise<SessionWithUser | null>;
87
+ create(data: CreateSessionData): Promise<AuthSession>;
88
+ update(id: number, data: Partial<Pick<AuthSession, 'revokedAt' | 'lastUsed' | 'twoFaSecret' | 'deviceId'>>): Promise<AuthSession>;
89
+ /** Update lastUsed and return session with user's verifiedHumanAt. */
90
+ updateLastUsed(id: number): Promise<AuthSession & {
91
+ user: {
92
+ verifiedHumanAt: Date | null;
93
+ };
94
+ }>;
95
+ /** Set revokedAt on a single session. */
96
+ revoke(id: number): Promise<void>;
97
+ /** Find active (non-revoked) sessions for a user, optionally excluding one. */
98
+ findActiveByUserId(userId: number, excludeSessionId?: number): Promise<Pick<AuthSession, 'id' | 'socketId' | 'userId'>[]>;
99
+ /** Revoke all active sessions for a user, optionally excluding one. */
100
+ revokeAllByUserId(userId: number, excludeSessionId?: number): Promise<void>;
101
+ /** Get twoFaSecret from all sessions that have one for a user. */
102
+ findTwoFaSecretsByUserId(userId: number): Promise<{
103
+ twoFaSecret: string | null;
104
+ }[]>;
105
+ /** Clear twoFaSecret on sessions for a user, optionally excluding one. */
106
+ clearTwoFaSecrets(userId: number, excludeSessionId?: number): Promise<void>;
107
+ /** Find session with device relation for TOTP verification. */
108
+ findByIdWithDevice(id: number, userId: number): Promise<SessionWithDevice | null>;
109
+ /** Revoke other sessions that share a device push token. */
110
+ revokeByDevicePushToken(userId: number, pushToken: string, excludeSessionId: number): Promise<void>;
111
+ /** Clear deviceId on all sessions for a user+device pair. */
112
+ clearDeviceId(userId: number, deviceId: number): Promise<void>;
113
+ };
114
+ otp: {
115
+ findValidByUserAndCode(userId: number, code: number): Promise<AuthOTP | null>;
116
+ create(data: {
117
+ userId: number;
118
+ code: number;
119
+ expiresAt: Date;
120
+ }): Promise<AuthOTP>;
121
+ delete(id: number): Promise<void>;
122
+ };
123
+ passwordReset: {
124
+ findById(id: string): Promise<AuthPasswordReset | null>;
125
+ create(userId: number): Promise<AuthPasswordReset>;
126
+ delete(id: string): Promise<void>;
127
+ deleteAllByUserId(userId: number): Promise<void>;
128
+ };
129
+ device: {
130
+ findByTokenSessionAndUser(pushToken: string, sessionId: number, userId: number): Promise<{
131
+ id: number;
132
+ } | null>;
133
+ upsertByPushToken(pushToken: string, sessionId: number, userId: number): Promise<void>;
134
+ findByUserAndToken(userId: number, pushToken: string): Promise<{
135
+ id: number;
136
+ } | null>;
137
+ disconnectUser(deviceId: number, userId: number): Promise<void>;
138
+ hasRemainingUsers(deviceId: number): Promise<boolean>;
139
+ delete(id: number): Promise<void>;
140
+ };
141
+ admin: {
142
+ findByUserId(userId: number): Promise<{
143
+ ip: string;
144
+ } | null>;
145
+ };
146
+ }
147
+
148
+ export type { AuthOTP as A, CreateSessionData as C, DatabaseAdapter as D, SessionWithDevice as S, AuthPasswordReset as a, AuthSession as b, AuthUser as c, CreateUserData as d, SessionWithUser as e };
@@ -0,0 +1,60 @@
1
+ import { AnyPgTable, PgColumn, PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core';
2
+ import { D as DatabaseAdapter } from './database-CqnmD1HM.mjs';
3
+
4
+ /**
5
+ * A Postgres Drizzle table with column properties accessible by name.
6
+ * `AnyPgTable` is Drizzle's base Postgres table type; intersecting with
7
+ * `Record<string, Column>` exposes the column descriptors for index access.
8
+ */
9
+ type DrizzleTable = AnyPgTable & Record<string, PgColumn>;
10
+ /**
11
+ * Drizzle table references required by the adapter.
12
+ * Consumers pass their Drizzle Postgres table objects so the adapter
13
+ * can build queries without knowing the schema file location.
14
+ *
15
+ * **Note:** This adapter only supports PostgreSQL via `drizzle-orm/pg-core`.
16
+ */
17
+ interface DrizzleAdapterTables {
18
+ users: DrizzleTable;
19
+ sessions: DrizzleTable;
20
+ otps: DrizzleTable;
21
+ passwordResets: DrizzleTable;
22
+ devices: DrizzleTable;
23
+ admins: DrizzleTable;
24
+ /** Join table for many-to-many device↔user relation (if applicable). */
25
+ devicesToUsers?: DrizzleTable;
26
+ /** Join table for many-to-many device↔session relation (if applicable). */
27
+ devicesToSessions?: DrizzleTable;
28
+ }
29
+ /**
30
+ * Any `PgDatabase` instance, regardless of the underlying driver
31
+ * (node-postgres, postgres.js, Neon, PGLite, etc.).
32
+ */
33
+ type AnyPgDatabase = PgDatabase<PgQueryResultHKT, Record<string, unknown>>;
34
+ /**
35
+ * Creates a DatabaseAdapter backed by Drizzle ORM.
36
+ *
37
+ * Usage:
38
+ * ```ts
39
+ * import { drizzle } from 'drizzle-orm/node-postgres';
40
+ * import { createDrizzleAdapter } from '@factiii/auth/drizzle';
41
+ * import * as schema from './schema';
42
+ *
43
+ * const db = drizzle(pool, { schema });
44
+ * const adapter = createDrizzleAdapter(db, {
45
+ * users: schema.users,
46
+ * sessions: schema.sessions,
47
+ * otps: schema.otps,
48
+ * passwordResets: schema.passwordResets,
49
+ * devices: schema.devices,
50
+ * admins: schema.admins,
51
+ * });
52
+ * ```
53
+ *
54
+ * **Important:** This adapter uses Drizzle's relational query API (`db.query.*`)
55
+ * for joins and `db.insert/update/delete` for mutations. Make sure your Drizzle
56
+ * instance is created with `{ schema }` so relational queries work.
57
+ */
58
+ declare function createDrizzleAdapter(db: AnyPgDatabase, tables: DrizzleAdapterTables): DatabaseAdapter;
59
+
60
+ export { type DrizzleAdapterTables, createDrizzleAdapter };
@@ -0,0 +1,60 @@
1
+ import { AnyPgTable, PgColumn, PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core';
2
+ import { D as DatabaseAdapter } from './database-CqnmD1HM.js';
3
+
4
+ /**
5
+ * A Postgres Drizzle table with column properties accessible by name.
6
+ * `AnyPgTable` is Drizzle's base Postgres table type; intersecting with
7
+ * `Record<string, Column>` exposes the column descriptors for index access.
8
+ */
9
+ type DrizzleTable = AnyPgTable & Record<string, PgColumn>;
10
+ /**
11
+ * Drizzle table references required by the adapter.
12
+ * Consumers pass their Drizzle Postgres table objects so the adapter
13
+ * can build queries without knowing the schema file location.
14
+ *
15
+ * **Note:** This adapter only supports PostgreSQL via `drizzle-orm/pg-core`.
16
+ */
17
+ interface DrizzleAdapterTables {
18
+ users: DrizzleTable;
19
+ sessions: DrizzleTable;
20
+ otps: DrizzleTable;
21
+ passwordResets: DrizzleTable;
22
+ devices: DrizzleTable;
23
+ admins: DrizzleTable;
24
+ /** Join table for many-to-many device↔user relation (if applicable). */
25
+ devicesToUsers?: DrizzleTable;
26
+ /** Join table for many-to-many device↔session relation (if applicable). */
27
+ devicesToSessions?: DrizzleTable;
28
+ }
29
+ /**
30
+ * Any `PgDatabase` instance, regardless of the underlying driver
31
+ * (node-postgres, postgres.js, Neon, PGLite, etc.).
32
+ */
33
+ type AnyPgDatabase = PgDatabase<PgQueryResultHKT, Record<string, unknown>>;
34
+ /**
35
+ * Creates a DatabaseAdapter backed by Drizzle ORM.
36
+ *
37
+ * Usage:
38
+ * ```ts
39
+ * import { drizzle } from 'drizzle-orm/node-postgres';
40
+ * import { createDrizzleAdapter } from '@factiii/auth/drizzle';
41
+ * import * as schema from './schema';
42
+ *
43
+ * const db = drizzle(pool, { schema });
44
+ * const adapter = createDrizzleAdapter(db, {
45
+ * users: schema.users,
46
+ * sessions: schema.sessions,
47
+ * otps: schema.otps,
48
+ * passwordResets: schema.passwordResets,
49
+ * devices: schema.devices,
50
+ * admins: schema.admins,
51
+ * });
52
+ * ```
53
+ *
54
+ * **Important:** This adapter uses Drizzle's relational query API (`db.query.*`)
55
+ * for joins and `db.insert/update/delete` for mutations. Make sure your Drizzle
56
+ * instance is created with `{ schema }` so relational queries work.
57
+ */
58
+ declare function createDrizzleAdapter(db: AnyPgDatabase, tables: DrizzleAdapterTables): DatabaseAdapter;
59
+
60
+ export { type DrizzleAdapterTables, createDrizzleAdapter };