@instroc/auth 1.1.0 → 1.2.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.
|
@@ -27,10 +27,34 @@ function authErrorFromResponse(response, data, fallbackMessage) {
|
|
|
27
27
|
return new AuthError(message, response.status, body.code);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// src/version.ts
|
|
31
|
+
var SDK_NAME = "instroc-auth";
|
|
32
|
+
var SDK_VERSION = true ? "1.2.0" : "0.0.0-dev";
|
|
33
|
+
function withSdkHeader(init) {
|
|
34
|
+
const headers = new Headers(init?.headers ?? {});
|
|
35
|
+
headers.set("X-Instroc-SDK", `${SDK_NAME}/${SDK_VERSION}`);
|
|
36
|
+
return { ...init, headers };
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
// src/provider.tsx
|
|
31
40
|
import { jsx } from "react/jsx-runtime";
|
|
32
41
|
var AuthContext = createContext(null);
|
|
33
42
|
var STORAGE_KEY = "instroc_auth_session";
|
|
43
|
+
var WARNING_SESSION_KEY = "instroc_auth_sdk_warning_seen";
|
|
44
|
+
function noteDeprecationWarning(response) {
|
|
45
|
+
const warning = response.headers?.get?.("X-Instroc-SDK-Warning");
|
|
46
|
+
if (!warning)
|
|
47
|
+
return;
|
|
48
|
+
if (typeof window === "undefined")
|
|
49
|
+
return;
|
|
50
|
+
try {
|
|
51
|
+
if (sessionStorage.getItem(WARNING_SESSION_KEY))
|
|
52
|
+
return;
|
|
53
|
+
sessionStorage.setItem(WARNING_SESSION_KEY, "1");
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
console.warn(`[instroc-auth] ${warning}`);
|
|
57
|
+
}
|
|
34
58
|
function AuthProvider({
|
|
35
59
|
children,
|
|
36
60
|
projectId: initialProjectId,
|
|
@@ -57,10 +81,14 @@ function AuthProvider({
|
|
|
57
81
|
},
|
|
58
82
|
[projectId, baseUrl]
|
|
59
83
|
);
|
|
60
|
-
const authFetch = useCallback(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
84
|
+
const authFetch = useCallback(async (url, init) => {
|
|
85
|
+
const response = await fetch(url, {
|
|
86
|
+
...withSdkHeader(init),
|
|
87
|
+
credentials: "include"
|
|
88
|
+
});
|
|
89
|
+
noteDeprecationWarning(response);
|
|
90
|
+
return response;
|
|
91
|
+
}, []);
|
|
64
92
|
const saveSession = useCallback(
|
|
65
93
|
(sessionData) => {
|
|
66
94
|
if (persistSession && typeof window !== "undefined") {
|
|
@@ -433,7 +461,11 @@ function AuthProvider({
|
|
|
433
461
|
if (!projectId)
|
|
434
462
|
return;
|
|
435
463
|
try {
|
|
436
|
-
const response = await fetch(
|
|
464
|
+
const response = await fetch(
|
|
465
|
+
`${baseUrl}/${projectId}/auth/config`,
|
|
466
|
+
withSdkHeader()
|
|
467
|
+
);
|
|
468
|
+
noteDeprecationWarning(response);
|
|
437
469
|
if (response.ok) {
|
|
438
470
|
const data = await response.json();
|
|
439
471
|
setAuthConfig(data.config || null);
|
|
@@ -476,11 +508,14 @@ function AuthProvider({
|
|
|
476
508
|
const params = new URLSearchParams(window.location.search);
|
|
477
509
|
const verifyEmailToken = params.get("verify_email");
|
|
478
510
|
if (verifyEmailToken && projectId) {
|
|
479
|
-
fetch(
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
511
|
+
fetch(
|
|
512
|
+
buildUrl("verify-email"),
|
|
513
|
+
withSdkHeader({
|
|
514
|
+
method: "POST",
|
|
515
|
+
headers: { "Content-Type": "application/json" },
|
|
516
|
+
body: JSON.stringify({ token: verifyEmailToken })
|
|
517
|
+
})
|
|
518
|
+
).then((res) => res.json()).then(() => {
|
|
484
519
|
window.history.replaceState({}, "", window.location.pathname);
|
|
485
520
|
}).catch(() => {
|
|
486
521
|
window.history.replaceState({}, "", window.location.pathname);
|
|
@@ -561,6 +596,30 @@ function useAuthRequired() {
|
|
|
561
596
|
}
|
|
562
597
|
return { user, session, loading: false };
|
|
563
598
|
}
|
|
599
|
+
function useIsOwner() {
|
|
600
|
+
const { user, session, loading } = useAuthContext();
|
|
601
|
+
if (loading || !user || !session)
|
|
602
|
+
return false;
|
|
603
|
+
if (user.is_owner === true)
|
|
604
|
+
return true;
|
|
605
|
+
const claim = decodeIsOwnerClaim(session.access_token);
|
|
606
|
+
return claim === true;
|
|
607
|
+
}
|
|
608
|
+
function decodeIsOwnerClaim(accessToken) {
|
|
609
|
+
try {
|
|
610
|
+
const parts = accessToken.split(".");
|
|
611
|
+
if (parts.length !== 3)
|
|
612
|
+
return void 0;
|
|
613
|
+
const payload = parts[1];
|
|
614
|
+
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
615
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
616
|
+
const json = typeof atob !== "undefined" ? atob(padded) : Buffer.from(padded, "base64").toString("utf-8");
|
|
617
|
+
const decoded = JSON.parse(json);
|
|
618
|
+
return decoded.is_owner;
|
|
619
|
+
} catch {
|
|
620
|
+
return void 0;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
564
623
|
|
|
565
624
|
// src/forms/use-form-action.ts
|
|
566
625
|
function resolveErrorMessage(err, messages, fallback) {
|
|
@@ -927,6 +986,7 @@ export {
|
|
|
927
986
|
useUser,
|
|
928
987
|
useSession,
|
|
929
988
|
useAuthRequired,
|
|
989
|
+
useIsOwner,
|
|
930
990
|
useLoginForm,
|
|
931
991
|
useSignupForm,
|
|
932
992
|
useOtpForm,
|
package/dist/forms.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { M as MessageOverrides, O as OnErrorResult, m as UseForgotPasswordFormOptions, n as UseForgotPasswordFormReturn, U as UseLoginFormOptions, h as UseLoginFormReturn, k as UseOtpFormOptions, l as UseOtpFormReturn, o as UseResetPasswordFormOptions, p as UseResetPasswordFormReturn, i as UseSignupFormOptions, j as UseSignupFormReturn, f as useForgotPasswordForm, u as useLoginForm, e as useOtpForm, g as useResetPasswordForm, d as useSignupForm } from './index-
|
|
1
|
+
export { M as MessageOverrides, O as OnErrorResult, m as UseForgotPasswordFormOptions, n as UseForgotPasswordFormReturn, U as UseLoginFormOptions, h as UseLoginFormReturn, k as UseOtpFormOptions, l as UseOtpFormReturn, o as UseResetPasswordFormOptions, p as UseResetPasswordFormReturn, i as UseSignupFormOptions, j as UseSignupFormReturn, f as useForgotPasswordForm, u as useLoginForm, e as useOtpForm, g as useResetPasswordForm, d as useSignupForm } from './index-iglVgSQI.js';
|
package/dist/forms.js
CHANGED
|
@@ -6,6 +6,13 @@ interface AuthUser {
|
|
|
6
6
|
avatar_url: string | null;
|
|
7
7
|
metadata: Record<string, unknown>;
|
|
8
8
|
created_at: string;
|
|
9
|
+
/**
|
|
10
|
+
* True for the project owner (first user or explicitly designated). Mirrors
|
|
11
|
+
* the `is_owner` JWT claim used for RLS bypass on the server. Templates
|
|
12
|
+
* gate admin UIs (/admin, /studio) on this field so only the owner sees
|
|
13
|
+
* CRUD controls in production.
|
|
14
|
+
*/
|
|
15
|
+
is_owner?: boolean;
|
|
9
16
|
}
|
|
10
17
|
interface AuthSession {
|
|
11
18
|
access_token: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as AuthProviderProps, a as AuthContextValue, b as AuthUser, c as AuthSession } from './index-
|
|
2
|
-
export { r as AuthConfig, w as AuthResponse, q as AuthState, L as LoginCredentials, M as MessageOverrides, s as OAuthProvider, O as OnErrorResult, R as RefreshResponse, S as SignupCredentials, t as SignupResult, v as UpdateProfileData, m as UseForgotPasswordFormOptions, n as UseForgotPasswordFormReturn, U as UseLoginFormOptions, h as UseLoginFormReturn, k as UseOtpFormOptions, l as UseOtpFormReturn, o as UseResetPasswordFormOptions, p as UseResetPasswordFormReturn, i as UseSignupFormOptions, j as UseSignupFormReturn, V as VisibilityConfig, f as useForgotPasswordForm, u as useLoginForm, e as useOtpForm, g as useResetPasswordForm, d as useSignupForm } from './index-
|
|
1
|
+
import { A as AuthProviderProps, a as AuthContextValue, b as AuthUser, c as AuthSession } from './index-iglVgSQI.js';
|
|
2
|
+
export { r as AuthConfig, w as AuthResponse, q as AuthState, L as LoginCredentials, M as MessageOverrides, s as OAuthProvider, O as OnErrorResult, R as RefreshResponse, S as SignupCredentials, t as SignupResult, v as UpdateProfileData, m as UseForgotPasswordFormOptions, n as UseForgotPasswordFormReturn, U as UseLoginFormOptions, h as UseLoginFormReturn, k as UseOtpFormOptions, l as UseOtpFormReturn, o as UseResetPasswordFormOptions, p as UseResetPasswordFormReturn, i as UseSignupFormOptions, j as UseSignupFormReturn, V as VisibilityConfig, f as useForgotPasswordForm, u as useLoginForm, e as useOtpForm, g as useResetPasswordForm, d as useSignupForm } from './index-iglVgSQI.js';
|
|
3
3
|
|
|
4
4
|
declare function AuthProvider({ children, projectId: initialProjectId, baseUrl, persistSession, onAuthStateChange, }: AuthProviderProps): JSX.Element;
|
|
5
5
|
declare function useAuthContext(): AuthContextValue;
|
|
@@ -22,6 +22,18 @@ declare function useAuthRequired(): {
|
|
|
22
22
|
session: AuthSession | null;
|
|
23
23
|
loading: boolean;
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Returns true if the current user is the project owner. Used by templates
|
|
27
|
+
* to gate admin/studio routes.
|
|
28
|
+
*
|
|
29
|
+
* Reads `user.is_owner` first (populated by the `/me` endpoint). Falls back
|
|
30
|
+
* to decoding the access token's `is_owner` claim so the check is accurate
|
|
31
|
+
* immediately after login (the login response doesn't echo is_owner — only
|
|
32
|
+
* `/me` does, and that only runs on mount).
|
|
33
|
+
*
|
|
34
|
+
* Returns `false` while loading or when unauthenticated.
|
|
35
|
+
*/
|
|
36
|
+
declare function useIsOwner(): boolean;
|
|
25
37
|
|
|
26
38
|
/**
|
|
27
39
|
* Typed error thrown by every `@instroc/auth` method when the server responds
|
|
@@ -54,4 +66,4 @@ declare class AuthError extends Error {
|
|
|
54
66
|
constructor(message: string, status: number, code?: string);
|
|
55
67
|
}
|
|
56
68
|
|
|
57
|
-
export { AuthContextValue, AuthError, AuthProvider, AuthProviderProps, AuthSession, AuthUser, useAuth, useAuthContext, useAuthRequired, useSession, useUser };
|
|
69
|
+
export { AuthContextValue, AuthError, AuthProvider, AuthProviderProps, AuthSession, AuthUser, useAuth, useAuthContext, useAuthRequired, useIsOwner, useSession, useUser };
|
package/dist/index.js
CHANGED
|
@@ -5,13 +5,14 @@ import {
|
|
|
5
5
|
useAuthContext,
|
|
6
6
|
useAuthRequired,
|
|
7
7
|
useForgotPasswordForm,
|
|
8
|
+
useIsOwner,
|
|
8
9
|
useLoginForm,
|
|
9
10
|
useOtpForm,
|
|
10
11
|
useResetPasswordForm,
|
|
11
12
|
useSession,
|
|
12
13
|
useSignupForm,
|
|
13
14
|
useUser
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-E6J2FYDA.js";
|
|
15
16
|
export {
|
|
16
17
|
AuthError,
|
|
17
18
|
AuthProvider,
|
|
@@ -19,6 +20,7 @@ export {
|
|
|
19
20
|
useAuthContext,
|
|
20
21
|
useAuthRequired,
|
|
21
22
|
useForgotPasswordForm,
|
|
23
|
+
useIsOwner,
|
|
22
24
|
useLoginForm,
|
|
23
25
|
useOtpForm,
|
|
24
26
|
useResetPasswordForm,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instroc/auth",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Authentication hooks for Instroc Cloud — useAuth, useUser, AuthProvider, and headless form hooks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,10 +18,6 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsup",
|
|
23
|
-
"dev": "tsup --watch"
|
|
24
|
-
},
|
|
25
21
|
"peerDependencies": {
|
|
26
22
|
"react": "^18.0.0"
|
|
27
23
|
},
|
|
@@ -39,5 +35,9 @@
|
|
|
39
35
|
},
|
|
40
36
|
"publishConfig": {
|
|
41
37
|
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"dev": "tsup --watch"
|
|
42
42
|
}
|
|
43
|
-
}
|
|
43
|
+
}
|