@burdenoff/microfe-auth 2026.1031.1 → 2026.1101.1
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/auth/AuthRoot.js +14 -11
- package/dist/auth/AuthRoot.js.map +1 -1
- package/dist/auth/AuthRoutes.js +1 -2
- package/dist/auth/AuthRoutes.js.map +1 -1
- package/dist/auth/components/AuthIcons.js +1 -2
- package/dist/auth/components/AuthIcons.js.map +1 -1
- package/dist/auth/components/AuthLayout.js +1 -2
- package/dist/auth/components/AuthLayout.js.map +1 -1
- package/dist/auth/components/CaptchaV2.js +9 -10
- package/dist/auth/components/CaptchaV2.js.map +1 -1
- package/dist/auth/components/ConfirmActionDialog.js +1 -2
- package/dist/auth/components/ConfirmActionDialog.js.map +1 -1
- package/dist/auth/components/profile/UserProfileMenu.js +7 -6
- package/dist/auth/components/profile/UserProfileMenu.js.map +1 -1
- package/dist/auth/components/workspace/WorkspaceSelector.js +1 -2
- package/dist/auth/components/workspace/WorkspaceSelector.js.map +1 -1
- package/dist/auth/constants/permissions.js +1 -2
- package/dist/auth/constants/permissions.js.map +1 -1
- package/dist/auth/constants/sessionKeys.js +1 -2
- package/dist/auth/constants/sessionKeys.js.map +1 -1
- package/dist/auth/hooks/contextDeepLink.js +1 -2
- package/dist/auth/hooks/contextDeepLink.js.map +1 -1
- package/dist/auth/hooks/index.js +12 -11
- package/dist/auth/hooks/useAccountOperations.js +1 -2
- package/dist/auth/hooks/useAccountOperations.js.map +1 -1
- package/dist/auth/hooks/useActiveContextBootstrapMirror.js +1 -2
- package/dist/auth/hooks/useActiveContextBootstrapMirror.js.map +1 -1
- package/dist/auth/hooks/useAuthOperations.js +37 -37
- package/dist/auth/hooks/useAuthOperations.js.map +1 -1
- package/dist/auth/hooks/useAuthPermissions.js +1 -2
- package/dist/auth/hooks/useAuthPermissions.js.map +1 -1
- package/dist/auth/hooks/useCaptchaV3.js +1 -2
- package/dist/auth/hooks/useCaptchaV3.js.map +1 -1
- package/dist/auth/hooks/useContextSwitcherData.js +81 -78
- package/dist/auth/hooks/useContextSwitcherData.js.map +1 -1
- package/dist/auth/hooks/useMfaOperations.js +4 -5
- package/dist/auth/hooks/useMfaOperations.js.map +1 -1
- package/dist/auth/hooks/useMyWorkspaces.js +4 -5
- package/dist/auth/hooks/useMyWorkspaces.js.map +1 -1
- package/dist/auth/hooks/useOAuthOperations.js +11 -12
- package/dist/auth/hooks/useOAuthOperations.js.map +1 -1
- package/dist/auth/hooks/usePasswordOperations.js +1 -2
- package/dist/auth/hooks/usePasswordOperations.js.map +1 -1
- package/dist/auth/hooks/useSessionOperations.js +3 -4
- package/dist/auth/hooks/useSessionOperations.js.map +1 -1
- package/dist/auth/hooks/useSignupCaptcha.js +1 -2
- package/dist/auth/hooks/useSignupCaptcha.js.map +1 -1
- package/dist/auth/hooks/useSsoOperations.js +80 -81
- package/dist/auth/hooks/useSsoOperations.js.map +1 -1
- package/dist/auth/hooks/useTokenRefresh.js +8 -12
- package/dist/auth/hooks/useTokenRefresh.js.map +1 -1
- package/dist/auth/hooks/useUserOperations.js +1 -2
- package/dist/auth/hooks/useUserOperations.js.map +1 -1
- package/dist/auth/hooks/useWorkspaceOperations.js +1 -2
- package/dist/auth/hooks/useWorkspaceOperations.js.map +1 -1
- package/dist/auth/hooks/useWorkspaceToken.js +1 -2
- package/dist/auth/hooks/useWorkspaceToken.js.map +1 -1
- package/dist/auth/index.js +19 -18
- package/dist/auth/pages/AuditLogPage.js +21 -22
- package/dist/auth/pages/AuditLogPage.js.map +1 -1
- package/dist/auth/pages/EmailVerifyPage.js +15 -17
- package/dist/auth/pages/EmailVerifyPage.js.map +1 -1
- package/dist/auth/pages/LoginPage.js +89 -105
- package/dist/auth/pages/LoginPage.js.map +1 -1
- package/dist/auth/pages/MagicLinkPage.js +30 -31
- package/dist/auth/pages/MagicLinkPage.js.map +1 -1
- package/dist/auth/pages/MfaSetupPage.js +18 -20
- package/dist/auth/pages/MfaSetupPage.js.map +1 -1
- package/dist/auth/pages/MfaVerifyPage.js +31 -35
- package/dist/auth/pages/MfaVerifyPage.js.map +1 -1
- package/dist/auth/pages/OAuthCallbackPage.js +4 -11
- package/dist/auth/pages/OAuthCallbackPage.js.map +1 -1
- package/dist/auth/pages/PasswordResetPage.js +20 -23
- package/dist/auth/pages/PasswordResetPage.js.map +1 -1
- package/dist/auth/pages/ReactivatePage.js +14 -18
- package/dist/auth/pages/ReactivatePage.js.map +1 -1
- package/dist/auth/pages/RegisterPage.js +180 -181
- package/dist/auth/pages/RegisterPage.js.map +1 -1
- package/dist/auth/pages/SSOClearPage.js +9 -10
- package/dist/auth/pages/SSOClearPage.js.map +1 -1
- package/dist/auth/pages/SSODepositPage.js +9 -13
- package/dist/auth/pages/SSODepositPage.js.map +1 -1
- package/dist/auth/pages/SSORelayCallbackPage.js +3 -7
- package/dist/auth/pages/SSORelayCallbackPage.js.map +1 -1
- package/dist/auth/pages/SSORelayPage.js +11 -10
- package/dist/auth/pages/SSORelayPage.js.map +1 -1
- package/dist/auth/pages/SecurityPage.js +19 -21
- package/dist/auth/pages/SecurityPage.js.map +1 -1
- package/dist/auth/pages/SessionBridgePage.js +9 -16
- package/dist/auth/pages/SessionBridgePage.js.map +1 -1
- package/dist/auth/pages/SessionsPage.js +17 -18
- package/dist/auth/pages/SessionsPage.js.map +1 -1
- package/dist/auth/pages/SsoCallbackPage.js +17 -22
- package/dist/auth/pages/SsoCallbackPage.js.map +1 -1
- package/dist/auth/pages/SsoConfigPage.js +69 -69
- package/dist/auth/pages/SsoConfigPage.js.map +1 -1
- package/dist/auth/pages/SsoLoginPage.js +9 -10
- package/dist/auth/pages/SsoLoginPage.js.map +1 -1
- package/dist/auth/pages/UserDetailsPage.js +16 -23
- package/dist/auth/pages/UserDetailsPage.js.map +1 -1
- package/dist/auth/pages/UserProfilePage.js +140 -141
- package/dist/auth/pages/UserProfilePage.js.map +1 -1
- package/dist/auth/pages/UsersPage.js +446 -453
- package/dist/auth/pages/UsersPage.js.map +1 -1
- package/dist/auth/providers/AuthProvider.js +1 -2
- package/dist/auth/providers/AuthProvider.js.map +1 -1
- package/dist/auth/providers/CaptchaProvider.js +1 -2
- package/dist/auth/providers/CaptchaProvider.js.map +1 -1
- package/dist/auth/store/authStore.js +5 -12
- package/dist/auth/store/authStore.js.map +1 -1
- package/dist/auth/utils/captchaConfig.js +5 -8
- package/dist/auth/utils/captchaConfig.js.map +1 -1
- package/dist/auth/utils/formatters.js +1 -2
- package/dist/auth/utils/formatters.js.map +1 -1
- package/dist/auth/utils/gatewayBaseUrls.js +1 -2
- package/dist/auth/utils/gatewayBaseUrls.js.map +1 -1
- package/dist/auth/utils/legalUrls.js +2 -3
- package/dist/auth/utils/legalUrls.js.map +1 -1
- package/dist/auth/utils/redirect.js +1 -2
- package/dist/auth/utils/redirect.js.map +1 -1
- package/dist/auth/utils/ssoOrigins.js +2 -3
- package/dist/auth/utils/ssoOrigins.js.map +1 -1
- package/dist/auth/utils/validation.js +34 -36
- package/dist/auth/utils/validation.js.map +1 -1
- package/dist/generated/global-operations.js +2 -3
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/wspace-operations.js +2 -3
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/microfe-auth.css +1 -1
- package/dist/shared/hooks/useTr.js +1 -2
- package/dist/shared/hooks/useTr.js.map +1 -1
- package/dist/translations/en.js +1 -2
- package/dist/translations/en.js.map +1 -1
- package/dist/utils/nativeBridge.js +1 -2
- package/package.json +7 -3
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useAuthStore as e } from "../store/authStore.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { OIDC_CODE_VERIFIER as t, OIDC_FLOW as n, OIDC_NONCE as r, OIDC_REDIRECT as i, OIDC_STATE as a, OIDC_TENANT_ID as o } from "../constants/sessionKeys.js";
|
|
3
|
+
import { AuthenticateOidcDocument as s, AuthenticateSamlDocument as c, DeleteSsoConfigDocument as l, GenerateOidcAuthUrlDocument as u, GenerateSamlRequestDocument as d, GetOidcConfigDocument as f, GetSsoConfigDocument as p, SaveSsoConfigDocument as m } from "../../generated/global-operations.js";
|
|
4
4
|
import { useMutation as h, useQuery as g } from "@apollo/client/react";
|
|
5
|
-
|
|
5
|
+
// src/auth/hooks/useSsoOperations.ts
|
|
6
6
|
function _(e) {
|
|
7
7
|
return typeof e == "object" && !!e;
|
|
8
8
|
}
|
|
@@ -16,10 +16,10 @@ function y(e, t) {
|
|
|
16
16
|
return n?.message ? n.message : e instanceof Error || _(e) && typeof e.message == "string" ? e.message : t;
|
|
17
17
|
}
|
|
18
18
|
function b(_) {
|
|
19
|
-
let [v, { loading: b }] = h(
|
|
19
|
+
let [v, { loading: b }] = h(d), [w, { loading: T }] = h(c), [E, { loading: D }] = h(m), [O, { loading: k }] = h(l), [A, { loading: j }] = h(u), [M, { loading: N }] = h(s), { data: P, loading: F, refetch: I } = g(p, {
|
|
20
20
|
variables: { tenantId: _ || "" },
|
|
21
21
|
skip: !_
|
|
22
|
-
}), { data: L, loading: R } = g(
|
|
22
|
+
}), { data: L, loading: R } = g(f, { skip: !1 });
|
|
23
23
|
return {
|
|
24
24
|
initiateSsoLogin: async (e, t) => {
|
|
25
25
|
try {
|
|
@@ -80,112 +80,112 @@ function b(_) {
|
|
|
80
80
|
throw console.error("Failed to delete SSO config:", e), e;
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
|
-
getOidcAuthUrl: async (e,
|
|
83
|
+
getOidcAuthUrl: async (e, n, r = [
|
|
84
84
|
"openid",
|
|
85
85
|
"profile",
|
|
86
86
|
"email"
|
|
87
87
|
]) => {
|
|
88
|
-
let
|
|
89
|
-
if (!
|
|
90
|
-
let
|
|
91
|
-
sessionStorage.setItem(
|
|
92
|
-
let
|
|
88
|
+
let i = L?.getOIDCConfig;
|
|
89
|
+
if (!i) throw Error("OIDC configuration not available");
|
|
90
|
+
let o = x(), s = await S(o), c = C();
|
|
91
|
+
sessionStorage.setItem(t, o), sessionStorage.setItem(a, c);
|
|
92
|
+
let l = new URLSearchParams({
|
|
93
93
|
response_type: "code",
|
|
94
94
|
client_id: e,
|
|
95
|
-
redirect_uri:
|
|
96
|
-
scope:
|
|
97
|
-
state:
|
|
98
|
-
code_challenge:
|
|
95
|
+
redirect_uri: n,
|
|
96
|
+
scope: r.join(" "),
|
|
97
|
+
state: c,
|
|
98
|
+
code_challenge: s,
|
|
99
99
|
code_challenge_method: "S256"
|
|
100
100
|
});
|
|
101
|
-
return `${
|
|
101
|
+
return `${i.authorizationEndpoint}?${l.toString()}`;
|
|
102
102
|
},
|
|
103
|
-
exchangeOidcCode: async (
|
|
104
|
-
let
|
|
105
|
-
if (!
|
|
106
|
-
let
|
|
107
|
-
if (!
|
|
103
|
+
exchangeOidcCode: async (n, r, i) => {
|
|
104
|
+
let o = L?.getOIDCConfig;
|
|
105
|
+
if (!o) throw Error("OIDC configuration not available");
|
|
106
|
+
let s = e.getState(), c = sessionStorage.getItem(t);
|
|
107
|
+
if (!c) throw Error("Code verifier not found. Please restart the login flow.");
|
|
108
108
|
try {
|
|
109
|
-
|
|
110
|
-
let e = await fetch(
|
|
109
|
+
s.setLoading(!0), s.setError(null);
|
|
110
|
+
let e = await fetch(o.tokenEndpoint, {
|
|
111
111
|
method: "POST",
|
|
112
112
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
113
113
|
body: new URLSearchParams({
|
|
114
114
|
grant_type: "authorization_code",
|
|
115
|
-
code:
|
|
116
|
-
redirect_uri:
|
|
117
|
-
client_id:
|
|
118
|
-
code_verifier:
|
|
115
|
+
code: n,
|
|
116
|
+
redirect_uri: i,
|
|
117
|
+
client_id: r,
|
|
118
|
+
code_verifier: c
|
|
119
119
|
})
|
|
120
|
-
}),
|
|
121
|
-
if (!e.ok) throw Error(
|
|
122
|
-
sessionStorage.removeItem(
|
|
123
|
-
let
|
|
120
|
+
}), l = await e.json();
|
|
121
|
+
if (!e.ok) throw Error(l.error_description || l.error || "Token exchange failed");
|
|
122
|
+
sessionStorage.removeItem(t), sessionStorage.removeItem(a);
|
|
123
|
+
let u = await (await fetch(o.userinfoEndpoint, { headers: { Authorization: `Bearer ${l.access_token}` } })).json(), d = (/* @__PURE__ */ new Date()).toISOString(), f = {
|
|
124
124
|
success: !0,
|
|
125
|
-
token:
|
|
126
|
-
refreshToken:
|
|
127
|
-
expiresIn:
|
|
125
|
+
token: l.access_token,
|
|
126
|
+
refreshToken: l.refresh_token,
|
|
127
|
+
expiresIn: l.expires_in,
|
|
128
128
|
user: {
|
|
129
|
-
id:
|
|
130
|
-
email:
|
|
131
|
-
username:
|
|
132
|
-
emailVerified:
|
|
133
|
-
firstName:
|
|
134
|
-
lastName:
|
|
135
|
-
name:
|
|
129
|
+
id: u.sub,
|
|
130
|
+
email: u.email,
|
|
131
|
+
username: u.preferred_username || u.email,
|
|
132
|
+
emailVerified: u.email_verified ?? !0,
|
|
133
|
+
firstName: u.given_name,
|
|
134
|
+
lastName: u.family_name,
|
|
135
|
+
name: u.name,
|
|
136
136
|
role: "MEMBER",
|
|
137
137
|
status: "ACTIVE",
|
|
138
138
|
mfaEnabled: !1,
|
|
139
|
-
createdAt:
|
|
140
|
-
updatedAt:
|
|
139
|
+
createdAt: d,
|
|
140
|
+
updatedAt: d
|
|
141
141
|
}
|
|
142
142
|
};
|
|
143
|
-
return
|
|
143
|
+
return f.user && f.token && (s.setUser(f.user), s.setAuthTokens(f.token, f.refreshToken || ""), s.setAuthenticated(!0)), f;
|
|
144
144
|
} catch (e) {
|
|
145
145
|
let t = e instanceof Error ? e.message : "OIDC authentication failed";
|
|
146
|
-
throw
|
|
146
|
+
throw s.setError(t), e;
|
|
147
147
|
} finally {
|
|
148
|
-
|
|
148
|
+
s.setLoading(!1);
|
|
149
149
|
}
|
|
150
150
|
},
|
|
151
|
-
validateOidcState: (e) => sessionStorage.getItem(
|
|
152
|
-
initiateExternalOidcLogin: async (e,
|
|
151
|
+
validateOidcState: (e) => sessionStorage.getItem(a) === e,
|
|
152
|
+
initiateExternalOidcLogin: async (e, i) => {
|
|
153
153
|
try {
|
|
154
|
-
let { data:
|
|
154
|
+
let { data: s } = await A({ variables: {
|
|
155
155
|
tenantId: e,
|
|
156
|
-
state:
|
|
156
|
+
state: i
|
|
157
157
|
} });
|
|
158
|
-
if (!
|
|
159
|
-
let
|
|
160
|
-
sessionStorage.setItem(
|
|
158
|
+
if (!s || !s.generateOIDCAuthUrl) throw Error("SSO is not configured for this tenant. Please contact your administrator or use email/password login.");
|
|
159
|
+
let c = s.generateOIDCAuthUrl;
|
|
160
|
+
sessionStorage.setItem(t, c.codeVerifier), sessionStorage.setItem(a, c.state), sessionStorage.setItem(r, c.nonce), sessionStorage.setItem(o, e), sessionStorage.setItem(n, "external"), sessionStorage.setItem("oidc_redirect", c.relayState || i || "/dashboard"), window.location.href = c.authUrl;
|
|
161
161
|
} catch (e) {
|
|
162
162
|
let t = y(e, "Failed to initiate OIDC SSO login");
|
|
163
163
|
throw console.error("Failed to initiate external OIDC login:", t), Error(t);
|
|
164
164
|
}
|
|
165
165
|
},
|
|
166
|
-
handleExternalOidcCallback: async (
|
|
167
|
-
let
|
|
166
|
+
handleExternalOidcCallback: async (s, c) => {
|
|
167
|
+
let l = e.getState();
|
|
168
168
|
try {
|
|
169
|
-
|
|
170
|
-
let e = sessionStorage.getItem(
|
|
171
|
-
if (
|
|
172
|
-
let
|
|
173
|
-
if (!
|
|
174
|
-
let { data:
|
|
175
|
-
tenantId:
|
|
176
|
-
code:
|
|
177
|
-
state:
|
|
178
|
-
codeVerifier:
|
|
179
|
-
nonce:
|
|
169
|
+
l.setLoading(!0), l.setError(null);
|
|
170
|
+
let e = sessionStorage.getItem(a);
|
|
171
|
+
if (c !== e) throw Error(`Invalid state parameter. Possible CSRF attack. Expected: ${e?.substring(0, 10)}..., Got: ${c.substring(0, 10)}...`);
|
|
172
|
+
let u = sessionStorage.getItem(t), d = sessionStorage.getItem(r), f = sessionStorage.getItem("oidc_tenant_id") || "default";
|
|
173
|
+
if (!u) throw Error("Code verifier not found. Please restart the login flow.");
|
|
174
|
+
let { data: p } = await M({ variables: { input: {
|
|
175
|
+
tenantId: f,
|
|
176
|
+
code: s,
|
|
177
|
+
state: c,
|
|
178
|
+
codeVerifier: u,
|
|
179
|
+
nonce: d
|
|
180
180
|
} } });
|
|
181
|
-
if (!
|
|
182
|
-
let
|
|
183
|
-
return sessionStorage.removeItem(
|
|
181
|
+
if (!p) throw Error("No data returned from authenticateOIDC mutation");
|
|
182
|
+
let m = p.authenticateOIDC, h = sessionStorage.getItem("oidc_redirect") || "/dashboard";
|
|
183
|
+
return sessionStorage.removeItem(t), sessionStorage.removeItem(a), sessionStorage.removeItem(r), sessionStorage.removeItem(o), sessionStorage.removeItem(n), sessionStorage.removeItem(i), m.user && m.accessToken ? (l.setUser(m.user), l.setAuthTokens(m.accessToken, m.refreshToken || ""), l.setAuthenticated(!0), {
|
|
184
184
|
success: !0,
|
|
185
|
-
user:
|
|
186
|
-
token:
|
|
187
|
-
refreshToken:
|
|
188
|
-
expiresIn:
|
|
185
|
+
user: m.user,
|
|
186
|
+
token: m.accessToken,
|
|
187
|
+
refreshToken: m.refreshToken,
|
|
188
|
+
expiresIn: m.expiresIn,
|
|
189
189
|
redirectPath: h
|
|
190
190
|
}) : {
|
|
191
191
|
success: !1,
|
|
@@ -194,15 +194,15 @@ function b(_) {
|
|
|
194
194
|
};
|
|
195
195
|
} catch (e) {
|
|
196
196
|
console.error("[OIDC Callback] Error:", e);
|
|
197
|
-
let
|
|
198
|
-
throw
|
|
197
|
+
let s = e instanceof Error ? e.message : "OIDC authentication failed";
|
|
198
|
+
throw l.setError(s), sessionStorage.removeItem(t), sessionStorage.removeItem(a), sessionStorage.removeItem(r), sessionStorage.removeItem(o), sessionStorage.removeItem(n), sessionStorage.removeItem(i), e;
|
|
199
199
|
} finally {
|
|
200
|
-
|
|
200
|
+
l.setLoading(!1);
|
|
201
201
|
}
|
|
202
202
|
},
|
|
203
203
|
isExternalOidcCallback: () => {
|
|
204
|
-
let e = sessionStorage.getItem(
|
|
205
|
-
return e ||
|
|
204
|
+
let e = sessionStorage.getItem(n) === "external", r = !!sessionStorage.getItem(o), i = !!sessionStorage.getItem(t);
|
|
205
|
+
return e || r && i;
|
|
206
206
|
},
|
|
207
207
|
ssoConfig: P?.getSSOConfig ?? void 0,
|
|
208
208
|
oidcConfig: L?.getOIDCConfig ?? void 0,
|
|
@@ -216,7 +216,7 @@ function b(_) {
|
|
|
216
216
|
};
|
|
217
217
|
}
|
|
218
218
|
function x() {
|
|
219
|
-
let e = new Uint8Array(32);
|
|
219
|
+
let e = /* @__PURE__ */ new Uint8Array(32);
|
|
220
220
|
return crypto.getRandomValues(e), w(e);
|
|
221
221
|
}
|
|
222
222
|
async function S(e) {
|
|
@@ -224,13 +224,12 @@ async function S(e) {
|
|
|
224
224
|
return w(new Uint8Array(n));
|
|
225
225
|
}
|
|
226
226
|
function C() {
|
|
227
|
-
let e = new Uint8Array(16);
|
|
227
|
+
let e = /* @__PURE__ */ new Uint8Array(16);
|
|
228
228
|
return crypto.getRandomValues(e), w(e);
|
|
229
229
|
}
|
|
230
230
|
function w(e) {
|
|
231
231
|
return btoa(String.fromCharCode(...e)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
232
232
|
}
|
|
233
|
-
//#endregion
|
|
234
233
|
export { b as useSsoOperations };
|
|
235
234
|
|
|
236
235
|
//# sourceMappingURL=useSsoOperations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSsoOperations.js","names":[],"sources":["../../../src/auth/hooks/useSsoOperations.ts"],"sourcesContent":["import { useMutation, useQuery } from \"@apollo/client/react\";\nimport { useAuthStore } from \"../store/authStore\";\nimport type { AuthResult, SSOConfig, User } from \"../types\";\nimport {\n OIDC_CODE_VERIFIER,\n OIDC_STATE,\n OIDC_NONCE,\n OIDC_TENANT_ID,\n OIDC_FLOW,\n OIDC_REDIRECT,\n} from \"../constants/sessionKeys\";\n\nimport {\n AuthenticateSamlDocument,\n GenerateSamlRequestDocument,\n AuthenticateOidcDocument,\n GenerateOidcAuthUrlDocument,\n GetSsoConfigDocument,\n GetOidcConfigDocument,\n SaveSsoConfigDocument,\n DeleteSsoConfigDocument,\n} from \"../../generated/global-operations\";\n// =============================================================================\n// GraphQL Response Types (Apollo Client 4.x)\n// =============================================================================\n\ninterface GetSSOConfigResponse {\n getSSOConfig: SSOConfig | null;\n}\n\ninterface GetSSOConfigVariables {\n tenantId: string;\n}\n\ninterface SaveSSOConfigResponse {\n saveSSOConfig: SSOConfig;\n}\n\ninterface SaveSSOConfigVariables {\n tenantId: string;\n input: SSOConfigInput;\n}\n\ninterface DeleteSSOConfigResponse {\n deleteSSOConfig: boolean;\n}\n\ninterface DeleteSSOConfigVariables {\n tenantId: string;\n}\n\ninterface GenerateSAMLRequestResponse {\n generateSAMLRequest: SAMLRequestResult;\n}\n\ninterface GenerateSAMLRequestVariables {\n tenantId: string;\n relayState?: string;\n}\n\ninterface AuthenticateSAMLResponse {\n authenticateSAML: AuthResult;\n}\n\ninterface AuthenticateSAMLVariables {\n input: {\n samlResponse: string;\n relayState?: string;\n tenantId?: string;\n };\n}\n\ninterface GetOIDCConfigResponse {\n getOIDCConfig: OIDCConfig | null;\n}\n\ninterface GenerateOIDCAuthUrlResponse {\n generateOIDCAuthUrl: ExternalOIDCAuthUrlResult;\n}\n\ninterface GenerateOIDCAuthUrlVariables {\n tenantId: string;\n state?: string;\n}\n\ninterface AuthenticateOIDCPayload {\n accessToken: string;\n refreshToken?: string;\n expiresIn: number;\n user: User;\n}\n\ninterface AuthenticateOIDCResponse {\n authenticateOIDC: AuthenticateOIDCPayload;\n}\n\ninterface AuthenticateOIDCVariables {\n input: {\n tenantId: string;\n code: string;\n state: string;\n codeVerifier: string;\n nonce?: string | null;\n };\n}\n\n// =============================================================================\n// GraphQL Operations\n// =============================================================================\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SSOConfigInput {\n entityId: string;\n ssoUrl: string;\n sloUrl?: string;\n certificate: string;\n enabled?: boolean;\n metadata?: Record<string, unknown>;\n}\n\nexport interface SAMLRequestResult {\n requestId: string;\n ssoUrl: string;\n samlRequest: string;\n}\n\nexport interface ExternalOIDCAuthUrlResult {\n authUrl: string;\n state: string;\n nonce: string;\n codeVerifier: string;\n relayState?: string;\n}\n\nexport interface OIDCConfig {\n issuer: string;\n authorizationEndpoint: string;\n tokenEndpoint: string;\n userinfoEndpoint: string;\n jwksUri: string;\n registrationEndpoint?: string;\n scopesSupported: string[];\n responseTypesSupported: string[];\n grantTypesSupported: string[];\n}\n\ninterface GraphQLErrorLike {\n message?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction getGraphQLErrors(error: unknown): GraphQLErrorLike[] {\n if (!isRecord(error)) return [];\n const graphQLErrors = error.graphQLErrors;\n if (!Array.isArray(graphQLErrors)) return [];\n return graphQLErrors.filter((item): item is GraphQLErrorLike =>\n isRecord(item),\n );\n}\n\nfunction getErrorMessage(error: unknown, fallback: string): string {\n const graphQLError = getGraphQLErrors(error)[0];\n if (graphQLError?.message) return graphQLError.message;\n if (error instanceof Error) return error.message;\n if (isRecord(error) && typeof error.message === \"string\") {\n return error.message;\n }\n return fallback;\n}\n\n// =============================================================================\n// Hook\n// =============================================================================\n\n/**\n * Hook for SSO/SAML operations\n */\nexport function useSsoOperations(tenantId?: string) {\n const [generateSamlRequestMutation, { loading: generatingRequest }] =\n useMutation<GenerateSAMLRequestResponse, GenerateSAMLRequestVariables>(\n GenerateSamlRequestDocument,\n );\n const [authenticateSamlMutation, { loading: authenticating }] = useMutation<\n AuthenticateSAMLResponse,\n AuthenticateSAMLVariables\n >(AuthenticateSamlDocument);\n const [saveSsoConfigMutation, { loading: saving }] = useMutation<\n SaveSSOConfigResponse,\n SaveSSOConfigVariables\n >(SaveSsoConfigDocument);\n const [deleteSsoConfigMutation, { loading: deleting }] = useMutation<\n DeleteSSOConfigResponse,\n DeleteSSOConfigVariables\n >(DeleteSsoConfigDocument);\n const [generateOidcAuthUrlMutation, { loading: generatingOidcUrl }] =\n useMutation<GenerateOIDCAuthUrlResponse, GenerateOIDCAuthUrlVariables>(\n GenerateOidcAuthUrlDocument,\n );\n const [authenticateOidcMutation, { loading: authenticatingOidc }] =\n useMutation<AuthenticateOIDCResponse, AuthenticateOIDCVariables>(\n AuthenticateOidcDocument,\n );\n\n // Get SSO config for tenant\n const {\n data: ssoConfigData,\n loading: loadingConfig,\n refetch: refetchConfig,\n } = useQuery<GetSSOConfigResponse, GetSSOConfigVariables>(\n GetSsoConfigDocument,\n {\n variables: { tenantId: tenantId || \"\" },\n skip: !tenantId,\n },\n );\n\n // Get OIDC discovery info (for our OIDC provider)\n const { data: oidcConfigData, loading: loadingOidc } =\n useQuery<GetOIDCConfigResponse>(GetOidcConfigDocument, {\n skip: false, // Always fetch OIDC config\n });\n\n /**\n * Initiate SP-initiated SAML login\n * Redirects user to IdP with SAML AuthnRequest\n */\n const initiateSsoLogin = async (\n targetTenantId: string,\n relayState?: string,\n ): Promise<void> => {\n try {\n const { data } = await generateSamlRequestMutation({\n variables: { tenantId: targetTenantId, relayState },\n });\n\n // Check if data exists\n if (!data || !data.generateSAMLRequest) {\n throw new Error(\n \"SAML SSO is not configured for this tenant. Please contact your administrator or use email/password login.\",\n );\n }\n\n const result = data.generateSAMLRequest;\n\n // Build the IdP redirect URL with SAML request\n const params = new URLSearchParams({\n SAMLRequest: result.samlRequest,\n });\n if (relayState) {\n params.append(\"RelayState\", relayState);\n }\n\n // Redirect to IdP SSO URL\n window.location.href = `${result.ssoUrl}?${params.toString()}`;\n } catch (error: unknown) {\n // Extract error message from Apollo error or regular error\n const message = getErrorMessage(\n error,\n \"Failed to initiate SAML SSO login\",\n );\n\n console.error(\"Failed to initiate SSO login:\", message);\n throw new Error(message);\n }\n };\n\n /**\n * Handle SAML callback from IdP\n * Called when IdP redirects back with SAML Response\n */\n const handleSamlCallback = async (\n samlResponse: string,\n relayState?: string,\n ): Promise<AuthResult> => {\n const store = useAuthStore.getState();\n const targetTenantId =\n tenantId || sessionStorage.getItem(OIDC_TENANT_ID) || undefined;\n\n try {\n store.setLoading(true);\n store.setError(null);\n\n const { data, error } = await authenticateSamlMutation({\n variables: {\n input: {\n samlResponse,\n relayState,\n tenantId: targetTenantId,\n },\n },\n });\n\n if (!data?.authenticateSAML) {\n const errMsg =\n error?.message ?? \"SAML authentication failed. Please try again.\";\n throw new Error(errMsg);\n }\n\n const result = data.authenticateSAML;\n\n if (result.success && result.user && result.token) {\n store.setUser(result.user);\n store.setAuthTokens(result.token, result.refreshToken || \"\");\n store.setAuthenticated(true);\n } else if (result.error) {\n store.setError(result.error);\n }\n\n return result;\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"SAML authentication failed\";\n store.setError(message);\n throw error;\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Initiate external OIDC login (Azure AD, Google, etc.)\n * Redirects user to external IdP for authentication\n */\n const initiateExternalOidcLogin = async (\n targetTenantId: string,\n redirectPath?: string,\n ): Promise<void> => {\n try {\n const { data } = await generateOidcAuthUrlMutation({\n variables: { tenantId: targetTenantId, state: redirectPath },\n });\n\n // Check if data exists\n if (!data || !data.generateOIDCAuthUrl) {\n throw new Error(\n \"SSO is not configured for this tenant. Please contact your administrator or use email/password login.\",\n );\n }\n\n const result = data.generateOIDCAuthUrl;\n\n // Store PKCE values for token exchange\n sessionStorage.setItem(OIDC_CODE_VERIFIER, result.codeVerifier);\n sessionStorage.setItem(OIDC_STATE, result.state); // Secure random state for CSRF protection\n sessionStorage.setItem(OIDC_NONCE, result.nonce);\n sessionStorage.setItem(OIDC_TENANT_ID, targetTenantId);\n sessionStorage.setItem(OIDC_FLOW, \"external\"); // Mark as external OIDC flow\n // Store relay state (redirect path) separately from security state\n sessionStorage.setItem(\n \"oidc_redirect\",\n result.relayState || redirectPath || \"/dashboard\",\n );\n\n // Redirect to external IdP\n window.location.href = result.authUrl;\n } catch (error: unknown) {\n // Extract error message from Apollo error or regular error\n const message = getErrorMessage(\n error,\n \"Failed to initiate OIDC SSO login\",\n );\n\n console.error(\"Failed to initiate external OIDC login:\", message);\n throw new Error(message);\n }\n };\n\n /**\n * Handle external OIDC callback\n * Called when external IdP redirects back with authorization code\n */\n const handleExternalOidcCallback = async (\n code: string,\n state: string,\n ): Promise<AuthResult> => {\n const store = useAuthStore.getState();\n\n try {\n store.setLoading(true);\n store.setError(null);\n\n // Verify state\n const storedState = sessionStorage.getItem(OIDC_STATE);\n\n if (state !== storedState) {\n throw new Error(\n `Invalid state parameter. Possible CSRF attack. Expected: ${storedState?.substring(0, 10)}..., Got: ${state.substring(0, 10)}...`,\n );\n }\n\n const codeVerifier = sessionStorage.getItem(OIDC_CODE_VERIFIER);\n const nonce = sessionStorage.getItem(OIDC_NONCE);\n const targetTenantId =\n sessionStorage.getItem(OIDC_TENANT_ID) || \"default\";\n\n if (!codeVerifier) {\n throw new Error(\n \"Code verifier not found. Please restart the login flow.\",\n );\n }\n\n const result = await authenticateOidcMutation({\n variables: {\n input: {\n tenantId: targetTenantId,\n code,\n state,\n codeVerifier,\n nonce,\n },\n },\n });\n\n const { data } = result;\n if (!data) {\n throw new Error(\"No data returned from authenticateOIDC mutation\");\n }\n\n const authPayload = data.authenticateOIDC;\n\n // Get redirect path before cleanup\n const redirectPath =\n sessionStorage.getItem(OIDC_REDIRECT) || \"/dashboard\";\n\n // Clean up session storage\n sessionStorage.removeItem(OIDC_CODE_VERIFIER);\n sessionStorage.removeItem(OIDC_STATE);\n sessionStorage.removeItem(OIDC_NONCE);\n sessionStorage.removeItem(OIDC_TENANT_ID);\n sessionStorage.removeItem(OIDC_FLOW);\n sessionStorage.removeItem(OIDC_REDIRECT);\n\n if (authPayload.user && authPayload.accessToken) {\n store.setUser(authPayload.user);\n store.setAuthTokens(\n authPayload.accessToken,\n authPayload.refreshToken || \"\",\n );\n store.setAuthenticated(true);\n\n return {\n success: true,\n user: authPayload.user,\n token: authPayload.accessToken,\n refreshToken: authPayload.refreshToken,\n expiresIn: authPayload.expiresIn,\n redirectPath,\n };\n }\n\n return { success: false, error: \"Authentication failed\", redirectPath };\n } catch (error) {\n console.error(\"[OIDC Callback] Error:\", error);\n\n const message =\n error instanceof Error ? error.message : \"OIDC authentication failed\";\n store.setError(message);\n\n // Clean up on error\n sessionStorage.removeItem(OIDC_CODE_VERIFIER);\n sessionStorage.removeItem(OIDC_STATE);\n sessionStorage.removeItem(OIDC_NONCE);\n sessionStorage.removeItem(OIDC_TENANT_ID);\n sessionStorage.removeItem(OIDC_FLOW);\n sessionStorage.removeItem(OIDC_REDIRECT);\n\n throw error;\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Check if the current callback is from external OIDC flow\n * Checks for multiple indicators to be robust\n */\n const isExternalOidcCallback = (): boolean => {\n const hasFlowFlag = sessionStorage.getItem(OIDC_FLOW) === \"external\";\n const hasTenantId = !!sessionStorage.getItem(OIDC_TENANT_ID);\n const hasCodeVerifier = !!sessionStorage.getItem(OIDC_CODE_VERIFIER);\n\n // Return true if we have the flow flag OR if we have both tenant ID and code verifier\n return hasFlowFlag || (hasTenantId && hasCodeVerifier);\n };\n\n /**\n * Save SSO configuration for tenant\n */\n const saveSsoConfig = async (\n configOrTenantId: SSOConfigInput | string,\n config?: SSOConfigInput,\n ): Promise<SSOConfig> => {\n // Support both old (tenantId, config) and new (config) signatures\n const targetTenantId =\n typeof configOrTenantId === \"string\" ? configOrTenantId : tenantId || \"\";\n const finalConfig =\n typeof configOrTenantId === \"string\" ? config! : configOrTenantId;\n\n try {\n const { data, error } = await saveSsoConfigMutation({\n variables: { tenantId: targetTenantId, input: finalConfig },\n });\n await refetchConfig();\n if (!data?.saveSSOConfig) {\n throw new Error(error?.message ?? \"Failed to save SSO config\");\n }\n return data.saveSSOConfig;\n } catch (error) {\n console.error(\"Failed to save SSO config:\", error);\n throw error;\n }\n };\n\n /**\n * Delete SSO configuration for tenant\n */\n const deleteSsoConfig = async (targetTenantId?: string): Promise<boolean> => {\n const finalTenantId = targetTenantId || tenantId || \"\";\n\n try {\n const { data, error } = await deleteSsoConfigMutation({\n variables: { tenantId: finalTenantId },\n });\n await refetchConfig();\n if (data?.deleteSSOConfig == null) {\n throw new Error(error?.message ?? \"Failed to delete SSO config\");\n }\n return data.deleteSSOConfig;\n } catch (error) {\n console.error(\"Failed to delete SSO config:\", error);\n throw error;\n }\n };\n\n /**\n * Get OIDC authorization URL for OAuth login (async for PKCE SHA-256)\n */\n const getOidcAuthUrl = async (\n clientId: string,\n redirectUri: string,\n scopes: string[] = [\"openid\", \"profile\", \"email\"],\n ): Promise<string> => {\n const config = oidcConfigData?.getOIDCConfig;\n if (!config) {\n throw new Error(\"OIDC configuration not available\");\n }\n\n // Generate PKCE code verifier and challenge (async for SHA-256)\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n // Store verifier in sessionStorage for token exchange\n sessionStorage.setItem(OIDC_CODE_VERIFIER, codeVerifier);\n sessionStorage.setItem(OIDC_STATE, state);\n\n const params = new URLSearchParams({\n response_type: \"code\",\n client_id: clientId,\n redirect_uri: redirectUri,\n scope: scopes.join(\" \"),\n state,\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n });\n\n return `${config.authorizationEndpoint}?${params.toString()}`;\n };\n\n /**\n * Exchange OIDC authorization code for tokens\n */\n const exchangeOidcCode = async (\n code: string,\n clientId: string,\n redirectUri: string,\n ): Promise<AuthResult> => {\n const config = oidcConfigData?.getOIDCConfig;\n if (!config) {\n throw new Error(\"OIDC configuration not available\");\n }\n\n const store = useAuthStore.getState();\n const codeVerifier = sessionStorage.getItem(OIDC_CODE_VERIFIER);\n\n if (!codeVerifier) {\n throw new Error(\n \"Code verifier not found. Please restart the login flow.\",\n );\n }\n\n try {\n store.setLoading(true);\n store.setError(null);\n\n // Exchange code for tokens at the OIDC token endpoint\n const response = await fetch(config.tokenEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: new URLSearchParams({\n grant_type: \"authorization_code\",\n code,\n redirect_uri: redirectUri,\n client_id: clientId,\n code_verifier: codeVerifier,\n }),\n });\n\n const tokenResponse = await response.json();\n\n if (!response.ok) {\n throw new Error(\n tokenResponse.error_description ||\n tokenResponse.error ||\n \"Token exchange failed\",\n );\n }\n\n // Clear PKCE data from session storage\n sessionStorage.removeItem(OIDC_CODE_VERIFIER);\n sessionStorage.removeItem(OIDC_STATE);\n\n // Get user info from userinfo endpoint\n const userinfoResponse = await fetch(config.userinfoEndpoint, {\n headers: {\n Authorization: `Bearer ${tokenResponse.access_token}`,\n },\n });\n\n const userinfo = await userinfoResponse.json();\n\n // Build AuthResult from OIDC response\n const now = new Date().toISOString();\n const result: AuthResult = {\n success: true,\n token: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token,\n expiresIn: tokenResponse.expires_in,\n user: {\n id: userinfo.sub,\n email: userinfo.email,\n username: userinfo.preferred_username || userinfo.email,\n emailVerified: userinfo.email_verified ?? true,\n firstName: userinfo.given_name,\n lastName: userinfo.family_name,\n name: userinfo.name,\n role: \"MEMBER\",\n status: \"ACTIVE\",\n mfaEnabled: false,\n createdAt: now,\n updatedAt: now,\n },\n };\n\n if (result.user && result.token) {\n store.setUser(result.user);\n store.setAuthTokens(result.token, result.refreshToken || \"\");\n store.setAuthenticated(true);\n }\n\n return result;\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"OIDC authentication failed\";\n store.setError(message);\n throw error;\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Validate OIDC state parameter\n */\n const validateOidcState = (state: string): boolean => {\n const storedState = sessionStorage.getItem(OIDC_STATE);\n return storedState === state;\n };\n\n return {\n // SSO/SAML operations\n initiateSsoLogin,\n handleSamlCallback,\n saveSsoConfig,\n deleteSsoConfig,\n\n // OIDC operations (our OIDC provider)\n getOidcAuthUrl,\n exchangeOidcCode,\n validateOidcState,\n\n // External OIDC operations (Azure AD, Google, etc.)\n initiateExternalOidcLogin,\n handleExternalOidcCallback,\n isExternalOidcCallback,\n\n // Data\n ssoConfig: ssoConfigData?.getSSOConfig ?? undefined,\n oidcConfig: oidcConfigData?.getOIDCConfig ?? undefined,\n\n // Loading states\n isLoading:\n loadingConfig ||\n loadingOidc ||\n generatingRequest ||\n authenticating ||\n saving ||\n deleting ||\n generatingOidcUrl ||\n authenticatingOidc,\n isGeneratingRequest: generatingRequest,\n isAuthenticating: authenticating || authenticatingOidc,\n isSaving: saving,\n isDeleting: deleting,\n isGeneratingOidcUrl: generatingOidcUrl,\n\n // Refetch\n refetchConfig,\n };\n}\n\n// =============================================================================\n// PKCE Helpers\n// =============================================================================\n\n/**\n * Generate random code verifier for PKCE\n */\nfunction generateCodeVerifier(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\n/**\n * Generate code challenge from verifier using SHA-256 (S256 method)\n * Uses Web Crypto API for secure hashing\n */\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n\n // Use SubtleCrypto for SHA-256 hash\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", data);\n const hashArray = new Uint8Array(hashBuffer);\n\n // Base64 URL encode the hash\n return base64UrlEncode(hashArray);\n}\n\n/**\n * Generate random state for CSRF protection\n */\nfunction generateState(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\n/**\n * Base64 URL encode\n */\nfunction base64UrlEncode(array: Uint8Array): string {\n const base64 = btoa(String.fromCharCode(...array));\n return base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useSsoOperations.js","names":["isRecord","value","getGraphQLErrors","error","graphQLErrors","item","getErrorMessage","graphQLError","fallback","useSsoOperations","generateSamlRequestMutation","generatingRequest","useMutation","GenerateSamlRequestDocument","authenticateSamlMutation","authenticating","AuthenticateSamlDocument","saveSsoConfigMutation","saving","SaveSsoConfigDocument","deleteSsoConfigMutation","deleting","DeleteSsoConfigDocument","generateOidcAuthUrlMutation","generatingOidcUrl","GenerateOidcAuthUrlDocument","authenticateOidcMutation","authenticatingOidc","AuthenticateOidcDocument","ssoConfigData","loadingConfig","refetchConfig","useQuery","GetSsoConfigDocument","tenantId","oidcConfigData","loadingOidc","GetOidcConfigDocument","targetTenantId","data","result","params","relayState","message","store","useAuthStore","errMsg","configOrTenantId","finalConfig","config","finalTenantId","codeVerifier","generateCodeVerifier","codeChallenge","generateCodeChallenge","state","generateState","OIDC_CODE_VERIFIER","OIDC_STATE","clientId","redirectUri","scopes","response","tokenResponse","userinfo","now","redirectPath","OIDC_NONCE","OIDC_TENANT_ID","OIDC_FLOW","storedState","nonce","authPayload","OIDC_REDIRECT","hasFlowFlag","hasTenantId","hasCodeVerifier","array","base64UrlEncode","verifier","hashBuffer"],"sources":["../../../src/auth/hooks/useSsoOperations.ts"],"sourcesContent":["import { useMutation, useQuery } from \"@apollo/client/react\";\nimport { useAuthStore } from \"../store/authStore\";\nimport type { AuthResult, SSOConfig, User } from \"../types\";\nimport {\n OIDC_CODE_VERIFIER,\n OIDC_STATE,\n OIDC_NONCE,\n OIDC_TENANT_ID,\n OIDC_FLOW,\n OIDC_REDIRECT,\n} from \"../constants/sessionKeys\";\n\nimport {\n AuthenticateSamlDocument,\n GenerateSamlRequestDocument,\n AuthenticateOidcDocument,\n GenerateOidcAuthUrlDocument,\n GetSsoConfigDocument,\n GetOidcConfigDocument,\n SaveSsoConfigDocument,\n DeleteSsoConfigDocument,\n} from \"../../generated/global-operations\";\n// =============================================================================\n// GraphQL Response Types (Apollo Client 4.x)\n// =============================================================================\n\ninterface GetSSOConfigResponse {\n getSSOConfig: SSOConfig | null;\n}\n\ninterface GetSSOConfigVariables {\n tenantId: string;\n}\n\ninterface SaveSSOConfigResponse {\n saveSSOConfig: SSOConfig;\n}\n\ninterface SaveSSOConfigVariables {\n tenantId: string;\n input: SSOConfigInput;\n}\n\ninterface DeleteSSOConfigResponse {\n deleteSSOConfig: boolean;\n}\n\ninterface DeleteSSOConfigVariables {\n tenantId: string;\n}\n\ninterface GenerateSAMLRequestResponse {\n generateSAMLRequest: SAMLRequestResult;\n}\n\ninterface GenerateSAMLRequestVariables {\n tenantId: string;\n relayState?: string;\n}\n\ninterface AuthenticateSAMLResponse {\n authenticateSAML: AuthResult;\n}\n\ninterface AuthenticateSAMLVariables {\n input: {\n samlResponse: string;\n relayState?: string;\n tenantId?: string;\n };\n}\n\ninterface GetOIDCConfigResponse {\n getOIDCConfig: OIDCConfig | null;\n}\n\ninterface GenerateOIDCAuthUrlResponse {\n generateOIDCAuthUrl: ExternalOIDCAuthUrlResult;\n}\n\ninterface GenerateOIDCAuthUrlVariables {\n tenantId: string;\n state?: string;\n}\n\ninterface AuthenticateOIDCPayload {\n accessToken: string;\n refreshToken?: string;\n expiresIn: number;\n user: User;\n}\n\ninterface AuthenticateOIDCResponse {\n authenticateOIDC: AuthenticateOIDCPayload;\n}\n\ninterface AuthenticateOIDCVariables {\n input: {\n tenantId: string;\n code: string;\n state: string;\n codeVerifier: string;\n nonce?: string | null;\n };\n}\n\n// =============================================================================\n// GraphQL Operations\n// =============================================================================\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SSOConfigInput {\n entityId: string;\n ssoUrl: string;\n sloUrl?: string;\n certificate: string;\n enabled?: boolean;\n metadata?: Record<string, unknown>;\n}\n\nexport interface SAMLRequestResult {\n requestId: string;\n ssoUrl: string;\n samlRequest: string;\n}\n\nexport interface ExternalOIDCAuthUrlResult {\n authUrl: string;\n state: string;\n nonce: string;\n codeVerifier: string;\n relayState?: string;\n}\n\nexport interface OIDCConfig {\n issuer: string;\n authorizationEndpoint: string;\n tokenEndpoint: string;\n userinfoEndpoint: string;\n jwksUri: string;\n registrationEndpoint?: string;\n scopesSupported: string[];\n responseTypesSupported: string[];\n grantTypesSupported: string[];\n}\n\ninterface GraphQLErrorLike {\n message?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction getGraphQLErrors(error: unknown): GraphQLErrorLike[] {\n if (!isRecord(error)) return [];\n const graphQLErrors = error.graphQLErrors;\n if (!Array.isArray(graphQLErrors)) return [];\n return graphQLErrors.filter((item): item is GraphQLErrorLike =>\n isRecord(item),\n );\n}\n\nfunction getErrorMessage(error: unknown, fallback: string): string {\n const graphQLError = getGraphQLErrors(error)[0];\n if (graphQLError?.message) return graphQLError.message;\n if (error instanceof Error) return error.message;\n if (isRecord(error) && typeof error.message === \"string\") {\n return error.message;\n }\n return fallback;\n}\n\n// =============================================================================\n// Hook\n// =============================================================================\n\n/**\n * Hook for SSO/SAML operations\n */\nexport function useSsoOperations(tenantId?: string) {\n const [generateSamlRequestMutation, { loading: generatingRequest }] =\n useMutation<GenerateSAMLRequestResponse, GenerateSAMLRequestVariables>(\n GenerateSamlRequestDocument,\n );\n const [authenticateSamlMutation, { loading: authenticating }] = useMutation<\n AuthenticateSAMLResponse,\n AuthenticateSAMLVariables\n >(AuthenticateSamlDocument);\n const [saveSsoConfigMutation, { loading: saving }] = useMutation<\n SaveSSOConfigResponse,\n SaveSSOConfigVariables\n >(SaveSsoConfigDocument);\n const [deleteSsoConfigMutation, { loading: deleting }] = useMutation<\n DeleteSSOConfigResponse,\n DeleteSSOConfigVariables\n >(DeleteSsoConfigDocument);\n const [generateOidcAuthUrlMutation, { loading: generatingOidcUrl }] =\n useMutation<GenerateOIDCAuthUrlResponse, GenerateOIDCAuthUrlVariables>(\n GenerateOidcAuthUrlDocument,\n );\n const [authenticateOidcMutation, { loading: authenticatingOidc }] =\n useMutation<AuthenticateOIDCResponse, AuthenticateOIDCVariables>(\n AuthenticateOidcDocument,\n );\n\n // Get SSO config for tenant\n const {\n data: ssoConfigData,\n loading: loadingConfig,\n refetch: refetchConfig,\n } = useQuery<GetSSOConfigResponse, GetSSOConfigVariables>(\n GetSsoConfigDocument,\n {\n variables: { tenantId: tenantId || \"\" },\n skip: !tenantId,\n },\n );\n\n // Get OIDC discovery info (for our OIDC provider)\n const { data: oidcConfigData, loading: loadingOidc } =\n useQuery<GetOIDCConfigResponse>(GetOidcConfigDocument, {\n skip: false, // Always fetch OIDC config\n });\n\n /**\n * Initiate SP-initiated SAML login\n * Redirects user to IdP with SAML AuthnRequest\n */\n const initiateSsoLogin = async (\n targetTenantId: string,\n relayState?: string,\n ): Promise<void> => {\n try {\n const { data } = await generateSamlRequestMutation({\n variables: { tenantId: targetTenantId, relayState },\n });\n\n // Check if data exists\n if (!data || !data.generateSAMLRequest) {\n throw new Error(\n \"SAML SSO is not configured for this tenant. Please contact your administrator or use email/password login.\",\n );\n }\n\n const result = data.generateSAMLRequest;\n\n // Build the IdP redirect URL with SAML request\n const params = new URLSearchParams({\n SAMLRequest: result.samlRequest,\n });\n if (relayState) {\n params.append(\"RelayState\", relayState);\n }\n\n // Redirect to IdP SSO URL\n window.location.href = `${result.ssoUrl}?${params.toString()}`;\n } catch (error: unknown) {\n // Extract error message from Apollo error or regular error\n const message = getErrorMessage(\n error,\n \"Failed to initiate SAML SSO login\",\n );\n\n console.error(\"Failed to initiate SSO login:\", message);\n throw new Error(message);\n }\n };\n\n /**\n * Handle SAML callback from IdP\n * Called when IdP redirects back with SAML Response\n */\n const handleSamlCallback = async (\n samlResponse: string,\n relayState?: string,\n ): Promise<AuthResult> => {\n const store = useAuthStore.getState();\n const targetTenantId =\n tenantId || sessionStorage.getItem(OIDC_TENANT_ID) || undefined;\n\n try {\n store.setLoading(true);\n store.setError(null);\n\n const { data, error } = await authenticateSamlMutation({\n variables: {\n input: {\n samlResponse,\n relayState,\n tenantId: targetTenantId,\n },\n },\n });\n\n if (!data?.authenticateSAML) {\n const errMsg =\n error?.message ?? \"SAML authentication failed. Please try again.\";\n throw new Error(errMsg);\n }\n\n const result = data.authenticateSAML;\n\n if (result.success && result.user && result.token) {\n store.setUser(result.user);\n store.setAuthTokens(result.token, result.refreshToken || \"\");\n store.setAuthenticated(true);\n } else if (result.error) {\n store.setError(result.error);\n }\n\n return result;\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"SAML authentication failed\";\n store.setError(message);\n throw error;\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Initiate external OIDC login (Azure AD, Google, etc.)\n * Redirects user to external IdP for authentication\n */\n const initiateExternalOidcLogin = async (\n targetTenantId: string,\n redirectPath?: string,\n ): Promise<void> => {\n try {\n const { data } = await generateOidcAuthUrlMutation({\n variables: { tenantId: targetTenantId, state: redirectPath },\n });\n\n // Check if data exists\n if (!data || !data.generateOIDCAuthUrl) {\n throw new Error(\n \"SSO is not configured for this tenant. Please contact your administrator or use email/password login.\",\n );\n }\n\n const result = data.generateOIDCAuthUrl;\n\n // Store PKCE values for token exchange\n sessionStorage.setItem(OIDC_CODE_VERIFIER, result.codeVerifier);\n sessionStorage.setItem(OIDC_STATE, result.state); // Secure random state for CSRF protection\n sessionStorage.setItem(OIDC_NONCE, result.nonce);\n sessionStorage.setItem(OIDC_TENANT_ID, targetTenantId);\n sessionStorage.setItem(OIDC_FLOW, \"external\"); // Mark as external OIDC flow\n // Store relay state (redirect path) separately from security state\n sessionStorage.setItem(\n \"oidc_redirect\",\n result.relayState || redirectPath || \"/dashboard\",\n );\n\n // Redirect to external IdP\n window.location.href = result.authUrl;\n } catch (error: unknown) {\n // Extract error message from Apollo error or regular error\n const message = getErrorMessage(\n error,\n \"Failed to initiate OIDC SSO login\",\n );\n\n console.error(\"Failed to initiate external OIDC login:\", message);\n throw new Error(message);\n }\n };\n\n /**\n * Handle external OIDC callback\n * Called when external IdP redirects back with authorization code\n */\n const handleExternalOidcCallback = async (\n code: string,\n state: string,\n ): Promise<AuthResult> => {\n const store = useAuthStore.getState();\n\n try {\n store.setLoading(true);\n store.setError(null);\n\n // Verify state\n const storedState = sessionStorage.getItem(OIDC_STATE);\n\n if (state !== storedState) {\n throw new Error(\n `Invalid state parameter. Possible CSRF attack. Expected: ${storedState?.substring(0, 10)}..., Got: ${state.substring(0, 10)}...`,\n );\n }\n\n const codeVerifier = sessionStorage.getItem(OIDC_CODE_VERIFIER);\n const nonce = sessionStorage.getItem(OIDC_NONCE);\n const targetTenantId =\n sessionStorage.getItem(OIDC_TENANT_ID) || \"default\";\n\n if (!codeVerifier) {\n throw new Error(\n \"Code verifier not found. Please restart the login flow.\",\n );\n }\n\n const result = await authenticateOidcMutation({\n variables: {\n input: {\n tenantId: targetTenantId,\n code,\n state,\n codeVerifier,\n nonce,\n },\n },\n });\n\n const { data } = result;\n if (!data) {\n throw new Error(\"No data returned from authenticateOIDC mutation\");\n }\n\n const authPayload = data.authenticateOIDC;\n\n // Get redirect path before cleanup\n const redirectPath =\n sessionStorage.getItem(OIDC_REDIRECT) || \"/dashboard\";\n\n // Clean up session storage\n sessionStorage.removeItem(OIDC_CODE_VERIFIER);\n sessionStorage.removeItem(OIDC_STATE);\n sessionStorage.removeItem(OIDC_NONCE);\n sessionStorage.removeItem(OIDC_TENANT_ID);\n sessionStorage.removeItem(OIDC_FLOW);\n sessionStorage.removeItem(OIDC_REDIRECT);\n\n if (authPayload.user && authPayload.accessToken) {\n store.setUser(authPayload.user);\n store.setAuthTokens(\n authPayload.accessToken,\n authPayload.refreshToken || \"\",\n );\n store.setAuthenticated(true);\n\n return {\n success: true,\n user: authPayload.user,\n token: authPayload.accessToken,\n refreshToken: authPayload.refreshToken,\n expiresIn: authPayload.expiresIn,\n redirectPath,\n };\n }\n\n return { success: false, error: \"Authentication failed\", redirectPath };\n } catch (error) {\n console.error(\"[OIDC Callback] Error:\", error);\n\n const message =\n error instanceof Error ? error.message : \"OIDC authentication failed\";\n store.setError(message);\n\n // Clean up on error\n sessionStorage.removeItem(OIDC_CODE_VERIFIER);\n sessionStorage.removeItem(OIDC_STATE);\n sessionStorage.removeItem(OIDC_NONCE);\n sessionStorage.removeItem(OIDC_TENANT_ID);\n sessionStorage.removeItem(OIDC_FLOW);\n sessionStorage.removeItem(OIDC_REDIRECT);\n\n throw error;\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Check if the current callback is from external OIDC flow\n * Checks for multiple indicators to be robust\n */\n const isExternalOidcCallback = (): boolean => {\n const hasFlowFlag = sessionStorage.getItem(OIDC_FLOW) === \"external\";\n const hasTenantId = !!sessionStorage.getItem(OIDC_TENANT_ID);\n const hasCodeVerifier = !!sessionStorage.getItem(OIDC_CODE_VERIFIER);\n\n // Return true if we have the flow flag OR if we have both tenant ID and code verifier\n return hasFlowFlag || (hasTenantId && hasCodeVerifier);\n };\n\n /**\n * Save SSO configuration for tenant\n */\n const saveSsoConfig = async (\n configOrTenantId: SSOConfigInput | string,\n config?: SSOConfigInput,\n ): Promise<SSOConfig> => {\n // Support both old (tenantId, config) and new (config) signatures\n const targetTenantId =\n typeof configOrTenantId === \"string\" ? configOrTenantId : tenantId || \"\";\n const finalConfig =\n typeof configOrTenantId === \"string\" ? config! : configOrTenantId;\n\n try {\n const { data, error } = await saveSsoConfigMutation({\n variables: { tenantId: targetTenantId, input: finalConfig },\n });\n await refetchConfig();\n if (!data?.saveSSOConfig) {\n throw new Error(error?.message ?? \"Failed to save SSO config\");\n }\n return data.saveSSOConfig;\n } catch (error) {\n console.error(\"Failed to save SSO config:\", error);\n throw error;\n }\n };\n\n /**\n * Delete SSO configuration for tenant\n */\n const deleteSsoConfig = async (targetTenantId?: string): Promise<boolean> => {\n const finalTenantId = targetTenantId || tenantId || \"\";\n\n try {\n const { data, error } = await deleteSsoConfigMutation({\n variables: { tenantId: finalTenantId },\n });\n await refetchConfig();\n if (data?.deleteSSOConfig == null) {\n throw new Error(error?.message ?? \"Failed to delete SSO config\");\n }\n return data.deleteSSOConfig;\n } catch (error) {\n console.error(\"Failed to delete SSO config:\", error);\n throw error;\n }\n };\n\n /**\n * Get OIDC authorization URL for OAuth login (async for PKCE SHA-256)\n */\n const getOidcAuthUrl = async (\n clientId: string,\n redirectUri: string,\n scopes: string[] = [\"openid\", \"profile\", \"email\"],\n ): Promise<string> => {\n const config = oidcConfigData?.getOIDCConfig;\n if (!config) {\n throw new Error(\"OIDC configuration not available\");\n }\n\n // Generate PKCE code verifier and challenge (async for SHA-256)\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n // Store verifier in sessionStorage for token exchange\n sessionStorage.setItem(OIDC_CODE_VERIFIER, codeVerifier);\n sessionStorage.setItem(OIDC_STATE, state);\n\n const params = new URLSearchParams({\n response_type: \"code\",\n client_id: clientId,\n redirect_uri: redirectUri,\n scope: scopes.join(\" \"),\n state,\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n });\n\n return `${config.authorizationEndpoint}?${params.toString()}`;\n };\n\n /**\n * Exchange OIDC authorization code for tokens\n */\n const exchangeOidcCode = async (\n code: string,\n clientId: string,\n redirectUri: string,\n ): Promise<AuthResult> => {\n const config = oidcConfigData?.getOIDCConfig;\n if (!config) {\n throw new Error(\"OIDC configuration not available\");\n }\n\n const store = useAuthStore.getState();\n const codeVerifier = sessionStorage.getItem(OIDC_CODE_VERIFIER);\n\n if (!codeVerifier) {\n throw new Error(\n \"Code verifier not found. Please restart the login flow.\",\n );\n }\n\n try {\n store.setLoading(true);\n store.setError(null);\n\n // Exchange code for tokens at the OIDC token endpoint\n const response = await fetch(config.tokenEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: new URLSearchParams({\n grant_type: \"authorization_code\",\n code,\n redirect_uri: redirectUri,\n client_id: clientId,\n code_verifier: codeVerifier,\n }),\n });\n\n const tokenResponse = await response.json();\n\n if (!response.ok) {\n throw new Error(\n tokenResponse.error_description ||\n tokenResponse.error ||\n \"Token exchange failed\",\n );\n }\n\n // Clear PKCE data from session storage\n sessionStorage.removeItem(OIDC_CODE_VERIFIER);\n sessionStorage.removeItem(OIDC_STATE);\n\n // Get user info from userinfo endpoint\n const userinfoResponse = await fetch(config.userinfoEndpoint, {\n headers: {\n Authorization: `Bearer ${tokenResponse.access_token}`,\n },\n });\n\n const userinfo = await userinfoResponse.json();\n\n // Build AuthResult from OIDC response\n const now = new Date().toISOString();\n const result: AuthResult = {\n success: true,\n token: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token,\n expiresIn: tokenResponse.expires_in,\n user: {\n id: userinfo.sub,\n email: userinfo.email,\n username: userinfo.preferred_username || userinfo.email,\n emailVerified: userinfo.email_verified ?? true,\n firstName: userinfo.given_name,\n lastName: userinfo.family_name,\n name: userinfo.name,\n role: \"MEMBER\",\n status: \"ACTIVE\",\n mfaEnabled: false,\n createdAt: now,\n updatedAt: now,\n },\n };\n\n if (result.user && result.token) {\n store.setUser(result.user);\n store.setAuthTokens(result.token, result.refreshToken || \"\");\n store.setAuthenticated(true);\n }\n\n return result;\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"OIDC authentication failed\";\n store.setError(message);\n throw error;\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Validate OIDC state parameter\n */\n const validateOidcState = (state: string): boolean => {\n const storedState = sessionStorage.getItem(OIDC_STATE);\n return storedState === state;\n };\n\n return {\n // SSO/SAML operations\n initiateSsoLogin,\n handleSamlCallback,\n saveSsoConfig,\n deleteSsoConfig,\n\n // OIDC operations (our OIDC provider)\n getOidcAuthUrl,\n exchangeOidcCode,\n validateOidcState,\n\n // External OIDC operations (Azure AD, Google, etc.)\n initiateExternalOidcLogin,\n handleExternalOidcCallback,\n isExternalOidcCallback,\n\n // Data\n ssoConfig: ssoConfigData?.getSSOConfig ?? undefined,\n oidcConfig: oidcConfigData?.getOIDCConfig ?? undefined,\n\n // Loading states\n isLoading:\n loadingConfig ||\n loadingOidc ||\n generatingRequest ||\n authenticating ||\n saving ||\n deleting ||\n generatingOidcUrl ||\n authenticatingOidc,\n isGeneratingRequest: generatingRequest,\n isAuthenticating: authenticating || authenticatingOidc,\n isSaving: saving,\n isDeleting: deleting,\n isGeneratingOidcUrl: generatingOidcUrl,\n\n // Refetch\n refetchConfig,\n };\n}\n\n// =============================================================================\n// PKCE Helpers\n// =============================================================================\n\n/**\n * Generate random code verifier for PKCE\n */\nfunction generateCodeVerifier(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\n/**\n * Generate code challenge from verifier using SHA-256 (S256 method)\n * Uses Web Crypto API for secure hashing\n */\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n\n // Use SubtleCrypto for SHA-256 hash\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", data);\n const hashArray = new Uint8Array(hashBuffer);\n\n // Base64 URL encode the hash\n return base64UrlEncode(hashArray);\n}\n\n/**\n * Generate random state for CSRF protection\n */\nfunction generateState(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\n/**\n * Base64 URL encode\n */\nfunction base64UrlEncode(array: Uint8Array): string {\n const base64 = btoa(String.fromCharCode(...array));\n return base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useAuthStore as e } from "../store/authStore.js";
|
|
2
2
|
import { useWorkspaceToken as t } from "./useWorkspaceToken.js";
|
|
3
3
|
import { useEffect as n, useRef as r } from "react";
|
|
4
|
-
|
|
4
|
+
// src/auth/hooks/useTokenRefresh.ts
|
|
5
5
|
function i(i = {}) {
|
|
6
|
-
let { workspaceAuthServiceUrl: a = "http://localhost:3730/graphql", refreshBeforeExpiry: o =
|
|
6
|
+
let { workspaceAuthServiceUrl: a = "http://localhost:3730/graphql", refreshBeforeExpiry: o = 3e5, enabled: s = !0, onRefreshSuccess: c, onRefreshError: l } = i, { workspaceToken: u } = e(), { refreshToken: d, getTimeUntilExpiry: f, isTokenExpired: p } = t({ workspaceGatewayUrl: a }), m = r(null);
|
|
7
7
|
return n(() => {
|
|
8
8
|
if (m.current &&= (clearTimeout(m.current), null), !s || !u) return;
|
|
9
9
|
if (p(u)) {
|
|
@@ -15,15 +15,12 @@ function i(i = {}) {
|
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
let e = f(u) - o;
|
|
18
|
-
if (e <= 0) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
return m.current = setTimeout(async () => {
|
|
18
|
+
if (e <= 0) d().then((e) => {
|
|
19
|
+
c?.(e);
|
|
20
|
+
}).catch((e) => {
|
|
21
|
+
console.error("[useTokenRefresh] Failed to refresh token (immediate):", e), l?.(e);
|
|
22
|
+
});
|
|
23
|
+
else return m.current = setTimeout(async () => {
|
|
27
24
|
try {
|
|
28
25
|
let e = await d();
|
|
29
26
|
c?.(e);
|
|
@@ -47,7 +44,6 @@ function i(i = {}) {
|
|
|
47
44
|
isScheduled: m.current !== null
|
|
48
45
|
};
|
|
49
46
|
}
|
|
50
|
-
//#endregion
|
|
51
47
|
export { i as useTokenRefresh };
|
|
52
48
|
|
|
53
49
|
//# sourceMappingURL=useTokenRefresh.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTokenRefresh.js","names":[],"sources":["../../../src/auth/hooks/useTokenRefresh.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { useWorkspaceToken } from \"./useWorkspaceToken\";\n\n/**\n * Automatic workspace token refresh hook\n *\n * Monitors the workspace token and automatically refreshes it\n * 5 minutes before expiry. This ensures uninterrupted access to\n * workspace resources.\n *\n * Usage:\n * ```tsx\n * // In your root App component\n * function App() {\n * useTokenRefresh({\n * workspaceAuthServiceUrl: 'http://localhost:3730/graphql',\n * refreshBeforeExpiry: 5 * 60 * 1000, // 5 minutes\n * });\n *\n * return <AppContent />;\n * }\n * ```\n */\nexport interface UseTokenRefreshOptions {\n /**\n * URL of workspace auth service GraphQL endpoint\n * Default: http://localhost:3730/graphql\n */\n workspaceAuthServiceUrl?: string;\n\n /**\n * Time before expiry to refresh token (in milliseconds)\n * Default: 5 minutes (300000ms)\n */\n refreshBeforeExpiry?: number;\n\n /**\n * Enable/disable auto-refresh\n * Default: true\n */\n enabled?: boolean;\n\n /**\n * Callback when token refresh succeeds\n */\n onRefreshSuccess?: (token: string) => void;\n\n /**\n * Callback when token refresh fails\n */\n onRefreshError?: (error: Error) => void;\n}\n\nexport function useTokenRefresh(options: UseTokenRefreshOptions = {}) {\n const {\n workspaceAuthServiceUrl = \"http://localhost:3730/graphql\",\n refreshBeforeExpiry = 5 * 60 * 1000, // 5 minutes\n enabled = true,\n onRefreshSuccess,\n onRefreshError,\n } = options;\n\n const { workspaceToken } = useAuthStore();\n const { refreshToken, getTimeUntilExpiry, isTokenExpired } =\n useWorkspaceToken({\n workspaceGatewayUrl: workspaceAuthServiceUrl,\n });\n\n const refreshTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n // Clear any existing timer\n if (refreshTimerRef.current) {\n clearTimeout(refreshTimerRef.current);\n refreshTimerRef.current = null;\n }\n\n // Exit if disabled or no token\n if (!enabled || !workspaceToken) {\n return;\n }\n\n // Check if token is already expired\n if (isTokenExpired(workspaceToken)) {\n console.warn(\n \"[useTokenRefresh] Token is already expired, triggering immediate refresh\",\n );\n\n refreshToken()\n .then((newToken) => {\n onRefreshSuccess?.(newToken);\n })\n .catch((error) => {\n console.error(\n \"[useTokenRefresh] Failed to refresh expired token:\",\n error,\n );\n onRefreshError?.(error);\n });\n\n return;\n }\n\n // Calculate time until refresh is needed\n const timeUntilExpiry = getTimeUntilExpiry(workspaceToken);\n const timeUntilRefresh = timeUntilExpiry - refreshBeforeExpiry;\n\n if (timeUntilRefresh <= 0) {\n // Token expires in less than refresh window, refresh immediately\n refreshToken()\n .then((newToken) => {\n onRefreshSuccess?.(newToken);\n })\n .catch((error) => {\n console.error(\n \"[useTokenRefresh] Failed to refresh token (immediate):\",\n error,\n );\n onRefreshError?.(error);\n });\n\n return;\n }\n\n // Schedule refresh\n refreshTimerRef.current = setTimeout(async () => {\n try {\n const newToken = await refreshToken();\n onRefreshSuccess?.(newToken);\n } catch (error) {\n console.error(\"[useTokenRefresh] Token refresh failed:\", error);\n onRefreshError?.(error as Error);\n\n // Optionally: Implement retry logic here\n // For now, we'll let the error propagate to the callback\n }\n }, timeUntilRefresh);\n\n // Cleanup on unmount or when dependencies change\n return () => {\n if (refreshTimerRef.current) {\n clearTimeout(refreshTimerRef.current);\n refreshTimerRef.current = null;\n }\n };\n }, [\n workspaceToken,\n enabled,\n refreshBeforeExpiry,\n refreshToken,\n getTimeUntilExpiry,\n isTokenExpired,\n onRefreshSuccess,\n onRefreshError,\n ]);\n\n // Return utility for manual refresh\n return {\n /**\n * Manually trigger a token refresh\n */\n manualRefresh: refreshToken,\n\n /**\n * Check if auto-refresh is currently scheduled\n */\n isScheduled: refreshTimerRef.current !== null,\n };\n}\n"],"mappings":";;;;AAsDA,
|
|
1
|
+
{"version":3,"file":"useTokenRefresh.js","names":["useTokenRefresh","options","useAuthStore","useWorkspaceToken","workspaceAuthServiceUrl","refreshTimerRef","useRef","useEffect","enabled","workspaceToken","isTokenExpired","refreshToken","newToken","error","onRefreshError","timeUntilRefresh","getTimeUntilExpiry","refreshBeforeExpiry","onRefreshSuccess"],"sources":["../../../src/auth/hooks/useTokenRefresh.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { useWorkspaceToken } from \"./useWorkspaceToken\";\n\n/**\n * Automatic workspace token refresh hook\n *\n * Monitors the workspace token and automatically refreshes it\n * 5 minutes before expiry. This ensures uninterrupted access to\n * workspace resources.\n *\n * Usage:\n * ```tsx\n * // In your root App component\n * function App() {\n * useTokenRefresh({\n * workspaceAuthServiceUrl: 'http://localhost:3730/graphql',\n * refreshBeforeExpiry: 5 * 60 * 1000, // 5 minutes\n * });\n *\n * return <AppContent />;\n * }\n * ```\n */\nexport interface UseTokenRefreshOptions {\n /**\n * URL of workspace auth service GraphQL endpoint\n * Default: http://localhost:3730/graphql\n */\n workspaceAuthServiceUrl?: string;\n\n /**\n * Time before expiry to refresh token (in milliseconds)\n * Default: 5 minutes (300000ms)\n */\n refreshBeforeExpiry?: number;\n\n /**\n * Enable/disable auto-refresh\n * Default: true\n */\n enabled?: boolean;\n\n /**\n * Callback when token refresh succeeds\n */\n onRefreshSuccess?: (token: string) => void;\n\n /**\n * Callback when token refresh fails\n */\n onRefreshError?: (error: Error) => void;\n}\n\nexport function useTokenRefresh(options: UseTokenRefreshOptions = {}) {\n const {\n workspaceAuthServiceUrl = \"http://localhost:3730/graphql\",\n refreshBeforeExpiry = 5 * 60 * 1000, // 5 minutes\n enabled = true,\n onRefreshSuccess,\n onRefreshError,\n } = options;\n\n const { workspaceToken } = useAuthStore();\n const { refreshToken, getTimeUntilExpiry, isTokenExpired } =\n useWorkspaceToken({\n workspaceGatewayUrl: workspaceAuthServiceUrl,\n });\n\n const refreshTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n // Clear any existing timer\n if (refreshTimerRef.current) {\n clearTimeout(refreshTimerRef.current);\n refreshTimerRef.current = null;\n }\n\n // Exit if disabled or no token\n if (!enabled || !workspaceToken) {\n return;\n }\n\n // Check if token is already expired\n if (isTokenExpired(workspaceToken)) {\n console.warn(\n \"[useTokenRefresh] Token is already expired, triggering immediate refresh\",\n );\n\n refreshToken()\n .then((newToken) => {\n onRefreshSuccess?.(newToken);\n })\n .catch((error) => {\n console.error(\n \"[useTokenRefresh] Failed to refresh expired token:\",\n error,\n );\n onRefreshError?.(error);\n });\n\n return;\n }\n\n // Calculate time until refresh is needed\n const timeUntilExpiry = getTimeUntilExpiry(workspaceToken);\n const timeUntilRefresh = timeUntilExpiry - refreshBeforeExpiry;\n\n if (timeUntilRefresh <= 0) {\n // Token expires in less than refresh window, refresh immediately\n refreshToken()\n .then((newToken) => {\n onRefreshSuccess?.(newToken);\n })\n .catch((error) => {\n console.error(\n \"[useTokenRefresh] Failed to refresh token (immediate):\",\n error,\n );\n onRefreshError?.(error);\n });\n\n return;\n }\n\n // Schedule refresh\n refreshTimerRef.current = setTimeout(async () => {\n try {\n const newToken = await refreshToken();\n onRefreshSuccess?.(newToken);\n } catch (error) {\n console.error(\"[useTokenRefresh] Token refresh failed:\", error);\n onRefreshError?.(error as Error);\n\n // Optionally: Implement retry logic here\n // For now, we'll let the error propagate to the callback\n }\n }, timeUntilRefresh);\n\n // Cleanup on unmount or when dependencies change\n return () => {\n if (refreshTimerRef.current) {\n clearTimeout(refreshTimerRef.current);\n refreshTimerRef.current = null;\n }\n };\n }, [\n workspaceToken,\n enabled,\n refreshBeforeExpiry,\n refreshToken,\n getTimeUntilExpiry,\n isTokenExpired,\n onRefreshSuccess,\n onRefreshError,\n ]);\n\n // Return utility for manual refresh\n return {\n /**\n * Manually trigger a token refresh\n */\n manualRefresh: refreshToken,\n\n /**\n * Check if auto-refresh is currently scheduled\n */\n isScheduled: refreshTimerRef.current !== null,\n };\n}\n"],"mappings":";;;;AAsDA,SAAgBA,EAAgB,IAAkC,CAAC,GAAG;CACpE,IAAM,EACJ,6BAA0B,iCAC1B,yBAAsB,KACtB,aAAU,IACV,qBACA,sBACEC,GAEE,EAAE,sBAAmBC,EAAa,GAClC,EAAE,iBAAc,uBAAoB,sBACxCC,EAAkB,EAChB,qBAAqBC,EACvB,CAAC,GAEGC,IAAkBC,EAA6C,IAAI;CAyFzE,OAvFAC,QAAgB;EAQd,IANA,AAEEF,EAAgB,aADhB,aAAaA,EAAgB,OAAO,GACV,OAIxB,CAACG,KAAW,CAACC,GACf;EAIF,IAAIC,EAAeD,CAAc,GAAG;GAKlC,AAJA,QAAQ,KACN,0EACF,GAEAE,EAAa,CAAC,CACX,MAAM,MAAa;IAClB,IAAmBC,CAAQ;GAC7B,CAAC,CAAC,CACD,OAAO,MAAU;IAKhB,AAJA,QAAQ,MACN,sDACAC,CACF,GACAC,IAAiBD,CAAK;GACxB,CAAC;GAEH;EACF;EAIA,IAAME,IADkBC,EAAmBP,CAClB,IAAkBQ;EAE3C,IAAIF,KAAoB,GAEtB,EAAa,CAAC,CACX,MAAM,MAAa;GAClB,IAAmBH,CAAQ;EAC7B,CAAC,CAAC,CACD,OAAO,MAAU;GAKhB,AAJA,QAAQ,MACN,0DACAC,CACF,GACAC,IAAiBD,CAAK;EACxB,CAAC;OAoBL,OAdA,EAAgB,UAAU,WAAW,YAAY;GAC/C,IAAI;IACF,IAAMD,IAAW,MAAMD,EAAa;IACpC,IAAmBC,CAAQ;GAC7B,SAASC,GAAO;IAEd,AADA,QAAQ,MAAM,2CAA2CA,CAAK,GAC9DC,IAAiBD,CAAc;GAIjC;EACF,GAAGE,CAAgB,SAGN;GACX,AAEEV,EAAgB,aADhB,aAAaA,EAAgB,OAAO,GACV;EAE9B;CACF,GAAG;EACDI;EACAD;EACAS;EACAN;EACAK;EACAN;EACAQ;EACAJ;CACF,CAAC,GAGM;EAIL,eAAeH;EAKf,aAAaN,EAAgB,YAAY;CAC3C;AACF"}
|
|
@@ -2,7 +2,7 @@ import { useAuthStore as e } from "../store/authStore.js";
|
|
|
2
2
|
import { DeleteUserDocument as t, GetUserDocument as n, HardDeleteUserDocument as r, ListUsersDocument as i, UpdateCurrentUserProfileDocument as a, UpdateUserDocument as o } from "../../generated/global-operations.js";
|
|
3
3
|
import { useCallback as s } from "react";
|
|
4
4
|
import { useLazyQuery as c, useMutation as l } from "@apollo/client/react";
|
|
5
|
-
|
|
5
|
+
// src/auth/hooks/useUserOperations.ts
|
|
6
6
|
function u() {
|
|
7
7
|
let u = e((e) => e.user), d = e((e) => e.setUser), f = e((e) => e.setLoading), p = e((e) => e.isLoading), [m, { loading: h }] = c(n), [g, { loading: _ }] = c(i, { fetchPolicy: "network-only" }), [v] = l(a), [y] = l(o), [b] = l(t), [x] = l(r);
|
|
8
8
|
return {
|
|
@@ -109,7 +109,6 @@ function u() {
|
|
|
109
109
|
isLoading: h || _ || p
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
|
-
//#endregion
|
|
113
112
|
export { u as useUserOperations };
|
|
114
113
|
|
|
115
114
|
//# sourceMappingURL=useUserOperations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useUserOperations.js","names":[],"sources":["../../../src/auth/hooks/useUserOperations.ts"],"sourcesContent":["import { useMutation, useLazyQuery } from \"@apollo/client/react\";\nimport { useCallback } from \"react\";\nimport { useAuthStore } from \"../store/authStore\";\nimport {\n GetUserDocument,\n ListUsersDocument,\n UpdateCurrentUserProfileDocument,\n UpdateUserDocument,\n DeleteUserDocument,\n HardDeleteUserDocument,\n} from \"../../generated/global-operations\";\nimport type {\n User,\n UserConnection,\n UserFilterInput,\n PaginationInput,\n DeleteUserResult,\n} from \"../types\";\n\n// =============================================================================\n// GRAPHQL RESPONSE TYPES\n// =============================================================================\n\ninterface GetUserQueryVariables {\n id: string;\n}\n\ninterface GetUserQueryResponse {\n user: User | null;\n}\n\ninterface ListUsersQueryVariables {\n filter?: UserFilterInput;\n pagination?: PaginationInput;\n}\n\ninterface ListUsersQueryResponse {\n users: UserConnection;\n}\n\ninterface UpdateCurrentUserProfileVariables {\n input: UpdateUserProfileInput;\n}\n\ninterface UpdateCurrentUserProfileResponse {\n updateCurrentUserProfile: User;\n}\n\ninterface UpdateUserVariables {\n id: string;\n input: AdminUpdateUserInput;\n}\n\ninterface UpdateUserResponse {\n updateUser: User;\n}\n\ninterface DeleteUserVariables {\n id: string;\n}\n\ninterface DeleteUserResponse {\n deleteUser: DeleteUserResult;\n}\n\ninterface HardDeleteUserVariables {\n id: string;\n}\n\ninterface HardDeleteUserResponse {\n hardDeleteUser: DeleteUserResult;\n}\n\n// GraphQL Operations\n\n// Input types for update\ninterface UpdateUserProfileInput {\n avatar?: string;\n username?: string;\n firstName?: string;\n lastName?: string;\n name?: string;\n phoneNumber?: string;\n defaultWorkspaceId?: string;\n language?: string;\n timezone?: string;\n}\n\ninterface AdminUpdateUserInput extends UpdateUserProfileInput {\n role?: string;\n status?: string;\n emailVerified?: boolean;\n}\n\n/**\n * Hook for user management operations\n */\nexport function useUserOperations() {\n const user = useAuthStore((state) => state.user);\n const setUser = useAuthStore((state) => state.setUser);\n const setLoading = useAuthStore((state) => state.setLoading);\n const isLoading = useAuthStore((state) => state.isLoading);\n\n const [getUserQuery, { loading: getUserLoading }] = useLazyQuery<\n GetUserQueryResponse,\n GetUserQueryVariables\n >(GetUserDocument);\n const [listUsersQuery, { loading: listUsersLoading }] = useLazyQuery<\n ListUsersQueryResponse,\n ListUsersQueryVariables\n >(ListUsersDocument, {\n fetchPolicy: \"network-only\",\n });\n const [updateCurrentUserProfileMutation] = useMutation<\n UpdateCurrentUserProfileResponse,\n UpdateCurrentUserProfileVariables\n >(UpdateCurrentUserProfileDocument);\n const [updateUserMutation] = useMutation<\n UpdateUserResponse,\n UpdateUserVariables\n >(UpdateUserDocument);\n const [deleteUserMutation] = useMutation<\n DeleteUserResponse,\n DeleteUserVariables\n >(DeleteUserDocument);\n const [hardDeleteUserMutation] = useMutation<\n HardDeleteUserResponse,\n HardDeleteUserVariables\n >(HardDeleteUserDocument);\n\n /**\n * Get user by ID (admin only)\n * Uses global-layer context (no x-workspace-id) since user management is\n * a global/tenant operation, not workspace-scoped.\n */\n const getUser = useCallback(\n async (id: string): Promise<User | null> => {\n try {\n const { data } = await getUserQuery({\n variables: { id },\n context: { headers: { \"x-workspace-id\": \"\" } },\n });\n return data?.user || null;\n } catch (error) {\n console.error(\"Failed to get user:\", error);\n throw error;\n }\n },\n [getUserQuery],\n );\n\n /**\n * List users with filtering and pagination (admin only)\n * Uses global-layer context (no x-workspace-id) since user management is\n * a global/tenant operation, not workspace-scoped.\n */\n const listUsers = useCallback(\n async (\n filter?: UserFilterInput,\n pagination?: PaginationInput,\n ): Promise<UserConnection> => {\n try {\n const { data, error } = await listUsersQuery({\n variables: { filter, pagination },\n context: { headers: { \"x-workspace-id\": \"\" } },\n });\n\n if (error) throw error;\n return data?.users || { users: [], totalCount: 0, hasMore: false };\n } catch (error) {\n // Ignore abort errors - they happen during component unmount/re-render\n if (error instanceof Error && error.name === \"AbortError\") {\n return { users: [], totalCount: 0, hasMore: false };\n }\n console.error(\"Failed to list users:\", error);\n throw error;\n }\n },\n [listUsersQuery],\n );\n\n /**\n * Update current user's profile\n */\n const updateCurrentUserProfile = useCallback(\n async (input: UpdateUserProfileInput): Promise<User> => {\n try {\n setLoading(true);\n\n const { data } = await updateCurrentUserProfileMutation({\n variables: { input },\n });\n\n const updatedUser = data!.updateCurrentUserProfile;\n\n // Update user in store\n if (user) {\n setUser({ ...user, ...updatedUser });\n }\n\n return updatedUser;\n } catch (error) {\n console.error(\"Failed to update profile:\", error);\n throw error;\n } finally {\n setLoading(false);\n }\n },\n [updateCurrentUserProfileMutation, setLoading, setUser, user],\n );\n\n /**\n * Update user by ID (admin only)\n * Uses global-layer context (no x-workspace-id) since user management is\n * a global/tenant operation, not workspace-scoped.\n */\n const updateUser = useCallback(\n async (id: string, input: AdminUpdateUserInput): Promise<User> => {\n try {\n setLoading(true);\n\n const { data } = await updateUserMutation({\n variables: { id, input },\n context: { headers: { \"x-workspace-id\": \"\" } },\n });\n\n return data!.updateUser;\n } catch (error) {\n console.error(\"Failed to update user:\", error);\n throw error;\n } finally {\n setLoading(false);\n }\n },\n [updateUserMutation, setLoading],\n );\n\n /**\n * Delete user (soft delete, admin only)\n * Uses global-layer context (no x-workspace-id) since user management is\n * a global/tenant operation, not workspace-scoped.\n */\n const deleteUser = useCallback(\n async (id: string): Promise<DeleteUserResult> => {\n try {\n setLoading(true);\n\n const { data } = await deleteUserMutation({\n variables: { id },\n context: { headers: { \"x-workspace-id\": \"\" } },\n });\n\n return data!.deleteUser;\n } catch (error) {\n console.error(\"Failed to delete user:\", error);\n throw error;\n } finally {\n setLoading(false);\n }\n },\n [deleteUserMutation, setLoading],\n );\n\n /**\n * Hard delete user (permanent, super admin only)\n * Uses global-layer context (no x-workspace-id) since user management is\n * a global/tenant operation, not workspace-scoped.\n */\n const hardDeleteUser = useCallback(\n async (id: string): Promise<DeleteUserResult> => {\n try {\n setLoading(true);\n\n const { data } = await hardDeleteUserMutation({\n variables: { id },\n context: { headers: { \"x-workspace-id\": \"\" } },\n });\n\n return data!.hardDeleteUser;\n } catch (error) {\n console.error(\"Failed to hard delete user:\", error);\n throw error;\n } finally {\n setLoading(false);\n }\n },\n [hardDeleteUserMutation, setLoading],\n );\n\n return {\n // Queries\n getUser,\n listUsers,\n // Mutations\n updateCurrentUserProfile,\n updateUser,\n deleteUser,\n hardDeleteUser,\n // State\n currentUser: user,\n isLoading: getUserLoading || listUsersLoading || isLoading,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useUserOperations.js","names":["useUserOperations","user","useAuthStore","state","setUser","setLoading","isLoading","getUserQuery","getUserLoading","useLazyQuery","GetUserDocument","listUsersQuery","listUsersLoading","ListUsersDocument","updateCurrentUserProfileMutation","useMutation","UpdateCurrentUserProfileDocument","updateUserMutation","UpdateUserDocument","deleteUserMutation","DeleteUserDocument","hardDeleteUserMutation","HardDeleteUserDocument","useCallback","data","error","updatedUser"],"sources":["../../../src/auth/hooks/useUserOperations.ts"],"sourcesContent":["import { useMutation, useLazyQuery } from \"@apollo/client/react\";\nimport { useCallback } from \"react\";\nimport { useAuthStore } from \"../store/authStore\";\nimport {\n GetUserDocument,\n ListUsersDocument,\n UpdateCurrentUserProfileDocument,\n UpdateUserDocument,\n DeleteUserDocument,\n HardDeleteUserDocument,\n} from \"../../generated/global-operations\";\nimport type {\n User,\n UserConnection,\n UserFilterInput,\n PaginationInput,\n DeleteUserResult,\n} from \"../types\";\n\n// =============================================================================\n// GRAPHQL RESPONSE TYPES\n// =============================================================================\n\ninterface GetUserQueryVariables {\n id: string;\n}\n\ninterface GetUserQueryResponse {\n user: User | null;\n}\n\ninterface ListUsersQueryVariables {\n filter?: UserFilterInput;\n pagination?: PaginationInput;\n}\n\ninterface ListUsersQueryResponse {\n users: UserConnection;\n}\n\ninterface UpdateCurrentUserProfileVariables {\n input: UpdateUserProfileInput;\n}\n\ninterface UpdateCurrentUserProfileResponse {\n updateCurrentUserProfile: User;\n}\n\ninterface UpdateUserVariables {\n id: string;\n input: AdminUpdateUserInput;\n}\n\ninterface UpdateUserResponse {\n updateUser: User;\n}\n\ninterface DeleteUserVariables {\n id: string;\n}\n\ninterface DeleteUserResponse {\n deleteUser: DeleteUserResult;\n}\n\ninterface HardDeleteUserVariables {\n id: string;\n}\n\ninterface HardDeleteUserResponse {\n hardDeleteUser: DeleteUserResult;\n}\n\n// GraphQL Operations\n\n// Input types for update\ninterface UpdateUserProfileInput {\n avatar?: string;\n username?: string;\n firstName?: string;\n lastName?: string;\n name?: string;\n phoneNumber?: string;\n defaultWorkspaceId?: string;\n language?: string;\n timezone?: string;\n}\n\ninterface AdminUpdateUserInput extends UpdateUserProfileInput {\n role?: string;\n status?: string;\n emailVerified?: boolean;\n}\n\n/**\n * Hook for user management operations\n */\nexport function useUserOperations() {\n const user = useAuthStore((state) => state.user);\n const setUser = useAuthStore((state) => state.setUser);\n const setLoading = useAuthStore((state) => state.setLoading);\n const isLoading = useAuthStore((state) => state.isLoading);\n\n const [getUserQuery, { loading: getUserLoading }] = useLazyQuery<\n GetUserQueryResponse,\n GetUserQueryVariables\n >(GetUserDocument);\n const [listUsersQuery, { loading: listUsersLoading }] = useLazyQuery<\n ListUsersQueryResponse,\n ListUsersQueryVariables\n >(ListUsersDocument, {\n fetchPolicy: \"network-only\",\n });\n const [updateCurrentUserProfileMutation] = useMutation<\n UpdateCurrentUserProfileResponse,\n UpdateCurrentUserProfileVariables\n >(UpdateCurrentUserProfileDocument);\n const [updateUserMutation] = useMutation<\n UpdateUserResponse,\n UpdateUserVariables\n >(UpdateUserDocument);\n const [deleteUserMutation] = useMutation<\n DeleteUserResponse,\n DeleteUserVariables\n >(DeleteUserDocument);\n const [hardDeleteUserMutation] = useMutation<\n HardDeleteUserResponse,\n HardDeleteUserVariables\n >(HardDeleteUserDocument);\n\n /**\n * Get user by ID (admin only)\n * Uses global-layer context (no x-workspace-id) since user management is\n * a global/tenant operation, not workspace-scoped.\n */\n const getUser = useCallback(\n async (id: string): Promise<User | null> => {\n try {\n const { data } = await getUserQuery({\n variables: { id },\n context: { headers: { \"x-workspace-id\": \"\" } },\n });\n return data?.user || null;\n } catch (error) {\n console.error(\"Failed to get user:\", error);\n throw error;\n }\n },\n [getUserQuery],\n );\n\n /**\n * List users with filtering and pagination (admin only)\n * Uses global-layer context (no x-workspace-id) since user management is\n * a global/tenant operation, not workspace-scoped.\n */\n const listUsers = useCallback(\n async (\n filter?: UserFilterInput,\n pagination?: PaginationInput,\n ): Promise<UserConnection> => {\n try {\n const { data, error } = await listUsersQuery({\n variables: { filter, pagination },\n context: { headers: { \"x-workspace-id\": \"\" } },\n });\n\n if (error) throw error;\n return data?.users || { users: [], totalCount: 0, hasMore: false };\n } catch (error) {\n // Ignore abort errors - they happen during component unmount/re-render\n if (error instanceof Error && error.name === \"AbortError\") {\n return { users: [], totalCount: 0, hasMore: false };\n }\n console.error(\"Failed to list users:\", error);\n throw error;\n }\n },\n [listUsersQuery],\n );\n\n /**\n * Update current user's profile\n */\n const updateCurrentUserProfile = useCallback(\n async (input: UpdateUserProfileInput): Promise<User> => {\n try {\n setLoading(true);\n\n const { data } = await updateCurrentUserProfileMutation({\n variables: { input },\n });\n\n const updatedUser = data!.updateCurrentUserProfile;\n\n // Update user in store\n if (user) {\n setUser({ ...user, ...updatedUser });\n }\n\n return updatedUser;\n } catch (error) {\n console.error(\"Failed to update profile:\", error);\n throw error;\n } finally {\n setLoading(false);\n }\n },\n [updateCurrentUserProfileMutation, setLoading, setUser, user],\n );\n\n /**\n * Update user by ID (admin only)\n * Uses global-layer context (no x-workspace-id) since user management is\n * a global/tenant operation, not workspace-scoped.\n */\n const updateUser = useCallback(\n async (id: string, input: AdminUpdateUserInput): Promise<User> => {\n try {\n setLoading(true);\n\n const { data } = await updateUserMutation({\n variables: { id, input },\n context: { headers: { \"x-workspace-id\": \"\" } },\n });\n\n return data!.updateUser;\n } catch (error) {\n console.error(\"Failed to update user:\", error);\n throw error;\n } finally {\n setLoading(false);\n }\n },\n [updateUserMutation, setLoading],\n );\n\n /**\n * Delete user (soft delete, admin only)\n * Uses global-layer context (no x-workspace-id) since user management is\n * a global/tenant operation, not workspace-scoped.\n */\n const deleteUser = useCallback(\n async (id: string): Promise<DeleteUserResult> => {\n try {\n setLoading(true);\n\n const { data } = await deleteUserMutation({\n variables: { id },\n context: { headers: { \"x-workspace-id\": \"\" } },\n });\n\n return data!.deleteUser;\n } catch (error) {\n console.error(\"Failed to delete user:\", error);\n throw error;\n } finally {\n setLoading(false);\n }\n },\n [deleteUserMutation, setLoading],\n );\n\n /**\n * Hard delete user (permanent, super admin only)\n * Uses global-layer context (no x-workspace-id) since user management is\n * a global/tenant operation, not workspace-scoped.\n */\n const hardDeleteUser = useCallback(\n async (id: string): Promise<DeleteUserResult> => {\n try {\n setLoading(true);\n\n const { data } = await hardDeleteUserMutation({\n variables: { id },\n context: { headers: { \"x-workspace-id\": \"\" } },\n });\n\n return data!.hardDeleteUser;\n } catch (error) {\n console.error(\"Failed to hard delete user:\", error);\n throw error;\n } finally {\n setLoading(false);\n }\n },\n [hardDeleteUserMutation, setLoading],\n );\n\n return {\n // Queries\n getUser,\n listUsers,\n // Mutations\n updateCurrentUserProfile,\n updateUser,\n deleteUser,\n hardDeleteUser,\n // State\n currentUser: user,\n isLoading: getUserLoading || listUsersLoading || isLoading,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useAuthStore as e } from "../store/authStore.js";
|
|
2
2
|
import { AddWorkspaceMemberDocument as t, CreateWorkspaceDocument as n, RemoveWorkspaceMemberDocument as r, UpdateWorkspaceDocument as i, UpdateWorkspaceMemberDocument as a } from "../../generated/global-operations.js";
|
|
3
3
|
import { useMutation as o } from "@apollo/client/react";
|
|
4
|
-
|
|
4
|
+
// src/auth/hooks/useWorkspaceOperations.ts
|
|
5
5
|
function s() {
|
|
6
6
|
let s = e(), [c] = o(n), [l] = o(i), [u] = o(t), [d] = o(a), [f] = o(r), p = async (e) => {
|
|
7
7
|
throw console.warn("generateWorkspaceToken: Use useMyWorkspaces().issueWorkspaceToken() instead. This operation requires the workspace gateway."), Error("Use useMyWorkspaces().issueWorkspaceToken() for workspace token issuance");
|
|
@@ -29,7 +29,6 @@ function s() {
|
|
|
29
29
|
isLoading: s.isLoading
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
-
//#endregion
|
|
33
32
|
export { s as useWorkspaceOperations };
|
|
34
33
|
|
|
35
34
|
//# sourceMappingURL=useWorkspaceOperations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWorkspaceOperations.js","names":[],"sources":["../../../src/auth/hooks/useWorkspaceOperations.ts"],"sourcesContent":["import { useMutation } from \"@apollo/client/react\";\nimport { useAuthStore } from \"../store/authStore\";\nimport {\n CreateWorkspaceDocument,\n UpdateWorkspaceDocument,\n AddWorkspaceMemberDocument,\n UpdateWorkspaceMemberDocument,\n RemoveWorkspaceMemberDocument,\n} from \"../../generated/global-operations\";\nimport type {\n WorkspaceTokenResponse,\n SecretRotationResponse,\n RotateWorkspaceSecretInput,\n} from \"../types\";\n\n// =============================================================================\n// NOTE: Workspace token/secret operations (issueWorkspaceToken,\n// createWorkspaceSecret, rotateWorkspaceSecret, revokeWorkspaceSecret,\n// revokeWorkspaceToken, revokeAllMyWorkspaceTokens) are in the wspace-auth-svc\n// schema (workspace gateway), NOT the global gateway. These operations are\n// handled by useMyWorkspaces.ts (via graphqlFetch to workspace gateway).\n//\n// The workspace CRUD operations (createWorkspace, updateWorkspace, etc.) are\n// in the global gateway schema and use generated Document constants.\n// =============================================================================\n\n/**\n * Hook for workspace token and secret operations\n */\nexport function useWorkspaceOperations() {\n const store = useAuthStore();\n\n // Workspace CRUD mutations (available in global gateway schema)\n const [createWorkspaceMutation] = useMutation(CreateWorkspaceDocument);\n const [updateWorkspaceMutation] = useMutation(UpdateWorkspaceDocument);\n const [addWorkspaceMemberMutation] = useMutation(AddWorkspaceMemberDocument);\n const [updateWorkspaceMemberMutation] = useMutation(\n UpdateWorkspaceMemberDocument,\n );\n const [removeWorkspaceMemberMutation] = useMutation(\n RemoveWorkspaceMemberDocument,\n );\n\n /**\n * Generate workspace token for a workspace.\n * This hook intentionally delegates workspace-token issuance to\n * `useMyWorkspaces().issueWorkspaceToken()` because that flow requires the\n * workspaces gateway rather than the global Apollo client.\n */\n const generateWorkspaceToken = async (\n _workspaceId: string,\n ): Promise<WorkspaceTokenResponse> => {\n console.warn(\n \"generateWorkspaceToken: Use useMyWorkspaces().issueWorkspaceToken() instead. \" +\n \"This operation requires the workspace gateway.\",\n );\n throw new Error(\n \"Use useMyWorkspaces().issueWorkspaceToken() for workspace token issuance\",\n );\n };\n\n /**\n * Switch to a different workspace\n */\n const switchWorkspace = async (\n workspaceId: string,\n ): Promise<WorkspaceTokenResponse> => {\n // Clear current workspace token first\n store.clearWorkspace();\n\n // Generate new workspace token\n return generateWorkspaceToken(workspaceId);\n };\n\n /**\n * Create workspace secret (admin only).\n * Unsupported from this hook because the operation lives on the workspaces gateway.\n */\n const createWorkspaceSecret = async (\n _workspaceId: string,\n ): Promise<boolean> => {\n console.warn(\n \"createWorkspaceSecret is not available from the global auth client\",\n );\n return false;\n };\n\n /**\n * Rotate workspace secret (admin only).\n * Unsupported from this hook because the operation lives on the workspaces gateway.\n */\n const rotateWorkspaceSecret = async (\n _input: RotateWorkspaceSecretInput,\n ): Promise<SecretRotationResponse> => {\n console.warn(\n \"rotateWorkspaceSecret is not available from the global auth client\",\n );\n throw new Error(\n \"rotateWorkspaceSecret is not available from the global auth client\",\n );\n };\n\n /**\n * Revoke workspace secret (emergency use, admin only).\n * Unsupported from this hook because the operation lives on the workspaces gateway.\n */\n const revokeWorkspaceSecret = async (\n _workspaceId: string,\n ): Promise<boolean> => {\n console.warn(\n \"revokeWorkspaceSecret is not available from the global auth client\",\n );\n return false;\n };\n\n /**\n * Revoke a specific workspace token.\n * Unsupported from this hook because the operation lives on the workspaces gateway.\n */\n const revokeWorkspaceToken = async (\n _token: string,\n _reason?: string,\n ): Promise<boolean> => {\n console.warn(\n \"revokeWorkspaceToken is not available from the global auth client\",\n );\n return false;\n };\n\n /**\n * Revoke all workspace tokens for current user.\n * Unsupported from this hook because the operation lives on the workspaces gateway.\n */\n const revokeAllMyWorkspaceTokens = async (\n _reason?: string,\n ): Promise<number> => {\n console.warn(\n \"revokeAllMyWorkspaceTokens is not available from the global auth client\",\n );\n return 0;\n };\n\n /**\n * Clear current workspace (logout from workspace)\n */\n const clearWorkspace = () => {\n store.clearWorkspace();\n };\n\n return {\n // Mutations\n generateWorkspaceToken,\n switchWorkspace,\n createWorkspaceSecret,\n rotateWorkspaceSecret,\n revokeWorkspaceSecret,\n revokeWorkspaceToken,\n revokeAllMyWorkspaceTokens,\n clearWorkspace,\n // Workspace CRUD mutations\n createWorkspaceMutation,\n updateWorkspaceMutation,\n addWorkspaceMemberMutation,\n updateWorkspaceMemberMutation,\n removeWorkspaceMemberMutation,\n // State\n currentWorkspaceId: store.currentWorkspaceId,\n workspaceToken: store.workspaceToken,\n isLoading: store.isLoading,\n };\n}\n"],"mappings":";;;;AA6BA,
|
|
1
|
+
{"version":3,"file":"useWorkspaceOperations.js","names":["useWorkspaceOperations","store","useAuthStore","createWorkspaceMutation","useMutation","CreateWorkspaceDocument","updateWorkspaceMutation","UpdateWorkspaceDocument","addWorkspaceMemberMutation","AddWorkspaceMemberDocument","updateWorkspaceMemberMutation","UpdateWorkspaceMemberDocument","removeWorkspaceMemberMutation","RemoveWorkspaceMemberDocument","generateWorkspaceToken","workspaceId"],"sources":["../../../src/auth/hooks/useWorkspaceOperations.ts"],"sourcesContent":["import { useMutation } from \"@apollo/client/react\";\nimport { useAuthStore } from \"../store/authStore\";\nimport {\n CreateWorkspaceDocument,\n UpdateWorkspaceDocument,\n AddWorkspaceMemberDocument,\n UpdateWorkspaceMemberDocument,\n RemoveWorkspaceMemberDocument,\n} from \"../../generated/global-operations\";\nimport type {\n WorkspaceTokenResponse,\n SecretRotationResponse,\n RotateWorkspaceSecretInput,\n} from \"../types\";\n\n// =============================================================================\n// NOTE: Workspace token/secret operations (issueWorkspaceToken,\n// createWorkspaceSecret, rotateWorkspaceSecret, revokeWorkspaceSecret,\n// revokeWorkspaceToken, revokeAllMyWorkspaceTokens) are in the wspace-auth-svc\n// schema (workspace gateway), NOT the global gateway. These operations are\n// handled by useMyWorkspaces.ts (via graphqlFetch to workspace gateway).\n//\n// The workspace CRUD operations (createWorkspace, updateWorkspace, etc.) are\n// in the global gateway schema and use generated Document constants.\n// =============================================================================\n\n/**\n * Hook for workspace token and secret operations\n */\nexport function useWorkspaceOperations() {\n const store = useAuthStore();\n\n // Workspace CRUD mutations (available in global gateway schema)\n const [createWorkspaceMutation] = useMutation(CreateWorkspaceDocument);\n const [updateWorkspaceMutation] = useMutation(UpdateWorkspaceDocument);\n const [addWorkspaceMemberMutation] = useMutation(AddWorkspaceMemberDocument);\n const [updateWorkspaceMemberMutation] = useMutation(\n UpdateWorkspaceMemberDocument,\n );\n const [removeWorkspaceMemberMutation] = useMutation(\n RemoveWorkspaceMemberDocument,\n );\n\n /**\n * Generate workspace token for a workspace.\n * This hook intentionally delegates workspace-token issuance to\n * `useMyWorkspaces().issueWorkspaceToken()` because that flow requires the\n * workspaces gateway rather than the global Apollo client.\n */\n const generateWorkspaceToken = async (\n _workspaceId: string,\n ): Promise<WorkspaceTokenResponse> => {\n console.warn(\n \"generateWorkspaceToken: Use useMyWorkspaces().issueWorkspaceToken() instead. \" +\n \"This operation requires the workspace gateway.\",\n );\n throw new Error(\n \"Use useMyWorkspaces().issueWorkspaceToken() for workspace token issuance\",\n );\n };\n\n /**\n * Switch to a different workspace\n */\n const switchWorkspace = async (\n workspaceId: string,\n ): Promise<WorkspaceTokenResponse> => {\n // Clear current workspace token first\n store.clearWorkspace();\n\n // Generate new workspace token\n return generateWorkspaceToken(workspaceId);\n };\n\n /**\n * Create workspace secret (admin only).\n * Unsupported from this hook because the operation lives on the workspaces gateway.\n */\n const createWorkspaceSecret = async (\n _workspaceId: string,\n ): Promise<boolean> => {\n console.warn(\n \"createWorkspaceSecret is not available from the global auth client\",\n );\n return false;\n };\n\n /**\n * Rotate workspace secret (admin only).\n * Unsupported from this hook because the operation lives on the workspaces gateway.\n */\n const rotateWorkspaceSecret = async (\n _input: RotateWorkspaceSecretInput,\n ): Promise<SecretRotationResponse> => {\n console.warn(\n \"rotateWorkspaceSecret is not available from the global auth client\",\n );\n throw new Error(\n \"rotateWorkspaceSecret is not available from the global auth client\",\n );\n };\n\n /**\n * Revoke workspace secret (emergency use, admin only).\n * Unsupported from this hook because the operation lives on the workspaces gateway.\n */\n const revokeWorkspaceSecret = async (\n _workspaceId: string,\n ): Promise<boolean> => {\n console.warn(\n \"revokeWorkspaceSecret is not available from the global auth client\",\n );\n return false;\n };\n\n /**\n * Revoke a specific workspace token.\n * Unsupported from this hook because the operation lives on the workspaces gateway.\n */\n const revokeWorkspaceToken = async (\n _token: string,\n _reason?: string,\n ): Promise<boolean> => {\n console.warn(\n \"revokeWorkspaceToken is not available from the global auth client\",\n );\n return false;\n };\n\n /**\n * Revoke all workspace tokens for current user.\n * Unsupported from this hook because the operation lives on the workspaces gateway.\n */\n const revokeAllMyWorkspaceTokens = async (\n _reason?: string,\n ): Promise<number> => {\n console.warn(\n \"revokeAllMyWorkspaceTokens is not available from the global auth client\",\n );\n return 0;\n };\n\n /**\n * Clear current workspace (logout from workspace)\n */\n const clearWorkspace = () => {\n store.clearWorkspace();\n };\n\n return {\n // Mutations\n generateWorkspaceToken,\n switchWorkspace,\n createWorkspaceSecret,\n rotateWorkspaceSecret,\n revokeWorkspaceSecret,\n revokeWorkspaceToken,\n revokeAllMyWorkspaceTokens,\n clearWorkspace,\n // Workspace CRUD mutations\n createWorkspaceMutation,\n updateWorkspaceMutation,\n addWorkspaceMemberMutation,\n updateWorkspaceMemberMutation,\n removeWorkspaceMemberMutation,\n // State\n currentWorkspaceId: store.currentWorkspaceId,\n workspaceToken: store.workspaceToken,\n isLoading: store.isLoading,\n };\n}\n"],"mappings":";;;;AA6BA,SAAgBA,IAAyB;CACvC,IAAMC,IAAQC,EAAa,GAGrB,CAACC,KAA2BC,EAAYC,CAAuB,GAC/D,CAACC,KAA2BF,EAAYG,CAAuB,GAC/D,CAACC,KAA8BJ,EAAYK,CAA0B,GACrE,CAACC,KAAiCN,EACtCO,CACF,GACM,CAACC,KAAiCR,EACtCS,CACF,GAQMC,IAAyB,OAC7B,MACoC;EAKpC,MAJA,QAAQ,KACN,6HAEF,GACU,MACR,0EACF;CACF;CA0FA,OAAO;EAEL;EACA,wBAvFA,OAGAb,EAAM,eAAe,GAGda,EAAuBC,CAAW;EAkFzC,8BA1EA,OAEA,QAAQ,KACN,oEACF,GACO;EAsEP,8BA9DA,MACoC;GAIpC,MAHA,QAAQ,KACN,oEACF,GACU,MACR,oEACF;EACF;EAuDE,8BAhDA,OAEA,QAAQ,KACN,oEACF,GACO;EA4CP,6BApCA,GACA,OAEA,QAAQ,KACN,mEACF,GACO;EA+BP,mCAvBA,OAEA,QAAQ,KACN,yEACF,GACO;EAmBP,sBAb2B;GAC3B,EAAM,eAAe;EACvB;EAaE;EACA;EACA;EACA;EACA;EAEA,oBAAoBd,EAAM;EAC1B,gBAAgBA,EAAM;EACtB,WAAWA,EAAM;CACnB;AACF"}
|