authfyio-react 0.3.9

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.
Files changed (68) hide show
  1. package/README.md +83 -0
  2. package/dist/client.d.ts +128 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +227 -0
  5. package/dist/components.d.ts +79 -0
  6. package/dist/components.d.ts.map +1 -0
  7. package/dist/components.js +111 -0
  8. package/dist/hooks-flow.d.ts +59 -0
  9. package/dist/hooks-flow.d.ts.map +1 -0
  10. package/dist/hooks-flow.js +133 -0
  11. package/dist/hooks.d.ts +113 -0
  12. package/dist/hooks.d.ts.map +1 -0
  13. package/dist/hooks.js +212 -0
  14. package/dist/index.d.ts +8 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +15 -0
  17. package/dist/jwt.d.ts +20 -0
  18. package/dist/jwt.d.ts.map +1 -0
  19. package/dist/jwt.js +30 -0
  20. package/dist/provider.d.ts +50 -0
  21. package/dist/provider.d.ts.map +1 -0
  22. package/dist/provider.js +91 -0
  23. package/dist/ui/CheckoutButton.d.ts +14 -0
  24. package/dist/ui/CheckoutButton.d.ts.map +1 -0
  25. package/dist/ui/CheckoutButton.js +28 -0
  26. package/dist/ui/Control.d.ts +17 -0
  27. package/dist/ui/Control.d.ts.map +1 -0
  28. package/dist/ui/Control.js +33 -0
  29. package/dist/ui/CreateOrganization.d.ts +19 -0
  30. package/dist/ui/CreateOrganization.d.ts.map +1 -0
  31. package/dist/ui/CreateOrganization.js +75 -0
  32. package/dist/ui/OrganizationList.d.ts +13 -0
  33. package/dist/ui/OrganizationList.d.ts.map +1 -0
  34. package/dist/ui/OrganizationList.js +47 -0
  35. package/dist/ui/OrganizationProfile.d.ts +12 -0
  36. package/dist/ui/OrganizationProfile.d.ts.map +1 -0
  37. package/dist/ui/OrganizationProfile.js +116 -0
  38. package/dist/ui/OrganizationSwitcher.d.ts +17 -0
  39. package/dist/ui/OrganizationSwitcher.d.ts.map +1 -0
  40. package/dist/ui/OrganizationSwitcher.js +59 -0
  41. package/dist/ui/PricingTable.d.ts +15 -0
  42. package/dist/ui/PricingTable.d.ts.map +1 -0
  43. package/dist/ui/PricingTable.js +74 -0
  44. package/dist/ui/SignIn.d.ts +23 -0
  45. package/dist/ui/SignIn.d.ts.map +1 -0
  46. package/dist/ui/SignIn.js +489 -0
  47. package/dist/ui/SignUp.d.ts +18 -0
  48. package/dist/ui/SignUp.d.ts.map +1 -0
  49. package/dist/ui/SignUp.js +153 -0
  50. package/dist/ui/UnstyledButtons.d.ts +24 -0
  51. package/dist/ui/UnstyledButtons.d.ts.map +1 -0
  52. package/dist/ui/UnstyledButtons.js +42 -0
  53. package/dist/ui/UserAvatar.d.ts +14 -0
  54. package/dist/ui/UserAvatar.d.ts.map +1 -0
  55. package/dist/ui/UserAvatar.js +52 -0
  56. package/dist/ui/UserButton.d.ts +15 -0
  57. package/dist/ui/UserButton.d.ts.map +1 -0
  58. package/dist/ui/UserButton.js +82 -0
  59. package/dist/ui/UserProfile.d.ts +19 -0
  60. package/dist/ui/UserProfile.d.ts.map +1 -0
  61. package/dist/ui/UserProfile.js +199 -0
  62. package/dist/ui/index.d.ts +14 -0
  63. package/dist/ui/index.d.ts.map +1 -0
  64. package/dist/ui/index.js +13 -0
  65. package/dist/ui/styles.d.ts +10 -0
  66. package/dist/ui/styles.d.ts.map +1 -0
  67. package/dist/ui/styles.js +291 -0
  68. package/package.json +46 -0
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Higher-level hooks that wrap the raw auth endpoints with a controlled-form
3
+ * API. Use these to build a fully custom sign-in / sign-up UI without
4
+ * reaching for fetch() every time.
5
+ */
6
+ export type SignInCreateParams = {
7
+ identifier: string;
8
+ password?: string;
9
+ };
10
+ export type SignInVerifyParams = {
11
+ code: string;
12
+ };
13
+ export type SignInResource = {
14
+ isLoading: boolean;
15
+ error: string | null;
16
+ /** Start email + password sign-in. Returns { status: 'complete' } on success. */
17
+ create: (p: SignInCreateParams) => Promise<{
18
+ status: 'complete' | 'needs_verification';
19
+ }>;
20
+ /** Start magic link / SMS OTP. Tell the user to watch their inbox / phone. */
21
+ sendCode: (p: {
22
+ strategy: 'magic_link' | 'sms_otp';
23
+ identifier: string;
24
+ }) => Promise<{
25
+ ok: boolean;
26
+ }>;
27
+ /** Verify SMS OTP. */
28
+ verifyCode: (p: SignInVerifyParams & {
29
+ phone: string;
30
+ }) => Promise<{
31
+ status: 'complete';
32
+ }>;
33
+ /** Start a Google / GitHub OAuth flow — returns the URL to navigate to. */
34
+ authenticateWithRedirect: (p: {
35
+ strategy: 'oauth_google' | 'oauth_github';
36
+ redirectUrl?: string;
37
+ }) => string;
38
+ };
39
+ export type SignUpCreateParams = {
40
+ emailAddress: string;
41
+ password: string;
42
+ };
43
+ export type SignUpResource = {
44
+ isLoading: boolean;
45
+ error: string | null;
46
+ create: (p: SignUpCreateParams) => Promise<{
47
+ status: 'complete';
48
+ }>;
49
+ };
50
+ /**
51
+ * Stateful controller for a custom sign-in form. Use when you want full
52
+ * control over the UI — otherwise drop in `<SignIn>`.
53
+ */
54
+ export declare function useSignIn(): SignInResource;
55
+ /**
56
+ * Stateful controller for a custom sign-up form.
57
+ */
58
+ export declare function useSignUp(): SignUpResource;
59
+ //# sourceMappingURL=hooks-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-flow.d.ts","sourceRoot":"","sources":["../src/hooks-flow.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iFAAiF;IACjF,MAAM,EAAE,CAAC,CAAC,EAAE,kBAAkB,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,UAAU,GAAG,oBAAoB,CAAA;KAAE,CAAC,CAAC;IAC1F,8EAA8E;IAC9E,QAAQ,EAAE,CAAC,CAAC,EAAE;QAAE,QAAQ,EAAE,YAAY,GAAG,SAAS,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACtG,sBAAsB;IACtB,UAAU,EAAE,CAAC,CAAC,EAAE,kBAAkB,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;IAC3F,2EAA2E;IAC3E,wBAAwB,EAAE,CAAC,CAAC,EAAE;QAAE,QAAQ,EAAE,cAAc,GAAG,cAAc,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;CAC9G,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,CAAC,CAAC,EAAE,kBAAkB,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;CACpE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,SAAS,IAAI,cAAc,CAsF1C;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,cAAc,CA8B1C"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Higher-level hooks that wrap the raw auth endpoints with a controlled-form
3
+ * API. Use these to build a fully custom sign-in / sign-up UI without
4
+ * reaching for fetch() every time.
5
+ */
6
+ import { useState } from 'react';
7
+ import { useAuthfyio } from './provider.js';
8
+ /**
9
+ * Stateful controller for a custom sign-in form. Use when you want full
10
+ * control over the UI — otherwise drop in `<SignIn>`.
11
+ */
12
+ export function useSignIn() {
13
+ const { client } = useAuthfyio();
14
+ const baseUrl = client.baseUrl;
15
+ const [isLoading, setLoading] = useState(false);
16
+ const [error, setError] = useState(null);
17
+ async function create(p) {
18
+ setLoading(true);
19
+ setError(null);
20
+ try {
21
+ if (p.password) {
22
+ const res = await fetch(`${baseUrl}/v1/auth/sign-in/email-password`, {
23
+ method: 'POST',
24
+ credentials: 'include',
25
+ headers: { 'content-type': 'application/json' },
26
+ body: JSON.stringify({ email: p.identifier, password: p.password }),
27
+ });
28
+ if (!res.ok) {
29
+ const body = await res.json().catch(() => ({}));
30
+ throw new Error(body?.message ?? 'invalid_credentials');
31
+ }
32
+ return { status: 'complete' };
33
+ }
34
+ throw new Error('missing_password');
35
+ }
36
+ catch (err) {
37
+ setError(err?.message ?? 'Sign-in failed.');
38
+ throw err;
39
+ }
40
+ finally {
41
+ setLoading(false);
42
+ }
43
+ }
44
+ async function sendCode(p) {
45
+ setLoading(true);
46
+ setError(null);
47
+ try {
48
+ const path = p.strategy === 'magic_link' ? '/v1/auth/sign-in/magic-link' : '/v1/auth/sign-in/sms-otp';
49
+ const body = p.strategy === 'magic_link' ? { email: p.identifier } : { phone: p.identifier };
50
+ const res = await fetch(`${baseUrl}${path}`, {
51
+ method: 'POST',
52
+ credentials: 'include',
53
+ headers: { 'content-type': 'application/json' },
54
+ body: JSON.stringify(body),
55
+ });
56
+ if (!res.ok) {
57
+ const bodyJson = await res.json().catch(() => ({}));
58
+ throw new Error(bodyJson?.message ?? 'send_code_failed');
59
+ }
60
+ return { ok: true };
61
+ }
62
+ catch (err) {
63
+ setError(err?.message ?? 'Could not send code.');
64
+ throw err;
65
+ }
66
+ finally {
67
+ setLoading(false);
68
+ }
69
+ }
70
+ async function verifyCode(p) {
71
+ setLoading(true);
72
+ setError(null);
73
+ try {
74
+ const res = await fetch(`${baseUrl}/v1/auth/sms/verify`, {
75
+ method: 'POST',
76
+ credentials: 'include',
77
+ headers: { 'content-type': 'application/json' },
78
+ body: JSON.stringify({ phone: p.phone, code: p.code }),
79
+ });
80
+ if (!res.ok) {
81
+ const body = await res.json().catch(() => ({}));
82
+ throw new Error(body?.message ?? 'invalid_code');
83
+ }
84
+ return { status: 'complete' };
85
+ }
86
+ catch (err) {
87
+ setError(err?.message ?? 'Invalid code.');
88
+ throw err;
89
+ }
90
+ finally {
91
+ setLoading(false);
92
+ }
93
+ }
94
+ function authenticateWithRedirect(p) {
95
+ const provider = p.strategy === 'oauth_google' ? 'google' : 'github';
96
+ return `${baseUrl}/v1/auth/oauth/${provider}/authorize`;
97
+ }
98
+ return { isLoading, error, create, sendCode, verifyCode, authenticateWithRedirect };
99
+ }
100
+ /**
101
+ * Stateful controller for a custom sign-up form.
102
+ */
103
+ export function useSignUp() {
104
+ const { client } = useAuthfyio();
105
+ const baseUrl = client.baseUrl;
106
+ const [isLoading, setLoading] = useState(false);
107
+ const [error, setError] = useState(null);
108
+ async function create(p) {
109
+ setLoading(true);
110
+ setError(null);
111
+ try {
112
+ const res = await fetch(`${baseUrl}/v1/auth/sign-up/email-password`, {
113
+ method: 'POST',
114
+ credentials: 'include',
115
+ headers: { 'content-type': 'application/json' },
116
+ body: JSON.stringify({ email: p.emailAddress, password: p.password }),
117
+ });
118
+ if (!res.ok) {
119
+ const body = await res.json().catch(() => ({}));
120
+ throw new Error(body?.message ?? 'sign_up_failed');
121
+ }
122
+ return { status: 'complete' };
123
+ }
124
+ catch (err) {
125
+ setError(err?.message ?? 'Sign-up failed.');
126
+ throw err;
127
+ }
128
+ finally {
129
+ setLoading(false);
130
+ }
131
+ }
132
+ return { isLoading, error, create };
133
+ }
@@ -0,0 +1,113 @@
1
+ export type HasCheck = {
2
+ /** Match by stable plan key (`plan.key`), e.g. 'pro', 'gold'. */
3
+ plan?: string;
4
+ /** Match by feature key attached to the active plan. */
5
+ feature?: string;
6
+ /** Match by org role (same as `orgRole`). */
7
+ role?: string;
8
+ /**
9
+ * Match by org permission key (e.g. 'org:sys_billing:manage' or
10
+ * 'org:read_users'). Resolved against the JWT's `org_perms` claim,
11
+ * which is populated from the user's role in the active org.
12
+ * Returns false if the user has no active org.
13
+ */
14
+ permission?: string;
15
+ };
16
+ export type AuthObject = {
17
+ userId: string | null;
18
+ sessionId: string | null;
19
+ orgId: string | null;
20
+ orgRole: string | null;
21
+ environmentId: string | null;
22
+ isSignedIn: boolean;
23
+ getToken: () => string | null;
24
+ /**
25
+ * customisable gating helper.
26
+ * `has({ plan: 'pro' })` — true when the active subscription plan's `key` matches.
27
+ * `has({ feature: 'pro_export' })` — true when the plan carries that feature.
28
+ * `has({ role: 'admin' })` — true when the session's `orgRole` matches.
29
+ * Undefined when billing data has not finished loading yet.
30
+ */
31
+ has: (check: HasCheck) => boolean | undefined;
32
+ };
33
+ export declare function useAuth(): AuthObject;
34
+ export type UserResource = {
35
+ id: string;
36
+ email: string | null;
37
+ username: string | null;
38
+ firstName: string | null;
39
+ lastName: string | null;
40
+ publicMetadata: Record<string, unknown>;
41
+ createdAt: string;
42
+ };
43
+ export type UseUserResult = {
44
+ isLoaded: false;
45
+ user: null;
46
+ } | {
47
+ isLoaded: true;
48
+ user: UserResource | null;
49
+ };
50
+ export declare function useUser(): UseUserResult;
51
+ export type OrganizationResource = {
52
+ id: string;
53
+ name: string;
54
+ slug: string;
55
+ };
56
+ export type UseOrganizationResult = {
57
+ isLoaded: false;
58
+ organization: null;
59
+ } | {
60
+ isLoaded: true;
61
+ organization: OrganizationResource | null;
62
+ };
63
+ export declare function useOrganization(): UseOrganizationResult;
64
+ export type PlanResource = {
65
+ id: string;
66
+ name: string;
67
+ description: string | null;
68
+ monthlyPriceCents: number;
69
+ annualPriceCents: number | null;
70
+ };
71
+ export declare function usePlans(): {
72
+ isLoaded: boolean;
73
+ plans: PlanResource[];
74
+ };
75
+ export type BillingMe = {
76
+ plan: {
77
+ id: string;
78
+ key: string | null;
79
+ name: string;
80
+ } | null;
81
+ features: Array<{
82
+ key: string;
83
+ name: string;
84
+ description?: string;
85
+ }>;
86
+ status: string | null;
87
+ };
88
+ export type UseBillingResult = {
89
+ isLoaded: boolean;
90
+ } & BillingMe;
91
+ /**
92
+ * Reads the caller's active plan + attached features from the instance API.
93
+ * Used by `<Protect plan="..." feature="...">`, `<Show when={...}>`, and
94
+ * `useAuth().has({...})`. Cached per-provider; the hook itself de-duplicates
95
+ * by memoizing the fetch promise.
96
+ */
97
+ export declare function useBilling(): UseBillingResult;
98
+ export type SubscriptionResource = {
99
+ id: string;
100
+ status: string;
101
+ planId: string;
102
+ currentPeriodEnd: string | null;
103
+ };
104
+ export declare function useSubscription(): {
105
+ isLoaded: boolean;
106
+ subscription: SubscriptionResource | null;
107
+ };
108
+ export type UseCheckoutResult = {
109
+ isLoading: boolean;
110
+ startCheckout: (planId: string, billingCycle?: 'monthly' | 'annual') => Promise<void>;
111
+ };
112
+ export declare function useCheckout(): UseCheckoutResult;
113
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
package/dist/hooks.js ADDED
@@ -0,0 +1,212 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { useAuthfyio, useSession, useUserId } from './provider.js';
3
+ export function useAuth() {
4
+ const s = useSession();
5
+ const billing = useBilling();
6
+ const orgPerms = s.status === 'signed_in' ? s.claims.org_perms ?? [] : [];
7
+ const base = s.status === 'signed_in'
8
+ ? {
9
+ userId: s.claims.sub ?? null,
10
+ sessionId: s.claims.sid ?? null,
11
+ orgId: s.claims.org ?? null,
12
+ orgRole: s.claims.org_role ?? null,
13
+ environmentId: s.claims.env ?? null,
14
+ isSignedIn: true,
15
+ getToken: () => s.jwt,
16
+ }
17
+ : {
18
+ userId: null,
19
+ sessionId: null,
20
+ orgId: null,
21
+ orgRole: null,
22
+ environmentId: null,
23
+ isSignedIn: false,
24
+ getToken: () => null,
25
+ };
26
+ const has = useCallback((check) => {
27
+ if (!base.isSignedIn)
28
+ return false;
29
+ if (check.role) {
30
+ if (base.orgRole !== check.role)
31
+ return false;
32
+ }
33
+ if (check.permission) {
34
+ if (!base.orgId)
35
+ return false;
36
+ if (!orgPerms.includes(check.permission))
37
+ return false;
38
+ }
39
+ if (check.plan || check.feature) {
40
+ if (!billing.isLoaded)
41
+ return undefined;
42
+ if (check.plan && billing.plan?.key !== check.plan)
43
+ return false;
44
+ if (check.feature && !billing.features.some((f) => f.key === check.feature))
45
+ return false;
46
+ }
47
+ return true;
48
+ }, [base.isSignedIn, base.orgRole, base.orgId, orgPerms, billing.isLoaded, billing.plan?.key, billing.features]);
49
+ return { ...base, has };
50
+ }
51
+ export function useUser() {
52
+ const { client } = useAuthfyio();
53
+ const userId = useUserId();
54
+ const [state, setState] = useState({ isLoaded: false, user: null });
55
+ useEffect(() => {
56
+ if (!userId) {
57
+ setState({ isLoaded: true, user: null });
58
+ return;
59
+ }
60
+ let cancelled = false;
61
+ client
62
+ .fetchCurrentUser()
63
+ .then((u) => {
64
+ if (!cancelled)
65
+ setState({ isLoaded: true, user: u });
66
+ })
67
+ .catch(() => {
68
+ if (!cancelled)
69
+ setState({ isLoaded: true, user: null });
70
+ });
71
+ return () => {
72
+ cancelled = true;
73
+ };
74
+ }, [userId, client]);
75
+ return state;
76
+ }
77
+ export function useOrganization() {
78
+ const { client } = useAuthfyio();
79
+ const auth = useAuth();
80
+ const [state, setState] = useState({ isLoaded: false, organization: null });
81
+ useEffect(() => {
82
+ if (!auth.orgId) {
83
+ setState({ isLoaded: true, organization: null });
84
+ return;
85
+ }
86
+ let cancelled = false;
87
+ client
88
+ .fetchOrganization(auth.orgId)
89
+ .then((org) => {
90
+ if (!cancelled)
91
+ setState({ isLoaded: true, organization: org });
92
+ })
93
+ .catch(() => {
94
+ if (!cancelled)
95
+ setState({ isLoaded: true, organization: null });
96
+ });
97
+ return () => {
98
+ cancelled = true;
99
+ };
100
+ }, [auth.orgId, client]);
101
+ return state;
102
+ }
103
+ export function usePlans() {
104
+ const { client } = useAuthfyio();
105
+ const [state, setState] = useState({
106
+ isLoaded: false,
107
+ plans: [],
108
+ });
109
+ useEffect(() => {
110
+ let cancelled = false;
111
+ client
112
+ .fetchPlans()
113
+ .then((plans) => {
114
+ if (!cancelled)
115
+ setState({ isLoaded: true, plans });
116
+ })
117
+ .catch(() => {
118
+ if (!cancelled)
119
+ setState({ isLoaded: true, plans: [] });
120
+ });
121
+ return () => {
122
+ cancelled = true;
123
+ };
124
+ }, [client]);
125
+ return state;
126
+ }
127
+ /**
128
+ * Reads the caller's active plan + attached features from the instance API.
129
+ * Used by `<Protect plan="..." feature="...">`, `<Show when={...}>`, and
130
+ * `useAuth().has({...})`. Cached per-provider; the hook itself de-duplicates
131
+ * by memoizing the fetch promise.
132
+ */
133
+ export function useBilling() {
134
+ const { client } = useAuthfyio();
135
+ const userId = useUserId();
136
+ const [state, setState] = useState({
137
+ isLoaded: false,
138
+ plan: null,
139
+ features: [],
140
+ status: null,
141
+ });
142
+ useEffect(() => {
143
+ if (!userId) {
144
+ setState({ isLoaded: true, plan: null, features: [], status: null });
145
+ return;
146
+ }
147
+ let cancelled = false;
148
+ client
149
+ .fetchMyBilling()
150
+ .then((b) => {
151
+ if (cancelled)
152
+ return;
153
+ if (!b) {
154
+ setState({ isLoaded: true, plan: null, features: [], status: null });
155
+ return;
156
+ }
157
+ setState({ isLoaded: true, plan: b.plan, features: b.features, status: b.status });
158
+ })
159
+ .catch(() => {
160
+ if (!cancelled)
161
+ setState({ isLoaded: true, plan: null, features: [], status: null });
162
+ });
163
+ return () => {
164
+ cancelled = true;
165
+ };
166
+ }, [userId, client]);
167
+ return state;
168
+ }
169
+ export function useSubscription() {
170
+ const { client } = useAuthfyio();
171
+ const userId = useUserId();
172
+ const [state, setState] = useState({
173
+ isLoaded: false,
174
+ subscription: null,
175
+ });
176
+ useEffect(() => {
177
+ if (!userId) {
178
+ setState({ isLoaded: true, subscription: null });
179
+ return;
180
+ }
181
+ let cancelled = false;
182
+ client
183
+ .fetchSubscription()
184
+ .then((sub) => {
185
+ if (!cancelled)
186
+ setState({ isLoaded: true, subscription: sub });
187
+ })
188
+ .catch(() => {
189
+ if (!cancelled)
190
+ setState({ isLoaded: true, subscription: null });
191
+ });
192
+ return () => {
193
+ cancelled = true;
194
+ };
195
+ }, [userId, client]);
196
+ return state;
197
+ }
198
+ export function useCheckout() {
199
+ const { client } = useAuthfyio();
200
+ const [isLoading, setIsLoading] = useState(false);
201
+ const startCheckout = useCallback(async (planId, billingCycle = 'monthly') => {
202
+ setIsLoading(true);
203
+ try {
204
+ const { url } = await client.createCheckoutSession(planId, billingCycle);
205
+ window.location.href = url;
206
+ }
207
+ finally {
208
+ setIsLoading(false);
209
+ }
210
+ }, [client]);
211
+ return { isLoading, startCheckout };
212
+ }
@@ -0,0 +1,8 @@
1
+ export * from './jwt.js';
2
+ export * from './client.js';
3
+ export * from './provider.js';
4
+ export * from './hooks.js';
5
+ export * from './components.js';
6
+ export * from './hooks-flow.js';
7
+ export * from './ui/index.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+ // Mark the entire barrel as client-side. Without this, importing any
3
+ // client component (e.g. SignedIn) from a server component eagerly
4
+ // evaluates provider.tsx's `createContext` on the server and throws
5
+ // `createContext only works in Client Components`. All public exports
6
+ // here are React hooks / components / a fetch-based client — none of
7
+ // them are usable from the server anyway, so the boundary belongs at
8
+ // the package root.
9
+ export * from './jwt.js';
10
+ export * from './client.js';
11
+ export * from './provider.js';
12
+ export * from './hooks.js';
13
+ export * from './components.js';
14
+ export * from './hooks-flow.js';
15
+ export * from './ui/index.js';
package/dist/jwt.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ export type SessionClaims = {
2
+ sid?: string;
3
+ sub?: string;
4
+ env?: string;
5
+ org?: string;
6
+ org_role?: string;
7
+ /**
8
+ * Permission keys (e.g. ['org:sys_billing:read', 'org:read_users'])
9
+ * the user holds in their active org. Absent / empty when the user
10
+ * is not in an org or the role grants no permissions. Backs
11
+ * `useAuth().has({ permission })`.
12
+ */
13
+ org_perms?: string[];
14
+ iat?: number;
15
+ exp?: number;
16
+ iss?: string;
17
+ };
18
+ export declare function decodeJwtPayload<T = unknown>(jwt: string): T;
19
+ export declare function getSessionJwtFromDocumentCookie(): string | null;
20
+ //# sourceMappingURL=jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAUF,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,CAK5D;AAED,wBAAgB,+BAA+B,IAAI,MAAM,GAAG,IAAI,CAY/D"}
package/dist/jwt.js ADDED
@@ -0,0 +1,30 @@
1
+ function base64UrlToString(input) {
2
+ const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));
3
+ const b64 = (input + pad).replace(/-/g, '+').replace(/_/g, '/');
4
+ const bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
5
+ const decoder = new TextDecoder();
6
+ return decoder.decode(bytes);
7
+ }
8
+ export function decodeJwtPayload(jwt) {
9
+ const parts = jwt.split('.');
10
+ if (parts.length < 2)
11
+ throw new Error('invalid_jwt');
12
+ const json = base64UrlToString(parts[1] ?? '');
13
+ return JSON.parse(json);
14
+ }
15
+ export function getSessionJwtFromDocumentCookie() {
16
+ if (typeof document === 'undefined')
17
+ return null;
18
+ const raw = document.cookie || '';
19
+ const parts = raw.split(';');
20
+ for (const p of parts) {
21
+ const idx = p.indexOf('=');
22
+ if (idx <= 0)
23
+ continue;
24
+ const k = p.slice(0, idx).trim();
25
+ if (k !== '__session')
26
+ continue;
27
+ return decodeURIComponent(p.slice(idx + 1).trim());
28
+ }
29
+ return null;
30
+ }
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import { AuthfyioReactClient } from './client.js';
3
+ import type { SessionClaims } from './jwt.js';
4
+ /**
5
+ * Default browser-side base URL. Resolves to the same-origin proxy mounted
6
+ * at `/api/af` (provided by `authfyio-nextjs/proxy`) so cookies set by
7
+ * the upstream API land on the customer's origin.
8
+ *
9
+ * For non-Next setups you can pass `baseUrl="https://api.authfyio.com"`
10
+ * directly, but you must arrange same-origin cookie scoping yourself.
11
+ */
12
+ export declare const DEFAULT_PROVIDER_BASE_URL = "/api/af";
13
+ export type AuthfyioProviderProps = {
14
+ /**
15
+ * Base URL the React SDK fetches against. Defaults to `/api/af` (the
16
+ * same-origin Next.js proxy provided by `authfyio-nextjs/proxy`).
17
+ * Override only if you've mounted the proxy at a different path or
18
+ * you're not using Next.
19
+ */
20
+ baseUrl?: string;
21
+ /**
22
+ * `pk_live_…` / `pk_test_…` from the dashboard. Identifies which
23
+ * environment the SDK targets. Required for the hosted SaaS — you can
24
+ * also pass it via `NEXT_PUBLIC_AUTHFYIO_PUBLISHABLE_KEY` and the
25
+ * provider will read it automatically.
26
+ */
27
+ publishableKey?: string;
28
+ autoRefresh?: boolean;
29
+ refreshSkewSeconds?: number;
30
+ children: React.ReactNode;
31
+ };
32
+ type SessionState = {
33
+ status: 'signed_out';
34
+ } | {
35
+ status: 'signed_in';
36
+ jwt: string;
37
+ claims: SessionClaims;
38
+ };
39
+ type Ctx = {
40
+ client: AuthfyioReactClient;
41
+ session: SessionState;
42
+ refresh: () => Promise<void>;
43
+ };
44
+ declare const Ctx: React.Context<Ctx | null>;
45
+ export declare function AuthfyioProvider(props: AuthfyioProviderProps): import("react/jsx-runtime").JSX.Element;
46
+ export declare function useAuthfyio(): Ctx;
47
+ export declare function useSession(): SessionState;
48
+ export declare function useUserId(): string | null;
49
+ export {};
50
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA0E,MAAM,OAAO,CAAC;AAE/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,YAAY,CAAC;AAEnD,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAEF,KAAK,YAAY,GACb;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,GACxB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC;AAEhE,KAAK,GAAG,GAAG;IACT,MAAM,EAAE,mBAAmB,CAAC;IAC5B,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAEF,QAAA,MAAM,GAAG,2BAAkC,CAAC;AAE5C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,2CAkE5D;AAED,wBAAgB,WAAW,QAI1B;AAED,wBAAgB,UAAU,iBAEzB;AAED,wBAAgB,SAAS,IAAI,MAAM,GAAG,IAAI,CAIzC"}