import { IdentityProvider, StytchLogin, useStytch, useStytchUser } from "@stytch/react";
import {
OAuthProviders,
OTPMethods,
Products,
type StytchEvent,
type StytchLoginConfig,
} from "@stytch/vanilla-js";
import { useEffect, useMemo } from "react";
/**
* A higher-order component that enforces a login requirement for the wrapped component.
* If the user is not logged in, the user is redirected to the login page and the
* current URL is stored in localStorage to enable return after authentication.
*/
export const withLoginRequired = (Component: React.FC) => () => {
const { user, fromCache } = useStytchUser();
useEffect(() => {
if (!user && !fromCache) {
localStorage.setItem("returnTo", window.location.href);
window.location.href = "/login";
}
}, [user, fromCache]);
if (!user) {
return null;
}
return <Component />;
};
/**
* The other half of the withLoginRequired flow
* Redirects the user to a specified URL stored in local storage or a default location.
* Behavior:
* - Checks for a `returnTo` entry in local storage to determine the redirection target.
* - If `returnTo` exists, clears its value from local storage and navigates to the specified URL.
* - If `returnTo` does not exist, redirects the user to the default '/todoapp' location.
*/
const onLoginComplete = () => {
const returnTo = localStorage.getItem("returnTo");
if (returnTo) {
localStorage.setItem("returnTo", "");
window.location.href = returnTo;
} else {
window.location.href = "/todoapp";
}
};
/**
* The Login page implementation. Wraps the StytchLogin UI component.
* View all configuration options at https://stytch.com/docs/sdks/ui-configuration
*/
export function Login() {
const loginConfig = useMemo<StytchLoginConfig>(
() => ({
oauthOptions: {
loginRedirectURL: `${window.location.origin}/authenticate`,
providers: [{ type: OAuthProviders.Google }],
signupRedirectURL: `${window.location.origin}/authenticate`,
},
otpOptions: {
expirationMinutes: 10,
methods: [OTPMethods.Email],
},
products: [Products.otp, Products.oauth],
}),
[],
);
const handleOnLoginComplete = (evt: StytchEvent) => {
if (evt.type !== "AUTHENTICATE_FLOW_COMPLETE") return;
onLoginComplete();
};
return <StytchLogin config={loginConfig} callbacks={{ onEvent: handleOnLoginComplete }} />;
}
/**
* The OAuth Authorization page implementation. Wraps the Stytch IdentityProvider UI component.
* View all configuration options at https://stytch.com/docs/sdks/idp-ui-configuration
*/
export const Authorize = withLoginRequired(() => <IdentityProvider />);
/**
* The Authentication callback page implementation. Handles completing the login flow after OAuth
*/
export function Authenticate() {
const client = useStytch();
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const token = params.get("token");
if (!token) return;
client.oauth.authenticate(token, { session_duration_minutes: 60 }).then(onLoginComplete);
}, [client]);
return "Loading...";
}
export const Logout = () => {
const stytch = useStytch();
const { user } = useStytchUser();
if (!user) return null;
return (
<button type="button" className="primary" onClick={() => stytch.session.revoke()}>
{" "}
Log Out{" "}
</button>
);
};