@crossmint/client-sdk-react-ui 1.3.24 → 1.4.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.
@@ -1,15 +1,15 @@
1
+ import { REFRESH_TOKEN_PREFIX, SESSION_PREFIX, deleteCookie, getCookie, setCookie } from "@/utils/authCookies";
1
2
  import { type ReactNode, createContext, useEffect, useState } from "react";
2
3
  import { createPortal } from "react-dom";
3
4
 
5
+ import { CrossmintAuthService } from "@crossmint/client-sdk-auth-core/client";
4
6
  import type { EVMSmartWalletChain } from "@crossmint/client-sdk-smart-wallet";
5
7
  import { type UIConfig, validateApiKeyAndGetCrossmintBaseUrl } from "@crossmint/common-sdk-base";
6
8
 
7
9
  import AuthModal from "../components/auth/AuthModal";
8
- import { useCrossmint, useWallet } from "../hooks";
10
+ import { type AuthMaterial, useCrossmint, useRefreshToken, useWallet } from "../hooks";
9
11
  import { CrossmintWalletProvider } from "./CrossmintWalletProvider";
10
12
 
11
- const SESSION_PREFIX = "crossmint-session";
12
-
13
13
  export type CrossmintAuthWalletConfig = {
14
14
  defaultChain: EVMSmartWalletChain;
15
15
  createOnLogin: "all-users" | "off";
@@ -28,6 +28,7 @@ type AuthContextType = {
28
28
  login: () => void;
29
29
  logout: () => void;
30
30
  jwt?: string;
31
+ refreshToken?: string;
31
32
  status: AuthStatus;
32
33
  };
33
34
 
@@ -38,16 +39,28 @@ export const AuthContext = createContext<AuthContextType>({
38
39
  });
39
40
 
40
41
  export function CrossmintAuthProvider({ embeddedWallets, children, appearance }: CrossmintAuthProviderProps) {
41
- const { crossmint, setJwt } = useCrossmint("CrossmintAuthProvider must be used within CrossmintProvider");
42
+ const { crossmint, setJwt, setRefreshToken } = useCrossmint(
43
+ "CrossmintAuthProvider must be used within CrossmintProvider"
44
+ );
45
+ const crossmintAuthService = new CrossmintAuthService(crossmint.apiKey);
42
46
  const crossmintBaseUrl = validateApiKeyAndGetCrossmintBaseUrl(crossmint.apiKey);
43
47
  const [modalOpen, setModalOpen] = useState(false);
44
48
 
45
- useEffect(() => {
46
- const session = sessionFromClient();
47
- if (session != null) {
48
- setJwt(session);
49
- }
50
- }, []);
49
+ const setAuthMaterial = (authMaterial: AuthMaterial) => {
50
+ setCookie(SESSION_PREFIX, authMaterial.jwtToken);
51
+ setCookie(REFRESH_TOKEN_PREFIX, authMaterial.refreshToken.secret, authMaterial.refreshToken.expiresAt);
52
+ setJwt(authMaterial.jwtToken);
53
+ setRefreshToken(authMaterial.refreshToken.secret);
54
+ };
55
+
56
+ const logout = () => {
57
+ deleteCookie(SESSION_PREFIX);
58
+ deleteCookie(REFRESH_TOKEN_PREFIX);
59
+ setJwt(undefined);
60
+ setRefreshToken(undefined);
61
+ };
62
+
63
+ useRefreshToken({ crossmintAuthService, setAuthMaterial, logout });
51
64
 
52
65
  const login = () => {
53
66
  if (crossmint.jwt != null) {
@@ -58,10 +71,12 @@ export function CrossmintAuthProvider({ embeddedWallets, children, appearance }:
58
71
  setModalOpen(true);
59
72
  };
60
73
 
61
- const logout = () => {
62
- document.cookie = `${SESSION_PREFIX}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
63
- setJwt(undefined);
64
- };
74
+ useEffect(() => {
75
+ if (crossmint.jwt == null) {
76
+ const jwt = getCookie(SESSION_PREFIX);
77
+ setJwt(jwt);
78
+ }
79
+ }, []);
65
80
 
66
81
  useEffect(() => {
67
82
  if (crossmint.jwt == null) {
@@ -71,12 +86,6 @@ export function CrossmintAuthProvider({ embeddedWallets, children, appearance }:
71
86
  setModalOpen(false);
72
87
  }, [crossmint.jwt]);
73
88
 
74
- useEffect(() => {
75
- if (crossmint.jwt) {
76
- document.cookie = `${SESSION_PREFIX}=${crossmint.jwt}; path=/;SameSite=Lax;`;
77
- }
78
- }, [crossmint.jwt]);
79
-
80
89
  const getAuthStatus = (): AuthStatus => {
81
90
  if (crossmint.jwt != null) {
82
91
  return "logged-in";
@@ -93,6 +102,7 @@ export function CrossmintAuthProvider({ embeddedWallets, children, appearance }:
93
102
  login,
94
103
  logout,
95
104
  jwt: crossmint.jwt,
105
+ refreshToken: crossmint.refreshToken,
96
106
  status: getAuthStatus(),
97
107
  }}
98
108
  >
@@ -105,7 +115,7 @@ export function CrossmintAuthProvider({ embeddedWallets, children, appearance }:
105
115
  <AuthModal
106
116
  baseUrl={crossmintBaseUrl}
107
117
  setModalOpen={setModalOpen}
108
- setJwtToken={setJwt}
118
+ setAuthMaterial={setAuthMaterial}
109
119
  apiKey={crossmint.apiKey}
110
120
  appearance={appearance}
111
121
  />,
@@ -144,8 +154,3 @@ function WalletManager({
144
154
 
145
155
  return <>{children}</>;
146
156
  }
147
-
148
- function sessionFromClient(): string | undefined {
149
- const crossmintSession = document.cookie.split("; ").find((row) => row.startsWith(SESSION_PREFIX));
150
- return crossmintSession ? crossmintSession.split("=")[1] : undefined;
151
- }
@@ -0,0 +1,41 @@
1
+ import { beforeEach, describe, expect, test } from "vitest";
2
+
3
+ import { waitForSettledState } from "../testUtils";
4
+ import { deleteCookie, getCookie, setCookie } from "./authCookies";
5
+
6
+ describe("authCookies", () => {
7
+ beforeEach(() => {
8
+ // Clear all cookies before each test
9
+ document.cookie.split(";").forEach((cookie) => {
10
+ document.cookie = cookie
11
+ .replace(/^ +/, "")
12
+ .replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/");
13
+ });
14
+ });
15
+
16
+ test("should return undefined for non-existent cookie", () => {
17
+ expect(getCookie("non-existent")).toBeUndefined();
18
+ });
19
+
20
+ test("should return the correct value for an existing cookie", async () => {
21
+ document.cookie = "test-cookie=test-value";
22
+ await waitForSettledState(() => {
23
+ expect(getCookie("test-cookie")).toBe("test-value");
24
+ });
25
+ });
26
+
27
+ test("should set a cookie without expiration", async () => {
28
+ setCookie("test-cookie", "test-value");
29
+ await waitForSettledState(() => {
30
+ expect(document.cookie).toContain("test-cookie=test-value");
31
+ });
32
+ });
33
+
34
+ test("should delete an existing cookie", async () => {
35
+ document.cookie = "test-cookie=test-value";
36
+ deleteCookie("test-cookie");
37
+ await waitForSettledState(() => {
38
+ expect(document.cookie).not.toContain("test-cookie");
39
+ });
40
+ });
41
+ });
@@ -0,0 +1,16 @@
1
+ export const SESSION_PREFIX = "crossmint-session";
2
+ export const REFRESH_TOKEN_PREFIX = "crossmint-refresh-token";
3
+
4
+ export function getCookie(name: string): string | undefined {
5
+ const crossmintRefreshToken = document.cookie.split("; ").find((row) => row.startsWith(name));
6
+ return crossmintRefreshToken ? crossmintRefreshToken.split("=")[1] : undefined;
7
+ }
8
+
9
+ export function setCookie(name: string, value: string, expiresAt?: string) {
10
+ const expiresInUtc = expiresAt ? new Date(expiresAt).toUTCString() : "";
11
+ document.cookie = `${name}=${value}; ${expiresAt ? `expires=${expiresInUtc};` : ""} path=/; SameSite=Lax;`;
12
+ }
13
+
14
+ export function deleteCookie(name: string) {
15
+ document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
16
+ }