@revealui/auth 0.3.0 → 0.3.2
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/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +10 -4
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/magic-link.js +1 -1
- package/dist/server/oauth.d.ts +15 -4
- package/dist/server/oauth.d.ts.map +1 -1
- package/dist/server/oauth.js +32 -17
- package/dist/server/providers/github.d.ts.map +1 -1
- package/dist/server/providers/github.js +0 -3
- package/dist/server/providers/google.d.ts.map +1 -1
- package/dist/server/providers/google.js +0 -1
- package/dist/server/providers/vercel.d.ts.map +1 -1
- package/dist/server/providers/vercel.js +0 -1
- package/dist/server/session.d.ts +17 -0
- package/dist/server/session.d.ts.map +1 -1
- package/dist/server/session.js +51 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAQ,MAAM,aAAa,CAAC;AASpE;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAQ,MAAM,aAAa,CAAC;AASpE;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,OAAO,CAAC,YAAY,CAAC,CAqKvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAiBtD;AAED;;;;;;;;GAQG;AACH,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,OAAO,CAAC,YAAY,CAAC,CAsLvB"}
|
package/dist/server/auth.js
CHANGED
|
@@ -51,8 +51,10 @@ export async function signIn(email, password, options) {
|
|
|
51
51
|
try {
|
|
52
52
|
db = getClient();
|
|
53
53
|
}
|
|
54
|
-
catch {
|
|
55
|
-
logger.error('Error getting database client'
|
|
54
|
+
catch (clientError) {
|
|
55
|
+
logger.error('Error getting database client', clientError instanceof Error ? clientError : undefined, {
|
|
56
|
+
message: clientError instanceof Error ? clientError.message : String(clientError),
|
|
57
|
+
});
|
|
56
58
|
return {
|
|
57
59
|
success: false,
|
|
58
60
|
reason: 'database_error',
|
|
@@ -69,8 +71,12 @@ export async function signIn(email, password, options) {
|
|
|
69
71
|
.limit(1);
|
|
70
72
|
user = result[0];
|
|
71
73
|
}
|
|
72
|
-
catch {
|
|
73
|
-
logger.error('Error querying user'
|
|
74
|
+
catch (dbError) {
|
|
75
|
+
logger.error('Error querying user', dbError instanceof Error ? dbError : undefined, {
|
|
76
|
+
message: dbError instanceof Error ? dbError.message : String(dbError),
|
|
77
|
+
name: dbError instanceof Error ? dbError.name : 'unknown',
|
|
78
|
+
stack: dbError instanceof Error ? dbError.stack : undefined,
|
|
79
|
+
});
|
|
74
80
|
return {
|
|
75
81
|
success: false,
|
|
76
82
|
reason: 'database_error',
|
package/dist/server/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type { MagicLinkConfig } from './magic-link.js';
|
|
|
12
12
|
export { configureMagicLink, createMagicLink, resetMagicLinkConfig, verifyMagicLink, } from './magic-link.js';
|
|
13
13
|
export type { MFAConfig, MFADisableProof, MFASetupResult } from './mfa.js';
|
|
14
14
|
export { configureMFA, disableMFA, initiateMFASetup, isMFAEnabled, regenerateBackupCodes, resetMFAConfig, verifyBackupCode, verifyMFACode, verifyMFASetup, } from './mfa.js';
|
|
15
|
-
export { buildAuthUrl, exchangeCode, fetchProviderUser, generateOAuthState, getLinkedProviders, linkOAuthAccount, type ProviderUser, unlinkOAuthAccount, upsertOAuthUser, verifyOAuthState, } from './oauth.js';
|
|
15
|
+
export { buildAuthUrl, exchangeCode, fetchProviderUser, generateOAuthState, getLinkedProviders, linkOAuthAccount, type ProviderUser, type UpsertOAuthOptions, unlinkOAuthAccount, upsertOAuthUser, verifyOAuthState, } from './oauth.js';
|
|
16
16
|
export type { PasskeyConfig } from './passkey.js';
|
|
17
17
|
export { configurePasskey, countUserCredentials, deletePasskey, generateAuthenticationChallenge, generateRegistrationChallenge, listPasskeys, renamePasskey, resetPasskeyConfig, storePasskey, verifyAuthentication, verifyRegistration, } from './passkey.js';
|
|
18
18
|
export type { ChangePasswordResult, PasswordResetResult, PasswordResetToken, } from './password-reset.js';
|
|
@@ -20,7 +20,7 @@ export { changePassword, generatePasswordResetToken, invalidatePasswordResetToke
|
|
|
20
20
|
export { meetsMinimumPasswordRequirements, validatePasswordStrength, } from './password-validation.js';
|
|
21
21
|
export { checkRateLimit, configureRateLimit, getRateLimitStatus, resetRateLimit, resetRateLimitConfig, } from './rate-limit.js';
|
|
22
22
|
export type { RequestContext, SessionBindingConfig, SessionData } from './session.js';
|
|
23
|
-
export { configureSessionBinding, createSession, deleteAllUserSessions, deleteSession, getSession, resetSessionBindingConfig, rotateSession, validateSessionBinding, } from './session.js';
|
|
23
|
+
export { configureSessionBinding, createSession, deleteAllUserSessions, deleteOtherUserSessions, deleteSession, getSession, isRecoverySession, resetSessionBindingConfig, rotateSession, validateSessionBinding, } from './session.js';
|
|
24
24
|
export { signCookiePayload, verifyCookiePayload } from './signed-cookie.js';
|
|
25
25
|
export type { Storage } from './storage/index.js';
|
|
26
26
|
export { createStorage, DatabaseStorage, getStorage, InMemoryStorage, resetStorage, } from './storage/index.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,aAAa,EACb,yBAAyB,EACzB,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EACL,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,YAAY,EACjB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,+BAA+B,EAC/B,6BAA6B,EAC7B,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,0BAA0B,EAC1B,4BAA4B,EAC5B,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gCAAgC,EAChC,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,aAAa,EACb,UAAU,EACV,yBAAyB,EACzB,aAAa,EACb,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,YAAY,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,aAAa,EACb,eAAe,EACf,UAAU,EACV,eAAe,EACf,YAAY,GACb,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,aAAa,EACb,yBAAyB,EACzB,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EACL,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,+BAA+B,EAC/B,6BAA6B,EAC7B,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,0BAA0B,EAC1B,4BAA4B,EAC5B,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gCAAgC,EAChC,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,yBAAyB,EACzB,aAAa,EACb,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,YAAY,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,aAAa,EACb,eAAe,EACf,UAAU,EACV,eAAe,EACf,YAAY,GACb,MAAM,oBAAoB,CAAC"}
|
package/dist/server/index.js
CHANGED
|
@@ -14,7 +14,7 @@ export { configurePasskey, countUserCredentials, deletePasskey, generateAuthenti
|
|
|
14
14
|
export { changePassword, generatePasswordResetToken, invalidatePasswordResetToken, resetPasswordWithToken, validatePasswordResetToken, } from './password-reset.js';
|
|
15
15
|
export { meetsMinimumPasswordRequirements, validatePasswordStrength, } from './password-validation.js';
|
|
16
16
|
export { checkRateLimit, configureRateLimit, getRateLimitStatus, resetRateLimit, resetRateLimitConfig, } from './rate-limit.js';
|
|
17
|
-
export { configureSessionBinding, createSession, deleteAllUserSessions, deleteSession, getSession, resetSessionBindingConfig, rotateSession, validateSessionBinding, } from './session.js';
|
|
17
|
+
export { configureSessionBinding, createSession, deleteAllUserSessions, deleteOtherUserSessions, deleteSession, getSession, isRecoverySession, resetSessionBindingConfig, rotateSession, validateSessionBinding, } from './session.js';
|
|
18
18
|
// Signed Cookie
|
|
19
19
|
export { signCookiePayload, verifyCookiePayload } from './signed-cookie.js';
|
|
20
20
|
export { createStorage, DatabaseStorage, getStorage, InMemoryStorage, resetStorage, } from './storage/index.js';
|
|
@@ -92,7 +92,7 @@ export async function createMagicLink(userId) {
|
|
|
92
92
|
*/
|
|
93
93
|
export async function verifyMagicLink(token) {
|
|
94
94
|
const db = getClient();
|
|
95
|
-
// Select unexpired, unused magic links
|
|
95
|
+
// Select all unexpired, unused magic links
|
|
96
96
|
const rows = await db
|
|
97
97
|
.select()
|
|
98
98
|
.from(magicLinks)
|
package/dist/server/oauth.d.ts
CHANGED
|
@@ -19,7 +19,9 @@ export interface ProviderUser {
|
|
|
19
19
|
* Cookie value is `<state>.<hmac>` — the HMAC is over the state string
|
|
20
20
|
* using REVEALUI_SECRET, providing CSRF protection without a DB table.
|
|
21
21
|
*/
|
|
22
|
-
export declare function generateOAuthState(provider: string, redirectTo: string
|
|
22
|
+
export declare function generateOAuthState(provider: string, redirectTo: string, options?: {
|
|
23
|
+
linkConsent?: boolean;
|
|
24
|
+
}): {
|
|
23
25
|
state: string;
|
|
24
26
|
cookieValue: string;
|
|
25
27
|
};
|
|
@@ -31,21 +33,30 @@ export declare function generateOAuthState(provider: string, redirectTo: string)
|
|
|
31
33
|
export declare function verifyOAuthState(state: string | null | undefined, cookieValue: string | null | undefined): {
|
|
32
34
|
provider: string;
|
|
33
35
|
redirectTo: string;
|
|
36
|
+
linkConsent?: boolean;
|
|
34
37
|
} | null;
|
|
35
38
|
export declare function buildAuthUrl(provider: string, redirectUri: string, state: string): string;
|
|
36
39
|
export declare function exchangeCode(provider: string, code: string, redirectUri: string): Promise<string>;
|
|
37
40
|
export declare function fetchProviderUser(provider: string, accessToken: string): Promise<ProviderUser>;
|
|
41
|
+
export interface UpsertOAuthOptions {
|
|
42
|
+
/**
|
|
43
|
+
* When true, the user has explicitly consented to link their OAuth
|
|
44
|
+
* provider to an existing local account with the same email.
|
|
45
|
+
* Without consent, an email-match throws OAuthAccountConflictError.
|
|
46
|
+
*/
|
|
47
|
+
linkConsent?: boolean;
|
|
48
|
+
}
|
|
38
49
|
/**
|
|
39
50
|
* Find or create a local user for the given OAuth identity.
|
|
40
51
|
*
|
|
41
52
|
* Flow:
|
|
42
53
|
* 1. Look up oauth_accounts by (provider, providerUserId) → get userId
|
|
43
54
|
* 2. If found: refresh metadata + return user
|
|
44
|
-
* 3. If not found: check users by email → link if match
|
|
45
|
-
* 4. If no match: create new user (role: '
|
|
55
|
+
* 3. If not found: check users by email → link if match (with consent) or throw
|
|
56
|
+
* 4. If no match: create new user (role: 'user', no password)
|
|
46
57
|
* 5. Insert oauth_accounts row
|
|
47
58
|
*/
|
|
48
|
-
export declare function upsertOAuthUser(provider: string, providerUser: ProviderUser): Promise<User>;
|
|
59
|
+
export declare function upsertOAuthUser(provider: string, providerUser: ProviderUser, options?: UpsertOAuthOptions): Promise<User>;
|
|
49
60
|
/**
|
|
50
61
|
* Link an OAuth provider to an existing authenticated user.
|
|
51
62
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/server/oauth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAMxC,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAMD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/server/oauth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAMxC,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAMD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAkBxC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACrC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAqDxE;AAwBD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CASzF;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CAQvB;AAMD,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,YAAY,EAC1B,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAmGf;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,IAAI,CAAC,CAiEf;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCxF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC,CAajG"}
|
package/dist/server/oauth.js
CHANGED
|
@@ -24,9 +24,14 @@ import * as vercel from './providers/vercel.js';
|
|
|
24
24
|
* Cookie value is `<state>.<hmac>` — the HMAC is over the state string
|
|
25
25
|
* using REVEALUI_SECRET, providing CSRF protection without a DB table.
|
|
26
26
|
*/
|
|
27
|
-
export function generateOAuthState(provider, redirectTo) {
|
|
27
|
+
export function generateOAuthState(provider, redirectTo, options) {
|
|
28
28
|
const nonce = crypto.randomBytes(16).toString('hex');
|
|
29
|
-
const payload = JSON.stringify({
|
|
29
|
+
const payload = JSON.stringify({
|
|
30
|
+
provider,
|
|
31
|
+
redirectTo,
|
|
32
|
+
nonce,
|
|
33
|
+
...(options?.linkConsent ? { linkConsent: true } : {}),
|
|
34
|
+
});
|
|
30
35
|
const state = Buffer.from(payload).toString('base64url');
|
|
31
36
|
const secret = process.env.REVEALUI_SECRET;
|
|
32
37
|
if (!secret) {
|
|
@@ -75,7 +80,11 @@ export function verifyOAuthState(state, cookieValue) {
|
|
|
75
80
|
}
|
|
76
81
|
try {
|
|
77
82
|
const parsed = JSON.parse(Buffer.from(state, 'base64url').toString());
|
|
78
|
-
return {
|
|
83
|
+
return {
|
|
84
|
+
provider: parsed.provider,
|
|
85
|
+
redirectTo: parsed.redirectTo,
|
|
86
|
+
...(parsed.linkConsent ? { linkConsent: true } : {}),
|
|
87
|
+
};
|
|
79
88
|
}
|
|
80
89
|
catch {
|
|
81
90
|
return null;
|
|
@@ -130,20 +139,17 @@ export async function fetchProviderUser(provider, accessToken) {
|
|
|
130
139
|
};
|
|
131
140
|
return fetchers[provider](accessToken);
|
|
132
141
|
}
|
|
133
|
-
// =============================================================================
|
|
134
|
-
// User Upsert
|
|
135
|
-
// =============================================================================
|
|
136
142
|
/**
|
|
137
143
|
* Find or create a local user for the given OAuth identity.
|
|
138
144
|
*
|
|
139
145
|
* Flow:
|
|
140
146
|
* 1. Look up oauth_accounts by (provider, providerUserId) → get userId
|
|
141
147
|
* 2. If found: refresh metadata + return user
|
|
142
|
-
* 3. If not found: check users by email → link if match
|
|
143
|
-
* 4. If no match: create new user (role: '
|
|
148
|
+
* 3. If not found: check users by email → link if match (with consent) or throw
|
|
149
|
+
* 4. If no match: create new user (role: 'user', no password)
|
|
144
150
|
* 5. Insert oauth_accounts row
|
|
145
151
|
*/
|
|
146
|
-
export async function upsertOAuthUser(provider, providerUser) {
|
|
152
|
+
export async function upsertOAuthUser(provider, providerUser, options) {
|
|
147
153
|
const db = getClient();
|
|
148
154
|
// 1. Check for existing linked account
|
|
149
155
|
const [existingAccount] = await db
|
|
@@ -173,12 +179,11 @@ export async function upsertOAuthUser(provider, providerUser) {
|
|
|
173
179
|
}
|
|
174
180
|
return user;
|
|
175
181
|
}
|
|
176
|
-
// 2. Check for existing user by email
|
|
182
|
+
// 2. Check for existing user by email
|
|
177
183
|
// If an account with this email already exists but was not linked via OAuth,
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
// via linkOAuthAccount().
|
|
184
|
+
// either link it (when the user has given explicit consent) or reject the
|
|
185
|
+
// login. Auto-linking without consent is an account takeover vector: an
|
|
186
|
+
// attacker who controls a provider email instantly owns the existing account.
|
|
182
187
|
let userId;
|
|
183
188
|
let isNewUser = false;
|
|
184
189
|
if (providerUser.email) {
|
|
@@ -188,10 +193,20 @@ export async function upsertOAuthUser(provider, providerUser) {
|
|
|
188
193
|
.where(eq(users.email, providerUser.email))
|
|
189
194
|
.limit(1);
|
|
190
195
|
if (existingUser) {
|
|
191
|
-
|
|
196
|
+
if (options?.linkConsent) {
|
|
197
|
+
// User explicitly consented to link — use the existing account
|
|
198
|
+
userId = existingUser.id;
|
|
199
|
+
isNewUser = false;
|
|
200
|
+
logger.info(`Linking ${provider} account to existing user ${userId} (consent-based)`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
throw new OAuthAccountConflictError(providerUser.email);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
isNewUser = true;
|
|
208
|
+
userId = crypto.randomUUID();
|
|
192
209
|
}
|
|
193
|
-
isNewUser = true;
|
|
194
|
-
userId = crypto.randomUUID();
|
|
195
210
|
}
|
|
196
211
|
else {
|
|
197
212
|
isNewUser = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../../src/server/providers/github.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOzF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../../src/server/providers/github.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOzF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoCrF;AAED,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAkD1E"}
|
|
@@ -20,7 +20,6 @@ export async function exchangeCode(code, redirectUri) {
|
|
|
20
20
|
method: 'POST',
|
|
21
21
|
headers: {
|
|
22
22
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
23
|
-
// biome-ignore lint/style/useNamingConvention: HTTP header names are case-sensitive per RFC 7230
|
|
24
23
|
Accept: 'application/json',
|
|
25
24
|
},
|
|
26
25
|
body: new URLSearchParams({
|
|
@@ -52,9 +51,7 @@ export async function exchangeCode(code, redirectUri) {
|
|
|
52
51
|
}
|
|
53
52
|
export async function fetchUser(accessToken) {
|
|
54
53
|
const headers = {
|
|
55
|
-
// biome-ignore lint/style/useNamingConvention: HTTP header names are case-sensitive per RFC 7230
|
|
56
54
|
Authorization: `Bearer ${accessToken}`,
|
|
57
|
-
// biome-ignore lint/style/useNamingConvention: HTTP header names are case-sensitive per RFC 7230
|
|
58
55
|
Accept: 'application/vnd.github+json',
|
|
59
56
|
};
|
|
60
57
|
const userResponse = await fetch('https://api.github.com/user', { headers });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../../src/server/providers/google.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CASzF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BrF;AAED,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../../src/server/providers/google.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CASzF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BrF;AAED,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA+B1E"}
|
|
@@ -45,7 +45,6 @@ export async function exchangeCode(code, redirectUri) {
|
|
|
45
45
|
}
|
|
46
46
|
export async function fetchUser(accessToken) {
|
|
47
47
|
const response = await fetch('https://openidconnect.googleapis.com/v1/userinfo', {
|
|
48
|
-
// biome-ignore lint/style/useNamingConvention: HTTP header names are case-sensitive per RFC 7230
|
|
49
48
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
50
49
|
});
|
|
51
50
|
if (!response.ok) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel.d.ts","sourceRoot":"","sources":["../../../src/server/providers/vercel.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BrF;AAED,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"vercel.d.ts","sourceRoot":"","sources":["../../../src/server/providers/vercel.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BrF;AAED,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAiC1E"}
|
|
@@ -38,7 +38,6 @@ export async function exchangeCode(code, redirectUri) {
|
|
|
38
38
|
}
|
|
39
39
|
export async function fetchUser(accessToken) {
|
|
40
40
|
const response = await fetch('https://api.vercel.com/v2/user', {
|
|
41
|
-
// biome-ignore lint/style/useNamingConvention: HTTP header names are case-sensitive per RFC 7230
|
|
42
41
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
43
42
|
});
|
|
44
43
|
if (!response.ok) {
|
package/dist/server/session.d.ts
CHANGED
|
@@ -33,6 +33,18 @@ export interface SessionData {
|
|
|
33
33
|
session: Session;
|
|
34
34
|
user: User;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if a session is a recovery session (created via magic link recovery).
|
|
38
|
+
*
|
|
39
|
+
* Recovery sessions are restricted — they should only be used for:
|
|
40
|
+
* - Changing the password (`/api/auth/change-password`)
|
|
41
|
+
* - Signing out (`/api/auth/sign-out`)
|
|
42
|
+
* - Viewing current session (`/api/auth/me`, `/api/auth/session`)
|
|
43
|
+
*
|
|
44
|
+
* All other operations (MFA management, passkey management, OAuth linking,
|
|
45
|
+
* admin actions) should reject recovery sessions.
|
|
46
|
+
*/
|
|
47
|
+
export declare function isRecoverySession(sessionData: SessionData | null): boolean;
|
|
36
48
|
/**
|
|
37
49
|
* Get session from request headers (cookie)
|
|
38
50
|
*
|
|
@@ -90,5 +102,10 @@ export declare function deleteSession(headers: Headers): Promise<boolean>;
|
|
|
90
102
|
*
|
|
91
103
|
* @param userId - User ID
|
|
92
104
|
*/
|
|
105
|
+
/**
|
|
106
|
+
* Delete all sessions for a user EXCEPT the specified session.
|
|
107
|
+
* Used for "sign out all other devices" functionality.
|
|
108
|
+
*/
|
|
109
|
+
export declare function deleteOtherUserSessions(userId: string, exceptSessionId: string): Promise<void>;
|
|
93
110
|
export declare function deleteAllUserSessions(userId: string): Promise<void>;
|
|
94
111
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAQjD,MAAM,WAAW,oBAAoB;IACnC,iEAAiE;IACjE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,+EAA+E;IAC/E,SAAS,EAAE,OAAO,CAAC;IACnB,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;CACzB;AAUD,mFAAmF;AACnF,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAEtF;AAED,qCAAqC;AACrC,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAMD,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,MAAM,GAAG,IAAI,CA0B3F;AAMD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,OAAO,EAChB,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAQjD,MAAM,WAAW,oBAAoB;IACnC,iEAAiE;IACjE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,+EAA+E;IAC/E,SAAS,EAAE,OAAO,CAAC;IACnB,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;CACzB;AAUD,mFAAmF;AACnF,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAEtF;AAED,qCAAqC;AACrC,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAMD,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,MAAM,GAAG,IAAI,CA0B3F;AAMD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAI1E;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,OAAO,EAChB,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAkH7B;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GACA,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAmE9C;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,+FAA+F;IAC/F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAoC9C;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAoBtE;AAED;;;;GAIG;AACH;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBzE"}
|
package/dist/server/session.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { logger } from '@revealui/core/observability/logger';
|
|
8
8
|
import { getClient } from '@revealui/db/client';
|
|
9
9
|
import { sessions, users } from '@revealui/db/schema';
|
|
10
|
-
import { and, eq, gt, isNull } from 'drizzle-orm';
|
|
10
|
+
import { and, eq, gt, isNull, ne } from 'drizzle-orm';
|
|
11
11
|
import { hashToken } from '../utils/token.js';
|
|
12
12
|
import { DatabaseError, TokenError } from './errors.js';
|
|
13
13
|
const DEFAULT_SESSION_BINDING = {
|
|
@@ -54,6 +54,23 @@ export function validateSessionBinding(session, ctx) {
|
|
|
54
54
|
}
|
|
55
55
|
return null;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Check if a session is a recovery session (created via magic link recovery).
|
|
59
|
+
*
|
|
60
|
+
* Recovery sessions are restricted — they should only be used for:
|
|
61
|
+
* - Changing the password (`/api/auth/change-password`)
|
|
62
|
+
* - Signing out (`/api/auth/sign-out`)
|
|
63
|
+
* - Viewing current session (`/api/auth/me`, `/api/auth/session`)
|
|
64
|
+
*
|
|
65
|
+
* All other operations (MFA management, passkey management, OAuth linking,
|
|
66
|
+
* admin actions) should reject recovery sessions.
|
|
67
|
+
*/
|
|
68
|
+
export function isRecoverySession(sessionData) {
|
|
69
|
+
if (!sessionData)
|
|
70
|
+
return false;
|
|
71
|
+
const metadata = sessionData.session.metadata;
|
|
72
|
+
return metadata?.recovery === true;
|
|
73
|
+
}
|
|
57
74
|
/**
|
|
58
75
|
* Get session from request headers (cookie)
|
|
59
76
|
*
|
|
@@ -95,7 +112,7 @@ export async function getSession(headers, requestContext) {
|
|
|
95
112
|
const result = await db
|
|
96
113
|
.select()
|
|
97
114
|
.from(sessions)
|
|
98
|
-
.where(and(eq(sessions.tokenHash, tokenHash), gt(sessions.expiresAt, new Date())))
|
|
115
|
+
.where(and(eq(sessions.tokenHash, tokenHash), gt(sessions.expiresAt, new Date()), isNull(sessions.deletedAt)))
|
|
99
116
|
.limit(1);
|
|
100
117
|
session = result[0];
|
|
101
118
|
}
|
|
@@ -317,6 +334,38 @@ export async function deleteSession(headers) {
|
|
|
317
334
|
*
|
|
318
335
|
* @param userId - User ID
|
|
319
336
|
*/
|
|
337
|
+
/**
|
|
338
|
+
* Delete all sessions for a user EXCEPT the specified session.
|
|
339
|
+
* Used for "sign out all other devices" functionality.
|
|
340
|
+
*/
|
|
341
|
+
export async function deleteOtherUserSessions(userId, exceptSessionId) {
|
|
342
|
+
try {
|
|
343
|
+
let db;
|
|
344
|
+
try {
|
|
345
|
+
db = getClient();
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
logger.error('Error getting database client');
|
|
349
|
+
throw new DatabaseError('Database connection failed', error);
|
|
350
|
+
}
|
|
351
|
+
try {
|
|
352
|
+
await db
|
|
353
|
+
.delete(sessions)
|
|
354
|
+
.where(and(eq(sessions.userId, userId), ne(sessions.id, exceptSessionId)));
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
logger.error('Error deleting other user sessions');
|
|
358
|
+
throw new DatabaseError('Failed to delete other user sessions', error);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
catch (err) {
|
|
362
|
+
if (err instanceof DatabaseError) {
|
|
363
|
+
throw err;
|
|
364
|
+
}
|
|
365
|
+
logger.error('Unexpected error in deleteOtherUserSessions');
|
|
366
|
+
throw new DatabaseError('Unexpected error deleting other user sessions', err);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
320
369
|
export async function deleteAllUserSessions(userId) {
|
|
321
370
|
try {
|
|
322
371
|
let db;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iBAAiB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACnC,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iBAAiB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACnC,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,IAAI,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,0GAA0G;AAC1G,MAAM,MAAM,YAAY,GACpB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACxE;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACvD;IACE,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EACF,qBAAqB,GACrB,gBAAgB,GAChB,cAAc,GACd,gBAAgB,GAChB,eAAe,GACf,oBAAoB,GACpB,kBAAkB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revealui/auth",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Authentication system for RevealUI - database-backed sessions with Better Auth patterns",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -12,23 +12,23 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@simplewebauthn/server": "^13.3.0",
|
|
14
14
|
"bcryptjs": "^3.0.3",
|
|
15
|
-
"drizzle-orm": "^0.45.
|
|
15
|
+
"drizzle-orm": "^0.45.2",
|
|
16
16
|
"zod": "^4.3.6",
|
|
17
|
-
"@revealui/
|
|
18
|
-
"@revealui/db": "0.3.0",
|
|
17
|
+
"@revealui/contracts": "1.3.1",
|
|
19
18
|
"@revealui/config": "0.3.0",
|
|
20
|
-
"@revealui/
|
|
19
|
+
"@revealui/db": "0.3.1",
|
|
20
|
+
"@revealui/core": "0.5.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@simplewebauthn/browser": "^13.3.0",
|
|
24
24
|
"@testing-library/react": "^16.3.2",
|
|
25
|
-
"@types/node": "^25.
|
|
25
|
+
"@types/node": "^25.5.0",
|
|
26
26
|
"@types/react": "^19.2.14",
|
|
27
|
-
"@vitest/coverage-v8": "^4.0
|
|
27
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
28
28
|
"happy-dom": "^20.8.4",
|
|
29
29
|
"react": "^19.2.3",
|
|
30
|
-
"typescript": "^
|
|
31
|
-
"vitest": "^4.0
|
|
30
|
+
"typescript": "^6.0.2",
|
|
31
|
+
"vitest": "^4.1.0",
|
|
32
32
|
"dev": "0.0.1"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|