@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
- (url, init) => fetch(url, { ...init, credentials: "include" }),
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(`${baseUrl}/${projectId}/auth/config`);
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(buildUrl("verify-email"), {
480
- method: "POST",
481
- headers: { "Content-Type": "application/json" },
482
- body: JSON.stringify({ token: verifyEmailToken })
483
- }).then((res) => res.json()).then(() => {
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-D4rCSC9H.js';
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
@@ -4,7 +4,7 @@ import {
4
4
  useOtpForm,
5
5
  useResetPasswordForm,
6
6
  useSignupForm
7
- } from "./chunk-GV63J77S.js";
7
+ } from "./chunk-E6J2FYDA.js";
8
8
  export {
9
9
  useForgotPasswordForm,
10
10
  useLoginForm,
@@ -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-D4rCSC9H.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-D4rCSC9H.js';
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-GV63J77S.js";
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.1.0",
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
+ }