@outseta/react 0.2.5 → 0.3.1
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 +24 -16
- package/dist/index.js +2363 -16
- package/package.json +2 -2
- package/src/components/AuthCta.tsx +35 -0
- package/src/components/ProtectedRoute.tsx +25 -11
- package/src/components/UpgradeCta.tsx +33 -0
- package/src/components/index.ts +4 -0
- package/src/generated/outsetaAPI.schemas.ts +215 -0
- package/src/generated/public/public.ts +748 -0
- package/src/index.ts +7 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outseta/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "React hooks and components for the Outseta API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@tanstack/react-query": "^5.0.0",
|
|
22
|
-
"@outseta/api-client": "0.
|
|
22
|
+
"@outseta/api-client": "0.3.1"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"react": "^18.0.0 || ^19.0.0"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import { useOutseta } from "./OutsetaProvider.js";
|
|
3
|
+
|
|
4
|
+
export interface AuthCtaProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/** Message shown before the login/signup actions. */
|
|
6
|
+
message?: ReactNode;
|
|
7
|
+
/** Label for the signup button. */
|
|
8
|
+
signupLabel?: ReactNode;
|
|
9
|
+
/** Label for the login button. */
|
|
10
|
+
loginLabel?: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generic login/signup call-to-action for gated content.
|
|
15
|
+
*/
|
|
16
|
+
export function AuthCta({
|
|
17
|
+
message = "Sign up or log in to continue.",
|
|
18
|
+
signupLabel = "Sign up",
|
|
19
|
+
loginLabel = "Log in",
|
|
20
|
+
...props
|
|
21
|
+
}: AuthCtaProps) {
|
|
22
|
+
const { openSignup, openLogin } = useOutseta();
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div {...props}>
|
|
26
|
+
<p>{message}</p>
|
|
27
|
+
<button type="button" onClick={() => openSignup()}>
|
|
28
|
+
{signupLabel}
|
|
29
|
+
</button>{" "}
|
|
30
|
+
<button type="button" onClick={() => openLogin()}>
|
|
31
|
+
{loginLabel}
|
|
32
|
+
</button>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -1,42 +1,56 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { useOutseta } from "./OutsetaProvider.js";
|
|
3
|
+
import { AuthCta } from "./AuthCta.js";
|
|
4
|
+
import { UpgradeCta } from "./UpgradeCta.js";
|
|
3
5
|
|
|
4
6
|
export interface ProtectedRouteProps {
|
|
5
|
-
/** Plan UIDs that grant access to this route */
|
|
7
|
+
/** Plan UIDs that grant access to this route. */
|
|
6
8
|
plans: string[];
|
|
7
|
-
/** Content to render when access is granted */
|
|
9
|
+
/** Content to render when access is granted. */
|
|
8
10
|
children: ReactNode;
|
|
9
|
-
/** Rendered while checking auth status
|
|
11
|
+
/** Rendered while checking auth status. Defaults to null. */
|
|
10
12
|
loading?: ReactNode;
|
|
11
|
-
/** Rendered when user is logged in but lacks the right plan */
|
|
13
|
+
/** Rendered when user is logged in but lacks the right plan. */
|
|
12
14
|
accessDenied?: ReactNode;
|
|
13
|
-
/** Rendered when user is not logged in */
|
|
15
|
+
/** Rendered when user is not logged in. */
|
|
14
16
|
unauthenticated?: ReactNode;
|
|
17
|
+
/** Message for the default unauthenticated CTA. */
|
|
18
|
+
unauthenticatedMessage?: ReactNode;
|
|
19
|
+
/** Message for the default upgrade/manage subscription CTA. */
|
|
20
|
+
accessDeniedMessage?: ReactNode;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
/**
|
|
18
24
|
* Protects content behind plan-based access control.
|
|
19
25
|
*
|
|
20
|
-
* Checks the current user's subscription plan UID against the
|
|
21
|
-
*
|
|
26
|
+
* Checks the current user's subscription plan UID against the provided list of
|
|
27
|
+
* allowed plan UIDs. If access is denied, generic login/signup or upgrade CTAs
|
|
28
|
+
* are rendered by default and can be replaced with the `unauthenticated` and
|
|
29
|
+
* `accessDenied` props.
|
|
22
30
|
*/
|
|
23
31
|
export function ProtectedRoute({
|
|
24
32
|
plans,
|
|
25
33
|
children,
|
|
26
34
|
loading = null,
|
|
27
|
-
accessDenied
|
|
28
|
-
unauthenticated
|
|
35
|
+
accessDenied,
|
|
36
|
+
unauthenticated,
|
|
37
|
+
unauthenticatedMessage,
|
|
38
|
+
accessDeniedMessage,
|
|
29
39
|
}: ProtectedRouteProps) {
|
|
30
40
|
const { user, isLoading } = useOutseta();
|
|
31
41
|
|
|
32
42
|
if (isLoading) return <>{loading}</>;
|
|
33
43
|
|
|
34
|
-
if (!user)
|
|
44
|
+
if (!user) {
|
|
45
|
+
return <>{unauthenticated ?? <AuthCta message={unauthenticatedMessage} />}</>;
|
|
46
|
+
}
|
|
35
47
|
|
|
36
48
|
const userPlanUid = user.Account?.CurrentSubscription?.Plan?.Uid;
|
|
37
49
|
const hasAccess = userPlanUid ? plans.includes(userPlanUid) : false;
|
|
38
50
|
|
|
39
|
-
if (!hasAccess)
|
|
51
|
+
if (!hasAccess) {
|
|
52
|
+
return <>{accessDenied ?? <UpgradeCta message={accessDeniedMessage} />}</>;
|
|
53
|
+
}
|
|
40
54
|
|
|
41
55
|
return <>{children}</>;
|
|
42
56
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import { useOutseta } from "./OutsetaProvider.js";
|
|
3
|
+
import type { ProfileWidgetOptions } from "./types.js";
|
|
4
|
+
|
|
5
|
+
export interface UpgradeCtaProps extends HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
/** Message shown before the upgrade/manage subscription action. */
|
|
7
|
+
message?: ReactNode;
|
|
8
|
+
/** Label for the profile/upgrade button. */
|
|
9
|
+
buttonLabel?: ReactNode;
|
|
10
|
+
/** Options passed to the Outseta profile widget. */
|
|
11
|
+
profileOptions?: ProfileWidgetOptions;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generic upgrade/manage-subscription call-to-action for gated content.
|
|
16
|
+
*/
|
|
17
|
+
export function UpgradeCta({
|
|
18
|
+
message = "Your current plan does not include access to this content.",
|
|
19
|
+
buttonLabel = "Manage subscription",
|
|
20
|
+
profileOptions = { tab: "planChange" },
|
|
21
|
+
...props
|
|
22
|
+
}: UpgradeCtaProps) {
|
|
23
|
+
const { openProfile } = useOutseta();
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div {...props}>
|
|
27
|
+
<p>{message}</p>
|
|
28
|
+
<button type="button" onClick={() => openProfile(profileOptions)}>
|
|
29
|
+
{buttonLabel}
|
|
30
|
+
</button>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -12,6 +12,10 @@ export { ProfileEmbed } from "./ProfileEmbed.js";
|
|
|
12
12
|
export type { ProfileEmbedProps } from "./ProfileEmbed.js";
|
|
13
13
|
export { ProtectedRoute } from "./ProtectedRoute.js";
|
|
14
14
|
export type { ProtectedRouteProps } from "./ProtectedRoute.js";
|
|
15
|
+
export { AuthCta } from "./AuthCta.js";
|
|
16
|
+
export type { AuthCtaProps } from "./AuthCta.js";
|
|
17
|
+
export { UpgradeCta } from "./UpgradeCta.js";
|
|
18
|
+
export type { UpgradeCtaProps } from "./UpgradeCta.js";
|
|
15
19
|
export { PurchaseAddonButton } from "./PurchaseAddonButton.js";
|
|
16
20
|
export type { PurchaseAddonButtonProps } from "./PurchaseAddonButton.js";
|
|
17
21
|
export { LogoutButton } from "./LogoutButton.js";
|
|
@@ -2633,11 +2633,163 @@ export const EmailListSubscriberStatus = {
|
|
|
2633
2633
|
Confirmed: 4,
|
|
2634
2634
|
} as const;
|
|
2635
2635
|
|
|
2636
|
+
export interface TokenPayload {
|
|
2637
|
+
/** @nullable */
|
|
2638
|
+
access_token?: string | null;
|
|
2639
|
+
/** @nullable */
|
|
2640
|
+
authentication_callback_url?: string | null;
|
|
2641
|
+
expires_in?: number;
|
|
2642
|
+
/** @nullable */
|
|
2643
|
+
id_token?: string | null;
|
|
2644
|
+
/** @nullable */
|
|
2645
|
+
token_type?: string | null;
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
/**
|
|
2649
|
+
* Returned when a login cannot complete with a password alone because the
|
|
2650
|
+
user has two-factor authentication enabled. Carries the opaque challenge
|
|
2651
|
+
token that must be echoed back to POST /api/v1/tokens/two-factor
|
|
2652
|
+
(or the recovery / resend / switch-mechanism endpoints) along with enough
|
|
2653
|
+
metadata for a client to render the verification step without a second
|
|
2654
|
+
round-trip. The property names are snake_case to match the wire format of
|
|
2655
|
+
the other token endpoints (see TokenPayload).
|
|
2656
|
+
*/
|
|
2657
|
+
export interface TwoFactorChallengePayload {
|
|
2658
|
+
/**
|
|
2659
|
+
* Present and true on the 202 response from POST /api/v1/tokens
|
|
2660
|
+
when the user has at least one verified 2FA method. Absent on the
|
|
2661
|
+
resend / switch-mechanism responses, which only ever follow an
|
|
2662
|
+
already-issued login challenge.
|
|
2663
|
+
* @nullable
|
|
2664
|
+
*/
|
|
2665
|
+
two_factor_required?: boolean | null;
|
|
2666
|
+
/**
|
|
2667
|
+
* Present and true instead of two_factor_required
|
|
2668
|
+
when the tenant forces 2FA but the user has not yet enrolled any
|
|
2669
|
+
method. In that case only challenge_token and
|
|
2670
|
+
expires_in are populated and the client must route the
|
|
2671
|
+
user through the mid-login enrollment endpoints
|
|
2672
|
+
(/api/v1/tokens/two-factor/enroll/...) before a token can be issued.
|
|
2673
|
+
* @nullable
|
|
2674
|
+
*/
|
|
2675
|
+
two_factor_enrollment_required?: boolean | null;
|
|
2676
|
+
/**
|
|
2677
|
+
* Short-lived signed JWT (audience outseta:2fa-challenge) that
|
|
2678
|
+
identifies this challenge. Echo it back verbatim to complete the login.
|
|
2679
|
+
* @nullable
|
|
2680
|
+
*/
|
|
2681
|
+
challenge_token?: string | null;
|
|
2682
|
+
/**
|
|
2683
|
+
* The mechanism this challenge was issued against: Email or
|
|
2684
|
+
Totp (authenticator app). For Email a code has already
|
|
2685
|
+
been sent to the user; for Totp the user reads the current
|
|
2686
|
+
code from their authenticator app and nothing is sent.
|
|
2687
|
+
* @nullable
|
|
2688
|
+
*/
|
|
2689
|
+
mechanism?: string | null;
|
|
2690
|
+
/**
|
|
2691
|
+
* A masked view of where an emailed code was sent (e.g. b***@outseta.com),
|
|
2692
|
+
suitable for display. Empty when mechanism is Totp.
|
|
2693
|
+
* @nullable
|
|
2694
|
+
*/
|
|
2695
|
+
masked_destination?: string | null;
|
|
2696
|
+
/** Seconds until the challenge expires (600). After this the
|
|
2697
|
+
challenge_token can no longer be verified and the login must restart. */
|
|
2698
|
+
expires_in?: number;
|
|
2699
|
+
/**
|
|
2700
|
+
* Every verified mechanism enrolled for this user (excluding recovery
|
|
2701
|
+
codes), e.g. ["Totp", "Email"]. A client can offer a
|
|
2702
|
+
"use a different method" option for any value other than the current
|
|
2703
|
+
mechanism via POST /api/v1/tokens/two-factor/switch-mechanism.
|
|
2704
|
+
* @nullable
|
|
2705
|
+
*/
|
|
2706
|
+
available_mechanisms?: string[] | null;
|
|
2707
|
+
/**
|
|
2708
|
+
* true when the user has a batch of recovery codes on file, in
|
|
2709
|
+
which case POST /api/v1/tokens/two-factor/recovery can be used
|
|
2710
|
+
as a fallback if they cannot produce a primary code.
|
|
2711
|
+
* @nullable
|
|
2712
|
+
*/
|
|
2713
|
+
recovery_codes_available?: boolean | null;
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2636
2716
|
export interface PasswordPayload {
|
|
2637
2717
|
/** @nullable */
|
|
2638
2718
|
Password?: string | null;
|
|
2639
2719
|
}
|
|
2640
2720
|
|
|
2721
|
+
/**
|
|
2722
|
+
* Returned from POST /api/v1/tokens/two-factor/enroll/totp/begin when a
|
|
2723
|
+
user forced into 2FA chooses to enroll an authenticator app. Carries the
|
|
2724
|
+
shared secret in the three forms a client may need to present it, plus the
|
|
2725
|
+
challenge token the user's first generated code is confirmed against. The
|
|
2726
|
+
property names are snake_case to match the wire format of the other token
|
|
2727
|
+
endpoints (see TokenPayload).
|
|
2728
|
+
*/
|
|
2729
|
+
export interface TwoFactorTotpEnrollmentPayload {
|
|
2730
|
+
/**
|
|
2731
|
+
* Short-lived signed JWT identifying the enrollment-test challenge.
|
|
2732
|
+
Echo it back to POST /api/v1/tokens/two-factor/enroll/totp/confirm
|
|
2733
|
+
along with the first code from the authenticator app.
|
|
2734
|
+
* @nullable
|
|
2735
|
+
*/
|
|
2736
|
+
challenge_token?: string | null;
|
|
2737
|
+
/**
|
|
2738
|
+
* The Base32-encoded shared secret. Offer this for manual entry by users
|
|
2739
|
+
who cannot scan the QR code.
|
|
2740
|
+
* @nullable
|
|
2741
|
+
*/
|
|
2742
|
+
secret?: string | null;
|
|
2743
|
+
/**
|
|
2744
|
+
* The full otpauth://totp/... URI encoding the secret, issuer and
|
|
2745
|
+
parameters. Most authenticator apps add an account directly from this.
|
|
2746
|
+
* @nullable
|
|
2747
|
+
*/
|
|
2748
|
+
otpauth_uri?: string | null;
|
|
2749
|
+
/**
|
|
2750
|
+
* A QR code rendering of otpauth_uri as a PNG, Base64
|
|
2751
|
+
encoded — render it as an <img> for the user to scan.
|
|
2752
|
+
* @nullable
|
|
2753
|
+
*/
|
|
2754
|
+
qr_code_png_base64?: string | null;
|
|
2755
|
+
/** Seconds until the enrollment-test challenge expires (600). */
|
|
2756
|
+
expires_in?: number;
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
/**
|
|
2760
|
+
* Returned from the mid-login enrollment confirm endpoints
|
|
2761
|
+
(POST /api/v1/tokens/two-factor/enroll/{email|totp}/confirm) once the
|
|
2762
|
+
user has proven control of the new mechanism. Completes a forced-enrollment
|
|
2763
|
+
login: it both finalizes the JWT access token and surfaces the freshly
|
|
2764
|
+
generated recovery codes. The property names are snake_case to match the
|
|
2765
|
+
wire format of the other token endpoints (see TokenPayload).
|
|
2766
|
+
*/
|
|
2767
|
+
export interface TwoFactorEnrollmentConfirmationPayload {
|
|
2768
|
+
/** Always true on a success response; the enrollment is now active. */
|
|
2769
|
+
confirmed?: boolean;
|
|
2770
|
+
/**
|
|
2771
|
+
* The user's recovery codes, generated as part of first-time enrollment.
|
|
2772
|
+
These are shown once and never returned again — prompt the user
|
|
2773
|
+
to store them somewhere safe. Each code is single-use at
|
|
2774
|
+
POST /api/v1/tokens/two-factor/recovery.
|
|
2775
|
+
* @nullable
|
|
2776
|
+
*/
|
|
2777
|
+
recovery_codes?: string[] | null;
|
|
2778
|
+
/**
|
|
2779
|
+
* The JWT access token. Use it as Authorization: bearer {access_token}
|
|
2780
|
+
for subsequent API calls — login is complete.
|
|
2781
|
+
* @nullable
|
|
2782
|
+
*/
|
|
2783
|
+
access_token?: string | null;
|
|
2784
|
+
/**
|
|
2785
|
+
* Always Bearer.
|
|
2786
|
+
* @nullable
|
|
2787
|
+
*/
|
|
2788
|
+
token_type?: string | null;
|
|
2789
|
+
/** Seconds until the access token expires. */
|
|
2790
|
+
expires_in?: number;
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2641
2793
|
/**
|
|
2642
2794
|
* @nullable
|
|
2643
2795
|
*/
|
|
@@ -5658,6 +5810,69 @@ export interface PersonSegmentRemovedWebhookPayload {
|
|
|
5658
5810
|
ActivityEventData?: PersonSegmentRemovedActivityData;
|
|
5659
5811
|
}
|
|
5660
5812
|
|
|
5813
|
+
export type AuthGetTokenParams = {
|
|
5814
|
+
/**
|
|
5815
|
+
* @nullable
|
|
5816
|
+
*/
|
|
5817
|
+
data: unknown | null;
|
|
5818
|
+
};
|
|
5819
|
+
|
|
5820
|
+
export type AuthVerifyTwoFactorTokenParams = {
|
|
5821
|
+
/**
|
|
5822
|
+
* @nullable
|
|
5823
|
+
*/
|
|
5824
|
+
data: unknown | null;
|
|
5825
|
+
};
|
|
5826
|
+
|
|
5827
|
+
export type AuthResendTwoFactorParams = {
|
|
5828
|
+
/**
|
|
5829
|
+
* @nullable
|
|
5830
|
+
*/
|
|
5831
|
+
data: unknown | null;
|
|
5832
|
+
};
|
|
5833
|
+
|
|
5834
|
+
export type AuthSwitchTwoFactorMechanismParams = {
|
|
5835
|
+
/**
|
|
5836
|
+
* @nullable
|
|
5837
|
+
*/
|
|
5838
|
+
data: unknown | null;
|
|
5839
|
+
};
|
|
5840
|
+
|
|
5841
|
+
export type AuthVerifyTwoFactorRecoveryParams = {
|
|
5842
|
+
/**
|
|
5843
|
+
* @nullable
|
|
5844
|
+
*/
|
|
5845
|
+
data: unknown | null;
|
|
5846
|
+
};
|
|
5847
|
+
|
|
5848
|
+
export type TokenTwoFactorEnrollmentBeginEmailParams = {
|
|
5849
|
+
/**
|
|
5850
|
+
* @nullable
|
|
5851
|
+
*/
|
|
5852
|
+
data: unknown | null;
|
|
5853
|
+
};
|
|
5854
|
+
|
|
5855
|
+
export type TokenTwoFactorEnrollmentBeginTotpParams = {
|
|
5856
|
+
/**
|
|
5857
|
+
* @nullable
|
|
5858
|
+
*/
|
|
5859
|
+
data: unknown | null;
|
|
5860
|
+
};
|
|
5861
|
+
|
|
5862
|
+
export type TokenTwoFactorEnrollmentConfirmEmailParams = {
|
|
5863
|
+
/**
|
|
5864
|
+
* @nullable
|
|
5865
|
+
*/
|
|
5866
|
+
data: unknown | null;
|
|
5867
|
+
};
|
|
5868
|
+
|
|
5869
|
+
export type TokenTwoFactorEnrollmentConfirmTotpParams = {
|
|
5870
|
+
/**
|
|
5871
|
+
* @nullable
|
|
5872
|
+
*/
|
|
5873
|
+
data: unknown | null;
|
|
5874
|
+
};
|
|
5875
|
+
|
|
5661
5876
|
export type CaseGetAllCasesParams = {
|
|
5662
5877
|
/**
|
|
5663
5878
|
* Search string
|