@geekapps/auth-nextjs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -0
- package/dist/client.d.ts +379 -0
- package/dist/client.js +745 -0
- package/dist/client.js.map +1 -0
- package/dist/server.d.ts +101 -0
- package/dist/server.js +701 -0
- package/dist/server.js.map +1 -0
- package/package.json +46 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,745 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/client.tsx
|
|
4
|
+
import {
|
|
5
|
+
createContext,
|
|
6
|
+
useCallback,
|
|
7
|
+
useContext,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useState
|
|
11
|
+
} from "react";
|
|
12
|
+
|
|
13
|
+
// src/actions.ts
|
|
14
|
+
import { cookies } from "next/headers";
|
|
15
|
+
function env(name) {
|
|
16
|
+
const value = process.env[name];
|
|
17
|
+
return value && value.trim().length > 0 ? value : void 0;
|
|
18
|
+
}
|
|
19
|
+
function normalizeBaseUrl(url) {
|
|
20
|
+
return url.replace(/\/$/, "");
|
|
21
|
+
}
|
|
22
|
+
function resolveIssuer() {
|
|
23
|
+
return normalizeBaseUrl(
|
|
24
|
+
env("GEEKAPPS_AUTH_ISSUER") ?? env("GEEKAPPS_AUTH_API_URL") ?? "http://localhost:3020"
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
function getServerClientHeaders() {
|
|
28
|
+
const clientId = env("GEEKAPPS_AUTH_CLIENT_ID");
|
|
29
|
+
const clientSecret = env("GEEKAPPS_AUTH_CLIENT_SECRET");
|
|
30
|
+
return clientId && clientSecret ? {
|
|
31
|
+
"x-geekapps-client-id": clientId,
|
|
32
|
+
"x-geekapps-client-secret": clientSecret
|
|
33
|
+
} : {};
|
|
34
|
+
}
|
|
35
|
+
function hashClientId(clientId) {
|
|
36
|
+
let h = 5381;
|
|
37
|
+
for (let i = 0; i < clientId.length; i++) {
|
|
38
|
+
h = (h << 5) + h ^ clientId.charCodeAt(i) & 255;
|
|
39
|
+
h = h >>> 0;
|
|
40
|
+
}
|
|
41
|
+
return h.toString(16).padStart(8, "0");
|
|
42
|
+
}
|
|
43
|
+
function deriveCookiePrefix() {
|
|
44
|
+
const clientId = env("GEEKAPPS_AUTH_CLIENT_ID") ?? "";
|
|
45
|
+
if (!clientId) return "gk";
|
|
46
|
+
return `gk_${hashClientId(clientId)}`;
|
|
47
|
+
}
|
|
48
|
+
function decodeMeta(value) {
|
|
49
|
+
try {
|
|
50
|
+
const m = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
|
|
51
|
+
return m.id ? m : null;
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function getCookieNames() {
|
|
57
|
+
const prefix = deriveCookiePrefix();
|
|
58
|
+
return {
|
|
59
|
+
active: `${prefix}_active`,
|
|
60
|
+
count: `${prefix}_count`,
|
|
61
|
+
activeOrganizationId: `${prefix}_ao`,
|
|
62
|
+
slotMeta: (n) => `${prefix}_m${n}`,
|
|
63
|
+
slotAt: (n) => `${prefix}_at${n}`,
|
|
64
|
+
slotRt: (n) => `${prefix}_rt${n}`
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async function getActiveAccessToken() {
|
|
68
|
+
const cookieStore = await cookies();
|
|
69
|
+
const cn = await getCookieNames();
|
|
70
|
+
const raw = cookieStore.get(cn.active)?.value;
|
|
71
|
+
const activeIdx = raw ? Number.parseInt(raw, 10) : 0;
|
|
72
|
+
const countRaw = cookieStore.get(cn.count)?.value;
|
|
73
|
+
const count = countRaw ? Number.parseInt(countRaw, 10) : 0;
|
|
74
|
+
if (count === 0) return null;
|
|
75
|
+
const idx = activeIdx >= 1 && activeIdx <= count ? activeIdx : 1;
|
|
76
|
+
return cookieStore.get(cn.slotAt(idx))?.value ?? null;
|
|
77
|
+
}
|
|
78
|
+
async function getActiveRefreshToken() {
|
|
79
|
+
const cookieStore = await cookies();
|
|
80
|
+
const cn = await getCookieNames();
|
|
81
|
+
const raw = cookieStore.get(cn.active)?.value;
|
|
82
|
+
const activeIdx = raw ? Number.parseInt(raw, 10) : 0;
|
|
83
|
+
const countRaw = cookieStore.get(cn.count)?.value;
|
|
84
|
+
const count = countRaw ? Number.parseInt(countRaw, 10) : 0;
|
|
85
|
+
if (count === 0) return null;
|
|
86
|
+
const idx = activeIdx >= 1 && activeIdx <= count ? activeIdx : 1;
|
|
87
|
+
return cookieStore.get(cn.slotRt(idx))?.value ?? null;
|
|
88
|
+
}
|
|
89
|
+
async function refreshAndSaveTokens() {
|
|
90
|
+
const refreshToken = await getActiveRefreshToken();
|
|
91
|
+
const clientId = env("GEEKAPPS_AUTH_CLIENT_ID") ?? "";
|
|
92
|
+
const clientSecret = env("GEEKAPPS_AUTH_CLIENT_SECRET") ?? "";
|
|
93
|
+
if (!refreshToken || !clientId) return null;
|
|
94
|
+
const basicAuth = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
|
95
|
+
const response = await fetch(`${resolveIssuer()}/auth/refresh`, {
|
|
96
|
+
method: "POST",
|
|
97
|
+
headers: {
|
|
98
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
99
|
+
authorization: `Basic ${basicAuth}`
|
|
100
|
+
},
|
|
101
|
+
body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken }),
|
|
102
|
+
cache: "no-store"
|
|
103
|
+
});
|
|
104
|
+
if (!response.ok) return null;
|
|
105
|
+
const data = await response.json().catch(() => null);
|
|
106
|
+
if (!data) return null;
|
|
107
|
+
const newAccessToken = data.accessToken ?? data.access_token;
|
|
108
|
+
const newRefreshToken = data.refreshToken ?? data.refresh_token;
|
|
109
|
+
if (!newAccessToken) return null;
|
|
110
|
+
const cookieStore = await cookies();
|
|
111
|
+
const cn = await getCookieNames();
|
|
112
|
+
const raw = cookieStore.get(cn.active)?.value;
|
|
113
|
+
const activeIdx = raw ? Number.parseInt(raw, 10) : 1;
|
|
114
|
+
const opts = {
|
|
115
|
+
httpOnly: true,
|
|
116
|
+
secure: process.env.NODE_ENV === "production",
|
|
117
|
+
sameSite: "lax",
|
|
118
|
+
path: "/",
|
|
119
|
+
maxAge: 60 * 60 * 24 * 30
|
|
120
|
+
};
|
|
121
|
+
cookieStore.set(cn.slotAt(activeIdx), newAccessToken, opts);
|
|
122
|
+
if (newRefreshToken) cookieStore.set(cn.slotRt(activeIdx), newRefreshToken, opts);
|
|
123
|
+
cookieStore.set(`${deriveCookiePrefix()}_tp`, newAccessToken, {
|
|
124
|
+
httpOnly: false,
|
|
125
|
+
secure: process.env.NODE_ENV === "production",
|
|
126
|
+
sameSite: "lax",
|
|
127
|
+
path: "/",
|
|
128
|
+
maxAge: 60 * 60 * 24 * 30
|
|
129
|
+
});
|
|
130
|
+
return newAccessToken;
|
|
131
|
+
}
|
|
132
|
+
async function authFetch(path) {
|
|
133
|
+
let accessToken = await getActiveAccessToken();
|
|
134
|
+
if (!accessToken) throw new Error("Not authenticated");
|
|
135
|
+
const issuer = resolveIssuer();
|
|
136
|
+
const doFetch = (token) => fetch(`${issuer}${path}`, {
|
|
137
|
+
headers: {
|
|
138
|
+
authorization: `Bearer ${token}`,
|
|
139
|
+
...getServerClientHeaders()
|
|
140
|
+
},
|
|
141
|
+
cache: "no-store"
|
|
142
|
+
});
|
|
143
|
+
let response = await doFetch(accessToken);
|
|
144
|
+
if (response.status === 401) {
|
|
145
|
+
const newToken = await refreshAndSaveTokens();
|
|
146
|
+
if (!newToken) throw new Error("Session expired");
|
|
147
|
+
response = await doFetch(newToken);
|
|
148
|
+
}
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
const err = await response.json().catch(() => null);
|
|
151
|
+
throw new Error(err?.error_description ?? err?.error ?? err?.message ?? `Auth error ${response.status}`);
|
|
152
|
+
}
|
|
153
|
+
if (response.status === 204) return null;
|
|
154
|
+
return response.json();
|
|
155
|
+
}
|
|
156
|
+
async function getMe() {
|
|
157
|
+
try {
|
|
158
|
+
const data = await authFetch("/auth/me");
|
|
159
|
+
if (!data) return { user: null };
|
|
160
|
+
if (typeof data === "object" && "user" in data) return data;
|
|
161
|
+
return { user: data };
|
|
162
|
+
} catch {
|
|
163
|
+
return { user: null };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function getAccounts() {
|
|
167
|
+
const cookieStore = await cookies();
|
|
168
|
+
const cn = await getCookieNames();
|
|
169
|
+
const countRaw = cookieStore.get(cn.count)?.value;
|
|
170
|
+
const count = countRaw ? Number.parseInt(countRaw, 10) : 0;
|
|
171
|
+
const activeRaw = cookieStore.get(cn.active)?.value;
|
|
172
|
+
const activeIdx = activeRaw ? Number.parseInt(activeRaw, 10) : 0;
|
|
173
|
+
const accounts = [];
|
|
174
|
+
for (let i = 1; i <= count; i++) {
|
|
175
|
+
const at = cookieStore.get(cn.slotAt(i))?.value;
|
|
176
|
+
if (!at) continue;
|
|
177
|
+
const meta = decodeMeta(cookieStore.get(cn.slotMeta(i))?.value ?? "");
|
|
178
|
+
if (!meta) continue;
|
|
179
|
+
accounts.push({
|
|
180
|
+
id: meta.id,
|
|
181
|
+
sessionId: meta.sid ?? meta.id,
|
|
182
|
+
isActive: i === activeIdx,
|
|
183
|
+
user: {
|
|
184
|
+
id: meta.id,
|
|
185
|
+
name: meta.n ?? null,
|
|
186
|
+
email: meta.e ?? null,
|
|
187
|
+
imageUrl: meta.p ?? null,
|
|
188
|
+
phone: null
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return { accounts };
|
|
193
|
+
}
|
|
194
|
+
async function getOrganizations() {
|
|
195
|
+
try {
|
|
196
|
+
const cookieStore = await cookies();
|
|
197
|
+
const cn = await getCookieNames();
|
|
198
|
+
const activeOrgId = cookieStore.get(cn.activeOrganizationId)?.value ?? null;
|
|
199
|
+
const data = await authFetch("/organizations");
|
|
200
|
+
const organizations = Array.isArray(data) ? data : data.organizations;
|
|
201
|
+
const resolvedOrgId = activeOrgId ?? organizations[0]?.id ?? null;
|
|
202
|
+
return { organizations, currentOrgId: resolvedOrgId };
|
|
203
|
+
} catch (err) {
|
|
204
|
+
console.error("[getOrganizations] error:", err);
|
|
205
|
+
return { organizations: [] };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/client.tsx
|
|
210
|
+
import { jsx } from "react/jsx-runtime";
|
|
211
|
+
function normalizeBasePath(basePath) {
|
|
212
|
+
return basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
213
|
+
}
|
|
214
|
+
function buildUrl(basePath, path) {
|
|
215
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
216
|
+
return `${normalizeBasePath(basePath)}${normalizedPath}`;
|
|
217
|
+
}
|
|
218
|
+
async function parseError(response) {
|
|
219
|
+
const data = await response.json().catch(() => null);
|
|
220
|
+
return data?.error_description ?? data?.error ?? data?.message ?? `Request failed with status ${response.status}`;
|
|
221
|
+
}
|
|
222
|
+
function redirectTo(path) {
|
|
223
|
+
window.location.assign(path);
|
|
224
|
+
}
|
|
225
|
+
function isMouseEvent(value) {
|
|
226
|
+
return Boolean(value && typeof value === "object" && "preventDefault" in value);
|
|
227
|
+
}
|
|
228
|
+
function normalizeSignInOptions(input) {
|
|
229
|
+
if (!input) return void 0;
|
|
230
|
+
if (isMouseEvent(input)) {
|
|
231
|
+
input.preventDefault();
|
|
232
|
+
return void 0;
|
|
233
|
+
}
|
|
234
|
+
return input;
|
|
235
|
+
}
|
|
236
|
+
async function clientRequest(path, options = {}, basePath = "/api/auth") {
|
|
237
|
+
const response = await fetch(buildUrl(basePath, path), {
|
|
238
|
+
body: options.body === void 0 ? void 0 : JSON.stringify(options.body),
|
|
239
|
+
credentials: "include",
|
|
240
|
+
headers: options.body === void 0 ? void 0 : {
|
|
241
|
+
"content-type": "application/json"
|
|
242
|
+
},
|
|
243
|
+
method: options.method ?? "GET"
|
|
244
|
+
});
|
|
245
|
+
if (!response.ok) {
|
|
246
|
+
throw new Error(await parseError(response));
|
|
247
|
+
}
|
|
248
|
+
if (response.status === 204) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
return await response.json();
|
|
252
|
+
}
|
|
253
|
+
async function toStandaloneAuthResult(fn) {
|
|
254
|
+
try {
|
|
255
|
+
return {
|
|
256
|
+
success: true,
|
|
257
|
+
data: await fn()
|
|
258
|
+
};
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return {
|
|
261
|
+
success: false,
|
|
262
|
+
error: error instanceof Error ? error.message : "Auth request failed"
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async function loginWithPassword(params, options) {
|
|
267
|
+
return toStandaloneAuthResult(async () => {
|
|
268
|
+
const data = await clientRequest(
|
|
269
|
+
"/login/password",
|
|
270
|
+
{
|
|
271
|
+
body: params,
|
|
272
|
+
method: "POST"
|
|
273
|
+
},
|
|
274
|
+
options?.basePath
|
|
275
|
+
);
|
|
276
|
+
if (params.redirectTo ?? data.redirectTo) {
|
|
277
|
+
redirectTo(params.redirectTo ?? data.redirectTo);
|
|
278
|
+
}
|
|
279
|
+
return data;
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
async function registerWithPassword(params, options) {
|
|
283
|
+
return toStandaloneAuthResult(async () => {
|
|
284
|
+
const data = await clientRequest(
|
|
285
|
+
"/register/password",
|
|
286
|
+
{
|
|
287
|
+
body: params,
|
|
288
|
+
method: "POST"
|
|
289
|
+
},
|
|
290
|
+
options?.basePath
|
|
291
|
+
);
|
|
292
|
+
if (params.redirectTo) {
|
|
293
|
+
redirectTo(params.redirectTo);
|
|
294
|
+
}
|
|
295
|
+
return data;
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
function startGoogleLogin(params, options) {
|
|
299
|
+
const basePath = options?.basePath ?? "/api/auth";
|
|
300
|
+
const url = new URL(buildUrl(basePath, "/login/google"), window.location.origin);
|
|
301
|
+
if (params?.callbackUrl) {
|
|
302
|
+
url.searchParams.set("callbackUrl", params.callbackUrl);
|
|
303
|
+
}
|
|
304
|
+
if (params?.redirectTo) {
|
|
305
|
+
url.searchParams.set("redirectTo", params.redirectTo);
|
|
306
|
+
}
|
|
307
|
+
if (params?.authorizationFlowId) {
|
|
308
|
+
url.searchParams.set("flow_id", params.authorizationFlowId);
|
|
309
|
+
}
|
|
310
|
+
redirectTo(url.toString());
|
|
311
|
+
}
|
|
312
|
+
async function requestPasswordlessLink(params, options) {
|
|
313
|
+
return toStandaloneAuthResult(
|
|
314
|
+
() => clientRequest(
|
|
315
|
+
"/passwordless/link",
|
|
316
|
+
{
|
|
317
|
+
body: params,
|
|
318
|
+
method: "POST"
|
|
319
|
+
},
|
|
320
|
+
options?.basePath
|
|
321
|
+
)
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
var GeekappsAuthContext = createContext(null);
|
|
325
|
+
function GeekappsAuthProvider({
|
|
326
|
+
children,
|
|
327
|
+
basePath = "/api/auth",
|
|
328
|
+
initialUser = null,
|
|
329
|
+
initialOrganizations = [],
|
|
330
|
+
initialCurrentOrgId = null
|
|
331
|
+
}) {
|
|
332
|
+
const [user, setUser] = useState(initialUser);
|
|
333
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
334
|
+
const [accounts, setAccounts] = useState([]);
|
|
335
|
+
const [isLoadingAccounts, setIsLoadingAccounts] = useState(true);
|
|
336
|
+
const [organizations, setOrganizations] = useState(initialOrganizations);
|
|
337
|
+
const [currentOrgId, setCurrentOrgId] = useState(initialCurrentOrgId);
|
|
338
|
+
const [isLoadingOrganizations, setIsLoadingOrganizations] = useState(true);
|
|
339
|
+
const request = useCallback(
|
|
340
|
+
async (path, options = {}) => {
|
|
341
|
+
const response = await fetch(buildUrl(basePath, path), {
|
|
342
|
+
method: options.method ?? "GET",
|
|
343
|
+
credentials: "include",
|
|
344
|
+
headers: options.body === void 0 ? void 0 : {
|
|
345
|
+
"content-type": "application/json"
|
|
346
|
+
},
|
|
347
|
+
body: options.body === void 0 ? void 0 : JSON.stringify(options.body)
|
|
348
|
+
});
|
|
349
|
+
if (!response.ok) {
|
|
350
|
+
throw new Error(await parseError(response));
|
|
351
|
+
}
|
|
352
|
+
if (response.status === 204) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
return await response.json();
|
|
356
|
+
},
|
|
357
|
+
[basePath]
|
|
358
|
+
);
|
|
359
|
+
const refreshUser = useCallback(async () => {
|
|
360
|
+
setIsLoading(true);
|
|
361
|
+
try {
|
|
362
|
+
const { user: u } = await getMe();
|
|
363
|
+
setUser(u);
|
|
364
|
+
} catch {
|
|
365
|
+
setUser(null);
|
|
366
|
+
} finally {
|
|
367
|
+
setIsLoading(false);
|
|
368
|
+
}
|
|
369
|
+
}, []);
|
|
370
|
+
const refreshAccounts = useCallback(async () => {
|
|
371
|
+
setIsLoadingAccounts(true);
|
|
372
|
+
try {
|
|
373
|
+
const { accounts: a } = await getAccounts();
|
|
374
|
+
setAccounts(a);
|
|
375
|
+
} catch {
|
|
376
|
+
setAccounts([]);
|
|
377
|
+
} finally {
|
|
378
|
+
setIsLoadingAccounts(false);
|
|
379
|
+
}
|
|
380
|
+
}, []);
|
|
381
|
+
const refreshOrganizations = useCallback(async () => {
|
|
382
|
+
setIsLoadingOrganizations(true);
|
|
383
|
+
try {
|
|
384
|
+
const { organizations: orgs, currentOrgId: nextCurrentOrgId } = await getOrganizations();
|
|
385
|
+
setOrganizations(orgs);
|
|
386
|
+
setCurrentOrgId((current) => {
|
|
387
|
+
if (nextCurrentOrgId !== void 0) return nextCurrentOrgId ?? null;
|
|
388
|
+
if (current && orgs.some((org) => org.id === current)) return current;
|
|
389
|
+
return orgs[0]?.id ?? null;
|
|
390
|
+
});
|
|
391
|
+
} catch {
|
|
392
|
+
setOrganizations([]);
|
|
393
|
+
setCurrentOrgId(null);
|
|
394
|
+
} finally {
|
|
395
|
+
setIsLoadingOrganizations(false);
|
|
396
|
+
}
|
|
397
|
+
}, []);
|
|
398
|
+
useEffect(() => {
|
|
399
|
+
void refreshUser();
|
|
400
|
+
void refreshAccounts();
|
|
401
|
+
void refreshOrganizations();
|
|
402
|
+
}, [refreshAccounts, refreshOrganizations, refreshUser]);
|
|
403
|
+
const currentOrg = useMemo(
|
|
404
|
+
() => organizations.find((organization) => organization.id === currentOrgId) ?? null,
|
|
405
|
+
[currentOrgId, organizations]
|
|
406
|
+
);
|
|
407
|
+
const setCurrentOrg = useCallback(
|
|
408
|
+
async (organizationId) => {
|
|
409
|
+
await request("/organizations/active", {
|
|
410
|
+
method: "POST",
|
|
411
|
+
body: { organizationId }
|
|
412
|
+
});
|
|
413
|
+
setCurrentOrgId(organizationId);
|
|
414
|
+
},
|
|
415
|
+
[request]
|
|
416
|
+
);
|
|
417
|
+
const signIn = useCallback(
|
|
418
|
+
async (input) => {
|
|
419
|
+
const options = normalizeSignInOptions(input);
|
|
420
|
+
const url = new URL(buildUrl(basePath, "/sign-in"), window.location.origin);
|
|
421
|
+
if (options?.redirectTo) url.searchParams.set("redirectTo", options.redirectTo);
|
|
422
|
+
if (options?.authorizationFlowId) url.searchParams.set("flow_id", options.authorizationFlowId);
|
|
423
|
+
redirectTo(url.toString());
|
|
424
|
+
},
|
|
425
|
+
[basePath]
|
|
426
|
+
);
|
|
427
|
+
const signUp = useCallback(
|
|
428
|
+
async (input) => {
|
|
429
|
+
const options = normalizeSignInOptions(input);
|
|
430
|
+
const url = new URL(buildUrl(basePath, "/sign-up"), window.location.origin);
|
|
431
|
+
if (options?.redirectTo) url.searchParams.set("redirectTo", options.redirectTo);
|
|
432
|
+
if (options?.authorizationFlowId) url.searchParams.set("flow_id", options.authorizationFlowId);
|
|
433
|
+
redirectTo(url.toString());
|
|
434
|
+
},
|
|
435
|
+
[basePath]
|
|
436
|
+
);
|
|
437
|
+
const addAccount = useCallback(
|
|
438
|
+
async (input) => {
|
|
439
|
+
const options = normalizeSignInOptions(input);
|
|
440
|
+
const url = new URL(buildUrl(basePath, "/add-account"), window.location.origin);
|
|
441
|
+
if (options?.redirectTo) url.searchParams.set("redirectTo", options.redirectTo);
|
|
442
|
+
redirectTo(url.toString());
|
|
443
|
+
},
|
|
444
|
+
[basePath]
|
|
445
|
+
);
|
|
446
|
+
const signOut = useCallback(async () => {
|
|
447
|
+
await request("/sign-out", { method: "POST" });
|
|
448
|
+
setUser(null);
|
|
449
|
+
await refreshAccounts();
|
|
450
|
+
}, [refreshAccounts, request]);
|
|
451
|
+
const signOutAll = useCallback(async () => {
|
|
452
|
+
await request("/sign-out-all", { method: "POST" });
|
|
453
|
+
setUser(null);
|
|
454
|
+
setAccounts([]);
|
|
455
|
+
}, [request]);
|
|
456
|
+
const switchAccount = useCallback(
|
|
457
|
+
async (sessionId, options) => {
|
|
458
|
+
await request("/accounts/switch", {
|
|
459
|
+
method: "POST",
|
|
460
|
+
body: { sessionId }
|
|
461
|
+
});
|
|
462
|
+
await Promise.all([refreshUser(), refreshAccounts(), refreshOrganizations()]);
|
|
463
|
+
if (options?.redirectTo) redirectTo(options.redirectTo);
|
|
464
|
+
},
|
|
465
|
+
[refreshAccounts, refreshOrganizations, refreshUser, request]
|
|
466
|
+
);
|
|
467
|
+
async function toAuthResult(fn) {
|
|
468
|
+
try {
|
|
469
|
+
return { success: true, data: await fn() };
|
|
470
|
+
} catch (error) {
|
|
471
|
+
return { success: false, error: error instanceof Error ? error.message : "Unexpected error" };
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
const value = useMemo(
|
|
475
|
+
() => ({
|
|
476
|
+
basePath,
|
|
477
|
+
user,
|
|
478
|
+
isLoading,
|
|
479
|
+
accounts,
|
|
480
|
+
isLoadingAccounts,
|
|
481
|
+
organizations,
|
|
482
|
+
currentOrg,
|
|
483
|
+
currentOrgId,
|
|
484
|
+
isLoadingOrganizations,
|
|
485
|
+
request,
|
|
486
|
+
signIn,
|
|
487
|
+
signUp,
|
|
488
|
+
addAccount,
|
|
489
|
+
signOut,
|
|
490
|
+
signOutAll,
|
|
491
|
+
refreshUser,
|
|
492
|
+
refreshAccounts,
|
|
493
|
+
switchAccount,
|
|
494
|
+
loginWithPassword: (params) => toAuthResult(async () => {
|
|
495
|
+
const data = await request("/login/password", { method: "POST", body: params });
|
|
496
|
+
await Promise.all([refreshUser(), refreshAccounts(), refreshOrganizations()]);
|
|
497
|
+
if (params.redirectTo ?? data.redirectTo) redirectTo(params.redirectTo ?? data.redirectTo);
|
|
498
|
+
return data;
|
|
499
|
+
}),
|
|
500
|
+
registerWithPassword: (params) => toAuthResult(async () => {
|
|
501
|
+
const data = await request("/register/password", { method: "POST", body: params });
|
|
502
|
+
await Promise.all([refreshUser(), refreshAccounts(), refreshOrganizations()]);
|
|
503
|
+
if (params.redirectTo) redirectTo(params.redirectTo);
|
|
504
|
+
return data;
|
|
505
|
+
}),
|
|
506
|
+
startGoogleLogin: (params) => {
|
|
507
|
+
const url = new URL(buildUrl(basePath, "/login/google"), window.location.origin);
|
|
508
|
+
if (params?.callbackUrl) url.searchParams.set("callbackUrl", params.callbackUrl);
|
|
509
|
+
if (params?.redirectTo) url.searchParams.set("redirectTo", params.redirectTo);
|
|
510
|
+
if (params?.authorizationFlowId) url.searchParams.set("flow_id", params.authorizationFlowId);
|
|
511
|
+
redirectTo(url.toString());
|
|
512
|
+
},
|
|
513
|
+
requestPasswordlessLink: (params) => toAuthResult(() => request("/passwordless/link", { method: "POST", body: params })),
|
|
514
|
+
refreshOrganizations,
|
|
515
|
+
setCurrentOrg,
|
|
516
|
+
createOrganization: (input) => toAuthResult(async () => {
|
|
517
|
+
const organization = await request("/organizations", { method: "POST", body: input });
|
|
518
|
+
await refreshOrganizations();
|
|
519
|
+
setCurrentOrgId(organization.id);
|
|
520
|
+
return organization;
|
|
521
|
+
}),
|
|
522
|
+
updateOrganization: (id, input) => toAuthResult(async () => {
|
|
523
|
+
const organization = await request(`/organizations/${id}`, { method: "PATCH", body: input });
|
|
524
|
+
await refreshOrganizations();
|
|
525
|
+
return organization;
|
|
526
|
+
}),
|
|
527
|
+
deleteOrganization: (id) => toAuthResult(async () => {
|
|
528
|
+
await request(`/organizations/${id}`, { method: "DELETE" });
|
|
529
|
+
await refreshOrganizations();
|
|
530
|
+
return null;
|
|
531
|
+
}),
|
|
532
|
+
hasPermission: async (options) => {
|
|
533
|
+
try {
|
|
534
|
+
const data = await request("/permissions/check", {
|
|
535
|
+
method: "POST",
|
|
536
|
+
body: options
|
|
537
|
+
});
|
|
538
|
+
return data.allowed;
|
|
539
|
+
} catch {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
listUsers: (params) => {
|
|
544
|
+
const query = new URLSearchParams();
|
|
545
|
+
if (params?.organizationId) query.set("organizationId", params.organizationId);
|
|
546
|
+
return request(`/users${query.size ? `?${query.toString()}` : ""}`);
|
|
547
|
+
},
|
|
548
|
+
getUser: (id) => request(`/users/${id}`),
|
|
549
|
+
createUser: (input) => toAuthResult(() => request("/users", { method: "POST", body: input })),
|
|
550
|
+
updateUser: (id, input) => toAuthResult(() => request(`/users/${id}`, { method: "PATCH", body: input })),
|
|
551
|
+
deleteUser: (id) => toAuthResult(async () => {
|
|
552
|
+
await request(`/users/${id}`, { method: "DELETE" });
|
|
553
|
+
return null;
|
|
554
|
+
}),
|
|
555
|
+
listRoles: (params) => {
|
|
556
|
+
const query = new URLSearchParams();
|
|
557
|
+
if (params?.organizationId) query.set("organizationId", params.organizationId);
|
|
558
|
+
if (params?.applicationId) query.set("applicationId", params.applicationId);
|
|
559
|
+
if (params?.type) query.set("type", params.type);
|
|
560
|
+
return request(`/roles${query.size ? `?${query.toString()}` : ""}`);
|
|
561
|
+
},
|
|
562
|
+
getRole: (id, type) => request(type === "custom" ? `/roles/custom/${id}` : `/roles/definitions/${id}`),
|
|
563
|
+
createRole: (input) => toAuthResult(() => request("/roles", { method: "POST", body: input })),
|
|
564
|
+
updateRole: (id, input) => toAuthResult(() => request(`/roles/custom/${id}`, { method: "PATCH", body: input })),
|
|
565
|
+
deleteRole: (id) => toAuthResult(async () => {
|
|
566
|
+
await request(`/roles/custom/${id}`, { method: "DELETE" });
|
|
567
|
+
return null;
|
|
568
|
+
}),
|
|
569
|
+
listPermissions: (params) => {
|
|
570
|
+
const query = new URLSearchParams();
|
|
571
|
+
if (params?.applicationId) query.set("applicationId", params.applicationId);
|
|
572
|
+
if (params?.roleId) query.set("roleId", params.roleId);
|
|
573
|
+
return request(`/permissions${query.size ? `?${query.toString()}` : ""}`);
|
|
574
|
+
},
|
|
575
|
+
createPermission: (input) => toAuthResult(() => request("/permissions", { method: "POST", body: input })),
|
|
576
|
+
updatePermission: (id, input) => toAuthResult(() => request(`/permissions/${id}`, { method: "PATCH", body: input })),
|
|
577
|
+
deletePermission: (id) => toAuthResult(async () => {
|
|
578
|
+
await request(`/permissions/${id}`, { method: "DELETE" });
|
|
579
|
+
return null;
|
|
580
|
+
}),
|
|
581
|
+
listUserSessions: (params) => {
|
|
582
|
+
const query = new URLSearchParams();
|
|
583
|
+
if (params?.userId) query.set("userId", params.userId);
|
|
584
|
+
if (params?.organizationId) query.set("organizationId", params.organizationId);
|
|
585
|
+
return request(`/sessions${query.size ? `?${query.toString()}` : ""}`);
|
|
586
|
+
},
|
|
587
|
+
revokeUserSession: (sessionId) => toAuthResult(async () => {
|
|
588
|
+
await request(`/sessions/${sessionId}`, { method: "DELETE" });
|
|
589
|
+
return null;
|
|
590
|
+
})
|
|
591
|
+
}),
|
|
592
|
+
[
|
|
593
|
+
accounts,
|
|
594
|
+
addAccount,
|
|
595
|
+
basePath,
|
|
596
|
+
currentOrg,
|
|
597
|
+
currentOrgId,
|
|
598
|
+
isLoading,
|
|
599
|
+
isLoadingAccounts,
|
|
600
|
+
isLoadingOrganizations,
|
|
601
|
+
organizations,
|
|
602
|
+
refreshAccounts,
|
|
603
|
+
refreshOrganizations,
|
|
604
|
+
refreshUser,
|
|
605
|
+
request,
|
|
606
|
+
setCurrentOrg,
|
|
607
|
+
signIn,
|
|
608
|
+
signOut,
|
|
609
|
+
signOutAll,
|
|
610
|
+
signUp,
|
|
611
|
+
switchAccount,
|
|
612
|
+
user
|
|
613
|
+
]
|
|
614
|
+
);
|
|
615
|
+
return /* @__PURE__ */ jsx(GeekappsAuthContext.Provider, { value, children });
|
|
616
|
+
}
|
|
617
|
+
function useAuth() {
|
|
618
|
+
const context = useContext(GeekappsAuthContext);
|
|
619
|
+
if (!context) throw new Error("useAuth must be used inside GeekappsAuthProvider");
|
|
620
|
+
return context;
|
|
621
|
+
}
|
|
622
|
+
function useOrg() {
|
|
623
|
+
const context = useAuth();
|
|
624
|
+
return {
|
|
625
|
+
organizations: context.organizations,
|
|
626
|
+
currentOrg: context.currentOrg,
|
|
627
|
+
currentOrgId: context.currentOrgId,
|
|
628
|
+
isLoadingOrganizations: context.isLoadingOrganizations,
|
|
629
|
+
refreshOrganizations: context.refreshOrganizations,
|
|
630
|
+
setCurrentOrg: context.setCurrentOrg,
|
|
631
|
+
createOrganization: context.createOrganization,
|
|
632
|
+
updateOrganization: context.updateOrganization,
|
|
633
|
+
deleteOrganization: context.deleteOrganization,
|
|
634
|
+
hasPermission: context.hasPermission
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function useAccounts() {
|
|
638
|
+
const context = useAuth();
|
|
639
|
+
return {
|
|
640
|
+
accounts: context.accounts,
|
|
641
|
+
addAccount: context.addAccount,
|
|
642
|
+
isLoadingAccounts: context.isLoadingAccounts,
|
|
643
|
+
refreshAccounts: context.refreshAccounts,
|
|
644
|
+
switchAccount: context.switchAccount
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
function useUsers() {
|
|
648
|
+
const context = useAuth();
|
|
649
|
+
return {
|
|
650
|
+
listUsers: context.listUsers,
|
|
651
|
+
getUser: context.getUser,
|
|
652
|
+
createUser: context.createUser,
|
|
653
|
+
updateUser: context.updateUser,
|
|
654
|
+
deleteUser: context.deleteUser
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
function useRoles() {
|
|
658
|
+
const context = useAuth();
|
|
659
|
+
return {
|
|
660
|
+
listRoles: context.listRoles,
|
|
661
|
+
getRole: context.getRole,
|
|
662
|
+
createRole: context.createRole,
|
|
663
|
+
updateRole: context.updateRole,
|
|
664
|
+
deleteRole: context.deleteRole
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
function usePermissions() {
|
|
668
|
+
const context = useAuth();
|
|
669
|
+
return {
|
|
670
|
+
listPermissions: context.listPermissions,
|
|
671
|
+
createPermission: context.createPermission,
|
|
672
|
+
updatePermission: context.updatePermission,
|
|
673
|
+
deletePermission: context.deletePermission
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
function useSessions() {
|
|
677
|
+
const context = useAuth();
|
|
678
|
+
return {
|
|
679
|
+
listUserSessions: context.listUserSessions,
|
|
680
|
+
revokeUserSession: context.revokeUserSession
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
function useServiceAccount() {
|
|
684
|
+
const { request } = useAuth();
|
|
685
|
+
const listServiceAccounts = useCallback(
|
|
686
|
+
(params) => {
|
|
687
|
+
const query = new URLSearchParams({ organizationId: params.organizationId });
|
|
688
|
+
if (params.applicationId) query.set("applicationId", params.applicationId);
|
|
689
|
+
return request(`/service-accounts?${query.toString()}`);
|
|
690
|
+
},
|
|
691
|
+
[request]
|
|
692
|
+
);
|
|
693
|
+
const createServiceAccount = useCallback(
|
|
694
|
+
(input) => request("/service-accounts", { method: "POST", body: input }),
|
|
695
|
+
[request]
|
|
696
|
+
);
|
|
697
|
+
const updateServiceAccountRole = useCallback(
|
|
698
|
+
async (id, roleId) => {
|
|
699
|
+
await request(`/service-accounts/${id}`, { method: "PATCH", body: { roleId } });
|
|
700
|
+
},
|
|
701
|
+
[request]
|
|
702
|
+
);
|
|
703
|
+
const revokeServiceAccount = useCallback(
|
|
704
|
+
async (id) => {
|
|
705
|
+
await request(`/service-accounts/${id}/revoke`, { method: "POST" });
|
|
706
|
+
},
|
|
707
|
+
[request]
|
|
708
|
+
);
|
|
709
|
+
const rotateServiceAccountSecret = useCallback(
|
|
710
|
+
(id) => request(`/service-accounts/${id}/rotate-secret`, {
|
|
711
|
+
method: "POST"
|
|
712
|
+
}),
|
|
713
|
+
[request]
|
|
714
|
+
);
|
|
715
|
+
const deleteServiceAccount = useCallback(
|
|
716
|
+
async (id) => {
|
|
717
|
+
await request(`/service-accounts/${id}`, { method: "DELETE" });
|
|
718
|
+
},
|
|
719
|
+
[request]
|
|
720
|
+
);
|
|
721
|
+
return {
|
|
722
|
+
listServiceAccounts,
|
|
723
|
+
createServiceAccount,
|
|
724
|
+
updateServiceAccountRole,
|
|
725
|
+
revokeServiceAccount,
|
|
726
|
+
rotateServiceAccountSecret,
|
|
727
|
+
deleteServiceAccount
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
export {
|
|
731
|
+
GeekappsAuthProvider,
|
|
732
|
+
loginWithPassword,
|
|
733
|
+
registerWithPassword,
|
|
734
|
+
requestPasswordlessLink,
|
|
735
|
+
startGoogleLogin,
|
|
736
|
+
useAccounts,
|
|
737
|
+
useAuth,
|
|
738
|
+
useOrg,
|
|
739
|
+
usePermissions,
|
|
740
|
+
useRoles,
|
|
741
|
+
useServiceAccount,
|
|
742
|
+
useSessions,
|
|
743
|
+
useUsers
|
|
744
|
+
};
|
|
745
|
+
//# sourceMappingURL=client.js.map
|