@instroc/auth 1.1.1 → 1.3.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.
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// src/forms/use-login-form.ts
|
|
2
|
-
import { useState as
|
|
2
|
+
import { useState as useState3, useCallback as useCallback2, useRef as useRef2, useEffect as useEffect3 } from "react";
|
|
3
|
+
|
|
4
|
+
// src/use-auth.ts
|
|
5
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
3
6
|
|
|
4
7
|
// src/provider.tsx
|
|
5
8
|
import {
|
|
@@ -29,7 +32,7 @@ function authErrorFromResponse(response, data, fallbackMessage) {
|
|
|
29
32
|
|
|
30
33
|
// src/version.ts
|
|
31
34
|
var SDK_NAME = "instroc-auth";
|
|
32
|
-
var SDK_VERSION = true ? "1.
|
|
35
|
+
var SDK_VERSION = true ? "1.3.0" : "0.0.0-dev";
|
|
33
36
|
function withSdkHeader(init) {
|
|
34
37
|
const headers = new Headers(init?.headers ?? {});
|
|
35
38
|
headers.set("X-Instroc-SDK", `${SDK_NAME}/${SDK_VERSION}`);
|
|
@@ -42,7 +45,7 @@ var AuthContext = createContext(null);
|
|
|
42
45
|
var STORAGE_KEY = "instroc_auth_session";
|
|
43
46
|
var WARNING_SESSION_KEY = "instroc_auth_sdk_warning_seen";
|
|
44
47
|
function noteDeprecationWarning(response) {
|
|
45
|
-
const warning = response.headers
|
|
48
|
+
const warning = response.headers?.get?.("X-Instroc-SDK-Warning");
|
|
46
49
|
if (!warning)
|
|
47
50
|
return;
|
|
48
51
|
if (typeof window === "undefined")
|
|
@@ -596,6 +599,67 @@ function useAuthRequired() {
|
|
|
596
599
|
}
|
|
597
600
|
return { user, session, loading: false };
|
|
598
601
|
}
|
|
602
|
+
function useIsOwner() {
|
|
603
|
+
const { user, session, loading } = useAuthContext();
|
|
604
|
+
if (loading || !user || !session)
|
|
605
|
+
return false;
|
|
606
|
+
if (user.is_owner === true)
|
|
607
|
+
return true;
|
|
608
|
+
const claim = decodeIsOwnerClaim(session.access_token);
|
|
609
|
+
return claim === true;
|
|
610
|
+
}
|
|
611
|
+
function useIsWorkspaceMember() {
|
|
612
|
+
const [isMember, setIsMember] = useState2(() => isDevHost());
|
|
613
|
+
useEffect2(() => {
|
|
614
|
+
if (typeof window === "undefined")
|
|
615
|
+
return;
|
|
616
|
+
let cancelled = false;
|
|
617
|
+
(async () => {
|
|
618
|
+
try {
|
|
619
|
+
const res = await fetch("/__instroc/workspace-status", {
|
|
620
|
+
credentials: "include",
|
|
621
|
+
cache: "no-store"
|
|
622
|
+
});
|
|
623
|
+
if (cancelled)
|
|
624
|
+
return;
|
|
625
|
+
if (!res.ok) {
|
|
626
|
+
setIsMember(isDevHost());
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
const data = await res.json();
|
|
630
|
+
setIsMember(data.isWorkspaceMember === true);
|
|
631
|
+
} catch {
|
|
632
|
+
if (!cancelled)
|
|
633
|
+
setIsMember(isDevHost());
|
|
634
|
+
}
|
|
635
|
+
})();
|
|
636
|
+
return () => {
|
|
637
|
+
cancelled = true;
|
|
638
|
+
};
|
|
639
|
+
}, []);
|
|
640
|
+
return isMember;
|
|
641
|
+
}
|
|
642
|
+
function isDevHost() {
|
|
643
|
+
if (typeof window === "undefined")
|
|
644
|
+
return false;
|
|
645
|
+
const h = window.location.hostname;
|
|
646
|
+
return h === "localhost" || h === "127.0.0.1" || h.endsWith(".fly.dev") || h.endsWith(".workers.dev");
|
|
647
|
+
}
|
|
648
|
+
function decodeIsOwnerClaim(accessToken) {
|
|
649
|
+
try {
|
|
650
|
+
const parts = accessToken.split(".");
|
|
651
|
+
if (parts.length !== 3)
|
|
652
|
+
return void 0;
|
|
653
|
+
const payload = parts[1];
|
|
654
|
+
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
655
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
656
|
+
const json = typeof atob !== "undefined" ? atob(padded) : Buffer.from(padded, "base64").toString("utf-8");
|
|
657
|
+
const decoded = JSON.parse(json);
|
|
658
|
+
return decoded.is_owner;
|
|
659
|
+
} catch {
|
|
660
|
+
return void 0;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
599
663
|
|
|
600
664
|
// src/forms/use-form-action.ts
|
|
601
665
|
function resolveErrorMessage(err, messages, fallback) {
|
|
@@ -623,10 +687,10 @@ function applyErrorResult(err, onError, messages, fallback, setError) {
|
|
|
623
687
|
function useLoginForm(options = {}) {
|
|
624
688
|
const { login, user } = useAuth();
|
|
625
689
|
const { onSuccess, onNeedsVerification, onError, messages } = options;
|
|
626
|
-
const [loading, setLoading] =
|
|
627
|
-
const [error, setError] =
|
|
690
|
+
const [loading, setLoading] = useState3(false);
|
|
691
|
+
const [error, setError] = useState3(null);
|
|
628
692
|
const mountedRef = useRef2(true);
|
|
629
|
-
|
|
693
|
+
useEffect3(() => {
|
|
630
694
|
mountedRef.current = true;
|
|
631
695
|
return () => {
|
|
632
696
|
mountedRef.current = false;
|
|
@@ -678,14 +742,14 @@ function useLoginForm(options = {}) {
|
|
|
678
742
|
}
|
|
679
743
|
|
|
680
744
|
// src/forms/use-signup-form.ts
|
|
681
|
-
import { useState as
|
|
745
|
+
import { useState as useState4, useCallback as useCallback3, useRef as useRef3, useEffect as useEffect4 } from "react";
|
|
682
746
|
function useSignupForm(options = {}) {
|
|
683
747
|
const { signup } = useAuth();
|
|
684
748
|
const { onSuccess, onNeedsVerification, onNeedsApproval, onError, messages } = options;
|
|
685
|
-
const [loading, setLoading] =
|
|
686
|
-
const [error, setError] =
|
|
749
|
+
const [loading, setLoading] = useState4(false);
|
|
750
|
+
const [error, setError] = useState4(null);
|
|
687
751
|
const mountedRef = useRef3(true);
|
|
688
|
-
|
|
752
|
+
useEffect4(() => {
|
|
689
753
|
mountedRef.current = true;
|
|
690
754
|
return () => {
|
|
691
755
|
mountedRef.current = false;
|
|
@@ -742,7 +806,7 @@ function useSignupForm(options = {}) {
|
|
|
742
806
|
}
|
|
743
807
|
|
|
744
808
|
// src/forms/use-otp-form.ts
|
|
745
|
-
import { useState as
|
|
809
|
+
import { useState as useState5, useCallback as useCallback4, useRef as useRef4, useEffect as useEffect5 } from "react";
|
|
746
810
|
function useOtpForm(options) {
|
|
747
811
|
const { verifyOTP, resendOTP } = useAuth();
|
|
748
812
|
const {
|
|
@@ -753,18 +817,18 @@ function useOtpForm(options) {
|
|
|
753
817
|
messages,
|
|
754
818
|
resendCooldownSeconds = 60
|
|
755
819
|
} = options;
|
|
756
|
-
const [loading, setLoading] =
|
|
757
|
-
const [resending, setResending] =
|
|
758
|
-
const [resendCooldown, setResendCooldown] =
|
|
759
|
-
const [error, setError] =
|
|
820
|
+
const [loading, setLoading] = useState5(false);
|
|
821
|
+
const [resending, setResending] = useState5(false);
|
|
822
|
+
const [resendCooldown, setResendCooldown] = useState5(0);
|
|
823
|
+
const [error, setError] = useState5(null);
|
|
760
824
|
const mountedRef = useRef4(true);
|
|
761
|
-
|
|
825
|
+
useEffect5(() => {
|
|
762
826
|
mountedRef.current = true;
|
|
763
827
|
return () => {
|
|
764
828
|
mountedRef.current = false;
|
|
765
829
|
};
|
|
766
830
|
}, []);
|
|
767
|
-
|
|
831
|
+
useEffect5(() => {
|
|
768
832
|
if (resendCooldown <= 0)
|
|
769
833
|
return;
|
|
770
834
|
const id = setTimeout(() => setResendCooldown((s) => s - 1), 1e3);
|
|
@@ -846,15 +910,15 @@ function useOtpForm(options) {
|
|
|
846
910
|
}
|
|
847
911
|
|
|
848
912
|
// src/forms/use-forgot-password-form.ts
|
|
849
|
-
import { useState as
|
|
913
|
+
import { useState as useState6, useCallback as useCallback5, useRef as useRef5, useEffect as useEffect6 } from "react";
|
|
850
914
|
function useForgotPasswordForm(options = {}) {
|
|
851
915
|
const { forgotPassword } = useAuth();
|
|
852
916
|
const { onSuccess, onError, messages } = options;
|
|
853
|
-
const [loading, setLoading] =
|
|
854
|
-
const [submitted, setSubmitted] =
|
|
855
|
-
const [error, setError] =
|
|
917
|
+
const [loading, setLoading] = useState6(false);
|
|
918
|
+
const [submitted, setSubmitted] = useState6(false);
|
|
919
|
+
const [error, setError] = useState6(null);
|
|
856
920
|
const mountedRef = useRef5(true);
|
|
857
|
-
|
|
921
|
+
useEffect6(() => {
|
|
858
922
|
mountedRef.current = true;
|
|
859
923
|
return () => {
|
|
860
924
|
mountedRef.current = false;
|
|
@@ -909,14 +973,14 @@ function useForgotPasswordForm(options = {}) {
|
|
|
909
973
|
}
|
|
910
974
|
|
|
911
975
|
// src/forms/use-reset-password-form.ts
|
|
912
|
-
import { useState as
|
|
976
|
+
import { useState as useState7, useCallback as useCallback6, useRef as useRef6, useEffect as useEffect7 } from "react";
|
|
913
977
|
function useResetPasswordForm(options) {
|
|
914
978
|
const { resetPassword } = useAuth();
|
|
915
979
|
const { token, onSuccess, onError, messages } = options;
|
|
916
|
-
const [loading, setLoading] =
|
|
917
|
-
const [error, setError] =
|
|
980
|
+
const [loading, setLoading] = useState7(false);
|
|
981
|
+
const [error, setError] = useState7(null);
|
|
918
982
|
const mountedRef = useRef6(true);
|
|
919
|
-
|
|
983
|
+
useEffect7(() => {
|
|
920
984
|
mountedRef.current = true;
|
|
921
985
|
return () => {
|
|
922
986
|
mountedRef.current = false;
|
|
@@ -962,6 +1026,8 @@ export {
|
|
|
962
1026
|
useUser,
|
|
963
1027
|
useSession,
|
|
964
1028
|
useAuthRequired,
|
|
1029
|
+
useIsOwner,
|
|
1030
|
+
useIsWorkspaceMember,
|
|
965
1031
|
useLoginForm,
|
|
966
1032
|
useSignupForm,
|
|
967
1033
|
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,47 @@ 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;
|
|
37
|
+
/**
|
|
38
|
+
* Minimal JWT payload decoder — no signature check (the server already
|
|
39
|
+
* verified it). Returns undefined if the token is malformed. We only read
|
|
40
|
+
* the `is_owner` boolean claim, which the BaaS auth signer sets from
|
|
41
|
+
* `_users.is_owner`.
|
|
42
|
+
*/
|
|
43
|
+
/**
|
|
44
|
+
* Returns true if the current visitor is a member of the Instroc workspace
|
|
45
|
+
* that owns this published app. Used by premium templates to gate owner-only
|
|
46
|
+
* UI such as `/admin` or `/studio` routes.
|
|
47
|
+
*
|
|
48
|
+
* This is distinct from {@link useIsOwner} — workspace members are the humans
|
|
49
|
+
* who built the project (via Instroc), whereas app users (BaaS `_users`) are
|
|
50
|
+
* the end users of the published app. The two populations never overlap, so
|
|
51
|
+
* `useIsOwner` always returns `false` for regular app visitors. Admin UI on
|
|
52
|
+
* premium templates should gate on this hook instead.
|
|
53
|
+
*
|
|
54
|
+
* Implementation: calls `GET /__instroc/workspace-status` on the subdomain
|
|
55
|
+
* router, which validates the `instroc_wa` cookie (HttpOnly, set after
|
|
56
|
+
* Instroc workspace login) against the project's `workspaceId`. The same
|
|
57
|
+
* cookie is what the privacy gate uses to allow workspace members into
|
|
58
|
+
* private apps.
|
|
59
|
+
*
|
|
60
|
+
* Dev/preview note: when running on `localhost`, `*.fly.dev`, or
|
|
61
|
+
* `*.workers.dev` (dev-session HMR, editor preview), the hook returns `true`
|
|
62
|
+
* so template authors can see admin UI while building. In production
|
|
63
|
+
* (`*.instroc.app` and custom domains) the server answer is authoritative.
|
|
64
|
+
*/
|
|
65
|
+
declare function useIsWorkspaceMember(): boolean;
|
|
25
66
|
|
|
26
67
|
/**
|
|
27
68
|
* Typed error thrown by every `@instroc/auth` method when the server responds
|
|
@@ -54,4 +95,4 @@ declare class AuthError extends Error {
|
|
|
54
95
|
constructor(message: string, status: number, code?: string);
|
|
55
96
|
}
|
|
56
97
|
|
|
57
|
-
export { AuthContextValue, AuthError, AuthProvider, AuthProviderProps, AuthSession, AuthUser, useAuth, useAuthContext, useAuthRequired, useSession, useUser };
|
|
98
|
+
export { AuthContextValue, AuthError, AuthProvider, AuthProviderProps, AuthSession, AuthUser, useAuth, useAuthContext, useAuthRequired, useIsOwner, useIsWorkspaceMember, useSession, useUser };
|
package/dist/index.js
CHANGED
|
@@ -5,13 +5,15 @@ import {
|
|
|
5
5
|
useAuthContext,
|
|
6
6
|
useAuthRequired,
|
|
7
7
|
useForgotPasswordForm,
|
|
8
|
+
useIsOwner,
|
|
9
|
+
useIsWorkspaceMember,
|
|
8
10
|
useLoginForm,
|
|
9
11
|
useOtpForm,
|
|
10
12
|
useResetPasswordForm,
|
|
11
13
|
useSession,
|
|
12
14
|
useSignupForm,
|
|
13
15
|
useUser
|
|
14
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-WPSPVFVM.js";
|
|
15
17
|
export {
|
|
16
18
|
AuthError,
|
|
17
19
|
AuthProvider,
|
|
@@ -19,6 +21,8 @@ export {
|
|
|
19
21
|
useAuthContext,
|
|
20
22
|
useAuthRequired,
|
|
21
23
|
useForgotPasswordForm,
|
|
24
|
+
useIsOwner,
|
|
25
|
+
useIsWorkspaceMember,
|
|
22
26
|
useLoginForm,
|
|
23
27
|
useOtpForm,
|
|
24
28
|
useResetPasswordForm,
|