@revealui/auth 0.4.4 → 0.4.5
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/react/csrf.d.ts +17 -0
- package/dist/react/csrf.d.ts.map +1 -0
- package/dist/react/csrf.js +36 -0
- package/dist/react/useMFA.d.ts +10 -1
- package/dist/react/useMFA.d.ts.map +1 -1
- package/dist/react/useMFA.js +30 -4
- package/dist/react/usePasskey.d.ts +9 -1
- package/dist/react/usePasskey.d.ts.map +1 -1
- package/dist/react/usePasskey.js +29 -4
- package/dist/react/useSignIn.d.ts +2 -1
- package/dist/react/useSignIn.d.ts.map +1 -1
- package/dist/react/useSignIn.js +2 -1
- package/dist/react/useSignOut.d.ts +8 -0
- package/dist/react/useSignOut.d.ts.map +1 -1
- package/dist/react/useSignOut.js +13 -0
- package/dist/react/useSignUp.d.ts +2 -1
- package/dist/react/useSignUp.d.ts.map +1 -1
- package/dist/react/useSignUp.js +2 -1
- package/dist/server/session.d.ts +7 -1
- package/dist/server/session.d.ts.map +1 -1
- package/dist/server/session.js +48 -6
- package/package.json +2 -2
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSRF Double-Submit Token Helper
|
|
3
|
+
*
|
|
4
|
+
* Shared by the react auth hooks (`usePasskey`, `useMFA`, `useSignOut`) whose
|
|
5
|
+
* POSTs target the RevealUI admin proxy's CSRF-gated endpoints.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Read the JS-readable `revealui-csrf` cookie. The RevealUI admin proxy
|
|
9
|
+
* (`apps/admin/src/proxy.ts`) requires it as an `X-CSRF-Token` header on any
|
|
10
|
+
* session-cookie-bearing unsafe request, and may re-issue the cookie on any
|
|
11
|
+
* response — so call this immediately before each fetch rather than once per
|
|
12
|
+
* flow. Returns undefined outside the browser or when the cookie is
|
|
13
|
+
* absent/empty. Mirrors the cookie-read in `@revealui/core`'s admin APIClient
|
|
14
|
+
* (`request()`) and `@revealui/ai`'s `useAgentStream`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function readCsrfToken(): string | undefined;
|
|
17
|
+
//# sourceMappingURL=csrf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../src/react/csrf.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAgBlD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSRF Double-Submit Token Helper
|
|
3
|
+
*
|
|
4
|
+
* Shared by the react auth hooks (`usePasskey`, `useMFA`, `useSignOut`) whose
|
|
5
|
+
* POSTs target the RevealUI admin proxy's CSRF-gated endpoints.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Read the JS-readable `revealui-csrf` cookie. The RevealUI admin proxy
|
|
9
|
+
* (`apps/admin/src/proxy.ts`) requires it as an `X-CSRF-Token` header on any
|
|
10
|
+
* session-cookie-bearing unsafe request, and may re-issue the cookie on any
|
|
11
|
+
* response — so call this immediately before each fetch rather than once per
|
|
12
|
+
* flow. Returns undefined outside the browser or when the cookie is
|
|
13
|
+
* absent/empty. Mirrors the cookie-read in `@revealui/core`'s admin APIClient
|
|
14
|
+
* (`request()`) and `@revealui/ai`'s `useAgentStream`.
|
|
15
|
+
*/
|
|
16
|
+
export function readCsrfToken() {
|
|
17
|
+
if (typeof document === 'undefined')
|
|
18
|
+
return undefined;
|
|
19
|
+
for (const part of document.cookie.split(';')) {
|
|
20
|
+
const eqIdx = part.indexOf('=');
|
|
21
|
+
if (eqIdx === -1)
|
|
22
|
+
continue;
|
|
23
|
+
if (part.slice(0, eqIdx).trim() === 'revealui-csrf') {
|
|
24
|
+
const raw = part.slice(eqIdx + 1).trim();
|
|
25
|
+
if (!raw)
|
|
26
|
+
return undefined;
|
|
27
|
+
try {
|
|
28
|
+
return decodeURIComponent(raw);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return raw;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
package/dist/react/useMFA.d.ts
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
* MFA Hooks
|
|
3
3
|
*
|
|
4
4
|
* React hooks for Multi-Factor Authentication setup and verification.
|
|
5
|
+
*
|
|
6
|
+
* CSRF: every POST echoes the JS-readable `revealui-csrf` cookie (the signed
|
|
7
|
+
* double-submit token the RevealUI admin proxy issues on page load) as an
|
|
8
|
+
* `X-CSRF-Token` header when the cookie is present. The proxy requires it on
|
|
9
|
+
* session-cookie-bearing unsafe requests and `/api/auth/mfa/*` is not exempt —
|
|
10
|
+
* MFA enrollment always runs with a session, so without the header it 403s
|
|
11
|
+
* ("CSRF token missing"). Cookie-less callers send no header and are
|
|
12
|
+
* unchanged.
|
|
5
13
|
*/
|
|
6
14
|
/**
|
|
7
15
|
* MFA setup data returned when initiating TOTP setup.
|
|
@@ -73,7 +81,8 @@ export interface UseMFAVerifyResult {
|
|
|
73
81
|
* const handleSubmit = async (code: string) => {
|
|
74
82
|
* const success = await verify(code);
|
|
75
83
|
* if (success) {
|
|
76
|
-
* router.push(
|
|
84
|
+
* // Full navigation: a soft router.push() would replay the pre-auth Router Cache
|
|
85
|
+
* window.location.href = '/admin';
|
|
77
86
|
* }
|
|
78
87
|
* };
|
|
79
88
|
* }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMFA.d.ts","sourceRoot":"","sources":["../../src/react/useMFA.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"useMFA.d.ts","sourceRoot":"","sources":["../../src/react/useMFA.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,KAAK,EAAE,MAAM,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC1C,0CAA0C;IAC1C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,WAAW,IAAI,iBAAiB,CA6E/C;AAED,MAAM,WAAW,kBAAkB;IACjC,sCAAsC;IACtC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,wCAAwC;IACxC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,IAAI,kBAAkB,CA8EjD"}
|
package/dist/react/useMFA.js
CHANGED
|
@@ -2,9 +2,18 @@
|
|
|
2
2
|
* MFA Hooks
|
|
3
3
|
*
|
|
4
4
|
* React hooks for Multi-Factor Authentication setup and verification.
|
|
5
|
+
*
|
|
6
|
+
* CSRF: every POST echoes the JS-readable `revealui-csrf` cookie (the signed
|
|
7
|
+
* double-submit token the RevealUI admin proxy issues on page load) as an
|
|
8
|
+
* `X-CSRF-Token` header when the cookie is present. The proxy requires it on
|
|
9
|
+
* session-cookie-bearing unsafe requests and `/api/auth/mfa/*` is not exempt —
|
|
10
|
+
* MFA enrollment always runs with a session, so without the header it 403s
|
|
11
|
+
* ("CSRF token missing"). Cookie-less callers send no header and are
|
|
12
|
+
* unchanged.
|
|
5
13
|
*/
|
|
6
14
|
'use client';
|
|
7
15
|
import { useState } from 'react';
|
|
16
|
+
import { readCsrfToken } from './csrf.js';
|
|
8
17
|
/**
|
|
9
18
|
* Hook for MFA setup on the security settings page.
|
|
10
19
|
*
|
|
@@ -38,9 +47,13 @@ export function useMFASetup() {
|
|
|
38
47
|
try {
|
|
39
48
|
setIsLoading(true);
|
|
40
49
|
setError(null);
|
|
50
|
+
const csrfToken = readCsrfToken();
|
|
41
51
|
const response = await fetch('/api/auth/mfa/setup', {
|
|
42
52
|
method: 'POST',
|
|
43
53
|
credentials: 'include',
|
|
54
|
+
// Conditional headers key keeps cookie-less setup requests
|
|
55
|
+
// byte-identical to the pre-CSRF request shape.
|
|
56
|
+
...(csrfToken ? { headers: { 'X-CSRF-Token': csrfToken } } : {}),
|
|
44
57
|
});
|
|
45
58
|
const json = await response.json();
|
|
46
59
|
if (!response.ok) {
|
|
@@ -64,9 +77,13 @@ export function useMFASetup() {
|
|
|
64
77
|
try {
|
|
65
78
|
setIsLoading(true);
|
|
66
79
|
setError(null);
|
|
80
|
+
const csrfToken = readCsrfToken();
|
|
67
81
|
const response = await fetch('/api/auth/mfa/verify-setup', {
|
|
68
82
|
method: 'POST',
|
|
69
|
-
headers: {
|
|
83
|
+
headers: {
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
...(csrfToken ? { 'X-CSRF-Token': csrfToken } : {}),
|
|
86
|
+
},
|
|
70
87
|
credentials: 'include',
|
|
71
88
|
body: JSON.stringify({ code }),
|
|
72
89
|
});
|
|
@@ -110,7 +127,8 @@ export function useMFASetup() {
|
|
|
110
127
|
* const handleSubmit = async (code: string) => {
|
|
111
128
|
* const success = await verify(code);
|
|
112
129
|
* if (success) {
|
|
113
|
-
* router.push(
|
|
130
|
+
* // Full navigation: a soft router.push() would replay the pre-auth Router Cache
|
|
131
|
+
* window.location.href = '/admin';
|
|
114
132
|
* }
|
|
115
133
|
* };
|
|
116
134
|
* }
|
|
@@ -123,9 +141,13 @@ export function useMFAVerify() {
|
|
|
123
141
|
try {
|
|
124
142
|
setIsLoading(true);
|
|
125
143
|
setError(null);
|
|
144
|
+
const csrfToken = readCsrfToken();
|
|
126
145
|
const response = await fetch('/api/auth/mfa/verify', {
|
|
127
146
|
method: 'POST',
|
|
128
|
-
headers: {
|
|
147
|
+
headers: {
|
|
148
|
+
'Content-Type': 'application/json',
|
|
149
|
+
...(csrfToken ? { 'X-CSRF-Token': csrfToken } : {}),
|
|
150
|
+
},
|
|
129
151
|
credentials: 'include',
|
|
130
152
|
body: JSON.stringify({ code }),
|
|
131
153
|
});
|
|
@@ -150,9 +172,13 @@ export function useMFAVerify() {
|
|
|
150
172
|
try {
|
|
151
173
|
setIsLoading(true);
|
|
152
174
|
setError(null);
|
|
175
|
+
const csrfToken = readCsrfToken();
|
|
153
176
|
const response = await fetch('/api/auth/mfa/backup', {
|
|
154
177
|
method: 'POST',
|
|
155
|
-
headers: {
|
|
178
|
+
headers: {
|
|
179
|
+
'Content-Type': 'application/json',
|
|
180
|
+
...(csrfToken ? { 'X-CSRF-Token': csrfToken } : {}),
|
|
181
|
+
},
|
|
156
182
|
credentials: 'include',
|
|
157
183
|
body: JSON.stringify({ code }),
|
|
158
184
|
});
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* React hooks for WebAuthn passkey registration and authentication.
|
|
5
5
|
* Uses dynamic imports for @simplewebauthn/browser to avoid SSR issues.
|
|
6
|
+
*
|
|
7
|
+
* CSRF: every POST echoes the JS-readable `revealui-csrf` cookie (the signed
|
|
8
|
+
* double-submit token the RevealUI admin proxy issues on page load) as an
|
|
9
|
+
* `X-CSRF-Token` header when the cookie is present. The proxy requires it on
|
|
10
|
+
* session-cookie-bearing unsafe requests — registration always runs with a
|
|
11
|
+
* session, so without the header it 403s ("CSRF token missing"). Cookie-less
|
|
12
|
+
* callers (no admin session) send no header and are unchanged.
|
|
6
13
|
*/
|
|
7
14
|
export interface PasskeyRegisterOptions {
|
|
8
15
|
/** Email for passkey registration (sign-up flow) */
|
|
@@ -67,7 +74,8 @@ export interface UsePasskeySignInResult {
|
|
|
67
74
|
* const handlePasskeyLogin = async () => {
|
|
68
75
|
* const success = await signIn();
|
|
69
76
|
* if (success) {
|
|
70
|
-
* router.push(
|
|
77
|
+
* // Full navigation: a soft router.push() would replay the pre-auth Router Cache
|
|
78
|
+
* window.location.href = '/admin';
|
|
71
79
|
* }
|
|
72
80
|
* };
|
|
73
81
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePasskey.d.ts","sourceRoot":"","sources":["../../src/react/usePasskey.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"usePasskey.d.ts","sourceRoot":"","sources":["../../src/react/usePasskey.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,MAAM,WAAW,sBAAsB;IACrC,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,wCAAwC;IACxC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,sBAAsB,KAAK,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;IACtF,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4CAA4C;IAC5C,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,IAAI,wBAAwB,CAkG7D;AAED,MAAM,WAAW,sBAAsB;IACrC,kCAAkC;IAClC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4CAA4C;IAC5C,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,gBAAgB,IAAI,sBAAsB,CAsFzD"}
|
package/dist/react/usePasskey.js
CHANGED
|
@@ -3,9 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* React hooks for WebAuthn passkey registration and authentication.
|
|
5
5
|
* Uses dynamic imports for @simplewebauthn/browser to avoid SSR issues.
|
|
6
|
+
*
|
|
7
|
+
* CSRF: every POST echoes the JS-readable `revealui-csrf` cookie (the signed
|
|
8
|
+
* double-submit token the RevealUI admin proxy issues on page load) as an
|
|
9
|
+
* `X-CSRF-Token` header when the cookie is present. The proxy requires it on
|
|
10
|
+
* session-cookie-bearing unsafe requests — registration always runs with a
|
|
11
|
+
* session, so without the header it 403s ("CSRF token missing"). Cookie-less
|
|
12
|
+
* callers (no admin session) send no header and are unchanged.
|
|
6
13
|
*/
|
|
7
14
|
'use client';
|
|
8
15
|
import { useEffect, useState } from 'react';
|
|
16
|
+
import { readCsrfToken } from './csrf.js';
|
|
9
17
|
/**
|
|
10
18
|
* Hook for passkey registration (sign-up and security settings).
|
|
11
19
|
*
|
|
@@ -43,9 +51,13 @@ export function usePasskeyRegister() {
|
|
|
43
51
|
setIsLoading(true);
|
|
44
52
|
setError(null);
|
|
45
53
|
// Step 1: Get registration options from the server
|
|
54
|
+
const optionsCsrfToken = readCsrfToken();
|
|
46
55
|
const optionsResponse = await fetch('/api/auth/passkey/register-options', {
|
|
47
56
|
method: 'POST',
|
|
48
|
-
headers: {
|
|
57
|
+
headers: {
|
|
58
|
+
'Content-Type': 'application/json',
|
|
59
|
+
...(optionsCsrfToken ? { 'X-CSRF-Token': optionsCsrfToken } : {}),
|
|
60
|
+
},
|
|
49
61
|
credentials: 'include',
|
|
50
62
|
body: JSON.stringify({
|
|
51
63
|
email: options?.email,
|
|
@@ -65,9 +77,13 @@ export function usePasskeyRegister() {
|
|
|
65
77
|
const optionsData = optionsJson;
|
|
66
78
|
const attestationResponse = await startRegistration({ optionsJSON: optionsData.options });
|
|
67
79
|
// Step 3: Verify with the server
|
|
80
|
+
const verifyCsrfToken = readCsrfToken();
|
|
68
81
|
const verifyResponse = await fetch('/api/auth/passkey/register-verify', {
|
|
69
82
|
method: 'POST',
|
|
70
|
-
headers: {
|
|
83
|
+
headers: {
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
...(verifyCsrfToken ? { 'X-CSRF-Token': verifyCsrfToken } : {}),
|
|
86
|
+
},
|
|
71
87
|
credentials: 'include',
|
|
72
88
|
body: JSON.stringify({
|
|
73
89
|
attestationResponse,
|
|
@@ -117,7 +133,8 @@ export function usePasskeyRegister() {
|
|
|
117
133
|
* const handlePasskeyLogin = async () => {
|
|
118
134
|
* const success = await signIn();
|
|
119
135
|
* if (success) {
|
|
120
|
-
* router.push(
|
|
136
|
+
* // Full navigation: a soft router.push() would replay the pre-auth Router Cache
|
|
137
|
+
* window.location.href = '/admin';
|
|
121
138
|
* }
|
|
122
139
|
* };
|
|
123
140
|
*
|
|
@@ -150,9 +167,13 @@ export function usePasskeySignIn() {
|
|
|
150
167
|
setIsLoading(true);
|
|
151
168
|
setError(null);
|
|
152
169
|
// Step 1: Get authentication options from the server
|
|
170
|
+
const optionsCsrfToken = readCsrfToken();
|
|
153
171
|
const optionsResponse = await fetch('/api/auth/passkey/authenticate-options', {
|
|
154
172
|
method: 'POST',
|
|
155
173
|
credentials: 'include',
|
|
174
|
+
// Conditional headers key keeps cookie-less sign-in requests
|
|
175
|
+
// byte-identical to the pre-CSRF request shape.
|
|
176
|
+
...(optionsCsrfToken ? { headers: { 'X-CSRF-Token': optionsCsrfToken } } : {}),
|
|
156
177
|
});
|
|
157
178
|
const optionsJson = await optionsResponse.json();
|
|
158
179
|
if (!optionsResponse.ok) {
|
|
@@ -166,9 +187,13 @@ export function usePasskeySignIn() {
|
|
|
166
187
|
const authOptionsData = optionsJson;
|
|
167
188
|
const assertionResponse = await startAuthentication({ optionsJSON: authOptionsData.options });
|
|
168
189
|
// Step 3: Verify with the server
|
|
190
|
+
const verifyCsrfToken = readCsrfToken();
|
|
169
191
|
const verifyResponse = await fetch('/api/auth/passkey/authenticate-verify', {
|
|
170
192
|
method: 'POST',
|
|
171
|
-
headers: {
|
|
193
|
+
headers: {
|
|
194
|
+
'Content-Type': 'application/json',
|
|
195
|
+
...(verifyCsrfToken ? { 'X-CSRF-Token': verifyCsrfToken } : {}),
|
|
196
|
+
},
|
|
172
197
|
credentials: 'include',
|
|
173
198
|
body: JSON.stringify({ authenticationResponse: assertionResponse }),
|
|
174
199
|
});
|
|
@@ -37,7 +37,8 @@ export interface UseSignInResult {
|
|
|
37
37
|
* e.preventDefault()
|
|
38
38
|
* const result = await signIn({ email, password })
|
|
39
39
|
* if (result.success) {
|
|
40
|
-
* router.push(
|
|
40
|
+
* // Full navigation: a soft router.push() would replay the pre-auth Router Cache
|
|
41
|
+
* window.location.href = '/dashboard'
|
|
41
42
|
* }
|
|
42
43
|
* }
|
|
43
44
|
* }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSignIn.d.ts","sourceRoot":"","sources":["../../src/react/useSignIn.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAkCxC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,CACN,KAAK,EAAE,WAAW,KACf,OAAO,CACR;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,GAC7B;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GACjC;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,WAAW,EAAE,IAAI,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAC3D,CAAC;IACF,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED
|
|
1
|
+
{"version":3,"file":"useSignIn.d.ts","sourceRoot":"","sources":["../../src/react/useSignIn.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAkCxC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,CACN,KAAK,EAAE,WAAW,KACf,OAAO,CACR;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,GAC7B;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GACjC;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,WAAW,EAAE,IAAI,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAC3D,CAAC;IACF,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,SAAS,IAAI,eAAe,CAsE3C"}
|
package/dist/react/useSignIn.js
CHANGED
|
@@ -48,7 +48,8 @@ const SignInSuccessResponseSchema = z.object({
|
|
|
48
48
|
* e.preventDefault()
|
|
49
49
|
* const result = await signIn({ email, password })
|
|
50
50
|
* if (result.success) {
|
|
51
|
-
* router.push(
|
|
51
|
+
* // Full navigation: a soft router.push() would replay the pre-auth Router Cache
|
|
52
|
+
* window.location.href = '/dashboard'
|
|
52
53
|
* }
|
|
53
54
|
* }
|
|
54
55
|
* }
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
* useSignOut Hook
|
|
3
3
|
*
|
|
4
4
|
* React hook for signing out a user.
|
|
5
|
+
*
|
|
6
|
+
* CSRF: the sign-out POST echoes the JS-readable `revealui-csrf` cookie (the
|
|
7
|
+
* signed double-submit token the RevealUI admin proxy issues on page load) as
|
|
8
|
+
* an `X-CSRF-Token` header when the cookie is present. The proxy requires it
|
|
9
|
+
* on session-cookie-bearing unsafe requests and `/api/auth/sign-out` is not
|
|
10
|
+
* exempt — sign-out always carries a session, so without the header it 403s
|
|
11
|
+
* ("CSRF token missing") and the server-side session survives. Cookie-less
|
|
12
|
+
* callers send no header and are unchanged.
|
|
5
13
|
*/
|
|
6
14
|
export interface UseSignOutResult {
|
|
7
15
|
signOut: () => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSignOut.d.ts","sourceRoot":"","sources":["../../src/react/useSignOut.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"useSignOut.d.ts","sourceRoot":"","sources":["../../src/react/useSignOut.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,IAAI,gBAAgB,CAsC7C"}
|
package/dist/react/useSignOut.js
CHANGED
|
@@ -2,9 +2,18 @@
|
|
|
2
2
|
* useSignOut Hook
|
|
3
3
|
*
|
|
4
4
|
* React hook for signing out a user.
|
|
5
|
+
*
|
|
6
|
+
* CSRF: the sign-out POST echoes the JS-readable `revealui-csrf` cookie (the
|
|
7
|
+
* signed double-submit token the RevealUI admin proxy issues on page load) as
|
|
8
|
+
* an `X-CSRF-Token` header when the cookie is present. The proxy requires it
|
|
9
|
+
* on session-cookie-bearing unsafe requests and `/api/auth/sign-out` is not
|
|
10
|
+
* exempt — sign-out always carries a session, so without the header it 403s
|
|
11
|
+
* ("CSRF token missing") and the server-side session survives. Cookie-less
|
|
12
|
+
* callers send no header and are unchanged.
|
|
5
13
|
*/
|
|
6
14
|
'use client';
|
|
7
15
|
import { useState } from 'react';
|
|
16
|
+
import { readCsrfToken } from './csrf.js';
|
|
8
17
|
/**
|
|
9
18
|
* Hook to sign out a user
|
|
10
19
|
*
|
|
@@ -30,9 +39,13 @@ export function useSignOut() {
|
|
|
30
39
|
try {
|
|
31
40
|
setIsLoading(true);
|
|
32
41
|
setError(null);
|
|
42
|
+
const csrfToken = readCsrfToken();
|
|
33
43
|
const response = await fetch('/api/auth/sign-out', {
|
|
34
44
|
method: 'POST',
|
|
35
45
|
credentials: 'include',
|
|
46
|
+
// Conditional headers key keeps cookie-less sign-out requests
|
|
47
|
+
// byte-identical to the pre-CSRF request shape.
|
|
48
|
+
...(csrfToken ? { headers: { 'X-CSRF-Token': csrfToken } } : {}),
|
|
36
49
|
});
|
|
37
50
|
if (!response.ok) {
|
|
38
51
|
throw new Error('Failed to sign out');
|
|
@@ -33,7 +33,8 @@ export interface UseSignUpResult {
|
|
|
33
33
|
* e.preventDefault()
|
|
34
34
|
* const result = await signUp({ email, password, name })
|
|
35
35
|
* if (result.success) {
|
|
36
|
-
* router.push(
|
|
36
|
+
* // Full navigation: a soft router.push() would replay the pre-auth Router Cache
|
|
37
|
+
* window.location.href = '/dashboard'
|
|
37
38
|
* }
|
|
38
39
|
* }
|
|
39
40
|
* }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSignUp.d.ts","sourceRoot":"","sources":["../../src/react/useSignUp.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAmCxC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3F,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED
|
|
1
|
+
{"version":3,"file":"useSignUp.d.ts","sourceRoot":"","sources":["../../src/react/useSignUp.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAmCxC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3F,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,SAAS,IAAI,eAAe,CAoD3C"}
|
package/dist/react/useSignUp.js
CHANGED
|
@@ -49,7 +49,8 @@ const SignUpSuccessResponseSchema = z.object({
|
|
|
49
49
|
* e.preventDefault()
|
|
50
50
|
* const result = await signUp({ email, password, name })
|
|
51
51
|
* if (result.success) {
|
|
52
|
-
* router.push(
|
|
52
|
+
* // Full navigation: a soft router.push() would replay the pre-auth Router Cache
|
|
53
|
+
* window.location.href = '/dashboard'
|
|
53
54
|
* }
|
|
54
55
|
* }
|
|
55
56
|
* }
|
package/dist/server/session.d.ts
CHANGED
|
@@ -93,8 +93,14 @@ export declare function rotateSession(userId: string, options?: {
|
|
|
93
93
|
/**
|
|
94
94
|
* Delete a session (sign out)
|
|
95
95
|
*
|
|
96
|
+
* Revokes EVERY session token presented in the cookie header, not just the
|
|
97
|
+
* first: a browser can hold duplicate `revealui-session` cookies (e.g. a
|
|
98
|
+
* stale host-only cookie alongside the domain-scoped one), and RFC 6265
|
|
99
|
+
* ordering lets the stale duplicate shadow the live token. Sign-out must
|
|
100
|
+
* revoke them all.
|
|
101
|
+
*
|
|
96
102
|
* @param headers - Request headers containing session cookie
|
|
97
|
-
* @returns True if session was deleted, false
|
|
103
|
+
* @returns True if at least one session row was deleted, false otherwise
|
|
98
104
|
*/
|
|
99
105
|
export declare function deleteSession(headers: Headers): Promise<boolean>;
|
|
100
106
|
/**
|
|
@@ -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,iFAAiF;IACjF,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
|
|
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,iFAAiF;IACjF,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;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CA+BtE;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, ne } from 'drizzle-orm';
|
|
10
|
+
import { and, eq, gt, inArray, 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 = {
|
|
@@ -310,21 +310,38 @@ export async function rotateSession(userId, options) {
|
|
|
310
310
|
/**
|
|
311
311
|
* Delete a session (sign out)
|
|
312
312
|
*
|
|
313
|
+
* Revokes EVERY session token presented in the cookie header, not just the
|
|
314
|
+
* first: a browser can hold duplicate `revealui-session` cookies (e.g. a
|
|
315
|
+
* stale host-only cookie alongside the domain-scoped one), and RFC 6265
|
|
316
|
+
* ordering lets the stale duplicate shadow the live token. Sign-out must
|
|
317
|
+
* revoke them all.
|
|
318
|
+
*
|
|
313
319
|
* @param headers - Request headers containing session cookie
|
|
314
|
-
* @returns True if session was deleted, false
|
|
320
|
+
* @returns True if at least one session row was deleted, false otherwise
|
|
315
321
|
*/
|
|
316
322
|
export async function deleteSession(headers) {
|
|
317
323
|
const cookieHeader = headers.get('cookie');
|
|
318
324
|
if (!cookieHeader) {
|
|
319
325
|
return false;
|
|
320
326
|
}
|
|
321
|
-
const
|
|
322
|
-
if (
|
|
327
|
+
const sessionTokens = extractAllSessionTokens(cookieHeader);
|
|
328
|
+
if (sessionTokens.length === 0) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
const tokenHashes = [];
|
|
332
|
+
for (const sessionToken of sessionTokens) {
|
|
333
|
+
try {
|
|
334
|
+
tokenHashes.push(hashToken(sessionToken));
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
logger.error('Error hashing session token during sign-out');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (tokenHashes.length === 0) {
|
|
323
341
|
return false;
|
|
324
342
|
}
|
|
325
|
-
const tokenHash = hashToken(sessionToken);
|
|
326
343
|
const db = getClient();
|
|
327
|
-
const result = await db.delete(sessions).where(
|
|
344
|
+
const result = await db.delete(sessions).where(inArray(sessions.tokenHash, tokenHashes));
|
|
328
345
|
// Check if any rows were deleted - Drizzle delete returns result with rowCount or similar
|
|
329
346
|
const rowCount = 'rowCount' in result && typeof result.rowCount === 'number' ? result.rowCount : 0;
|
|
330
347
|
return rowCount > 0;
|
|
@@ -405,6 +422,31 @@ function extractSessionToken(cookieHeader) {
|
|
|
405
422
|
}
|
|
406
423
|
return decodeURIComponent(sessionCookie.substring('revealui-session='.length));
|
|
407
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* Extract every distinct session token from a cookie header.
|
|
427
|
+
*
|
|
428
|
+
* Duplicate `revealui-session` cookies are real: a host-only and a
|
|
429
|
+
* domain-scoped cookie with the same name travel together in one header.
|
|
430
|
+
* Deletion must see all of them; validation (getSession) deliberately keeps
|
|
431
|
+
* first-match semantics via extractSessionToken.
|
|
432
|
+
*/
|
|
433
|
+
function extractAllSessionTokens(cookieHeader) {
|
|
434
|
+
const prefix = 'revealui-session=';
|
|
435
|
+
const tokens = new Set();
|
|
436
|
+
for (const part of cookieHeader.split(';')) {
|
|
437
|
+
const cookie = part.trim();
|
|
438
|
+
if (!cookie.startsWith(prefix)) {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
try {
|
|
442
|
+
tokens.add(decodeURIComponent(cookie.substring(prefix.length)));
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
// Malformed percent-encoding in one duplicate must not abort sign-out.
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return [...tokens];
|
|
449
|
+
}
|
|
408
450
|
/**
|
|
409
451
|
* Generate a secure random session token
|
|
410
452
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revealui/auth",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "Database-backed session auth for Hono and Next.js — bcrypt, OAuth, brute-force protection, rate limiting, password reset. Ships with RevealUI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"zod": "^4.4.3",
|
|
17
17
|
"@revealui/config": "0.4.2",
|
|
18
18
|
"@revealui/contracts": "0.6.1",
|
|
19
|
-
"@revealui/core": "0.10.
|
|
19
|
+
"@revealui/core": "0.10.1",
|
|
20
20
|
"@revealui/db": "0.7.1",
|
|
21
21
|
"@revealui/security": "0.4.1"
|
|
22
22
|
},
|