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.
- package/README.md +83 -0
- package/dist/client.d.ts +128 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +227 -0
- package/dist/components.d.ts +79 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +111 -0
- package/dist/hooks-flow.d.ts +59 -0
- package/dist/hooks-flow.d.ts.map +1 -0
- package/dist/hooks-flow.js +133 -0
- package/dist/hooks.d.ts +113 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +212 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/jwt.d.ts +20 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +30 -0
- package/dist/provider.d.ts +50 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +91 -0
- package/dist/ui/CheckoutButton.d.ts +14 -0
- package/dist/ui/CheckoutButton.d.ts.map +1 -0
- package/dist/ui/CheckoutButton.js +28 -0
- package/dist/ui/Control.d.ts +17 -0
- package/dist/ui/Control.d.ts.map +1 -0
- package/dist/ui/Control.js +33 -0
- package/dist/ui/CreateOrganization.d.ts +19 -0
- package/dist/ui/CreateOrganization.d.ts.map +1 -0
- package/dist/ui/CreateOrganization.js +75 -0
- package/dist/ui/OrganizationList.d.ts +13 -0
- package/dist/ui/OrganizationList.d.ts.map +1 -0
- package/dist/ui/OrganizationList.js +47 -0
- package/dist/ui/OrganizationProfile.d.ts +12 -0
- package/dist/ui/OrganizationProfile.d.ts.map +1 -0
- package/dist/ui/OrganizationProfile.js +116 -0
- package/dist/ui/OrganizationSwitcher.d.ts +17 -0
- package/dist/ui/OrganizationSwitcher.d.ts.map +1 -0
- package/dist/ui/OrganizationSwitcher.js +59 -0
- package/dist/ui/PricingTable.d.ts +15 -0
- package/dist/ui/PricingTable.d.ts.map +1 -0
- package/dist/ui/PricingTable.js +74 -0
- package/dist/ui/SignIn.d.ts +23 -0
- package/dist/ui/SignIn.d.ts.map +1 -0
- package/dist/ui/SignIn.js +489 -0
- package/dist/ui/SignUp.d.ts +18 -0
- package/dist/ui/SignUp.d.ts.map +1 -0
- package/dist/ui/SignUp.js +153 -0
- package/dist/ui/UnstyledButtons.d.ts +24 -0
- package/dist/ui/UnstyledButtons.d.ts.map +1 -0
- package/dist/ui/UnstyledButtons.js +42 -0
- package/dist/ui/UserAvatar.d.ts +14 -0
- package/dist/ui/UserAvatar.d.ts.map +1 -0
- package/dist/ui/UserAvatar.js +52 -0
- package/dist/ui/UserButton.d.ts +15 -0
- package/dist/ui/UserButton.d.ts.map +1 -0
- package/dist/ui/UserButton.js +82 -0
- package/dist/ui/UserProfile.d.ts +19 -0
- package/dist/ui/UserProfile.d.ts.map +1 -0
- package/dist/ui/UserProfile.js +199 -0
- package/dist/ui/index.d.ts +14 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +13 -0
- package/dist/ui/styles.d.ts +10 -0
- package/dist/ui/styles.d.ts.map +1 -0
- package/dist/ui/styles.js +291 -0
- 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
|
+
}
|
package/dist/hooks.d.ts
ADDED
|
@@ -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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|