@crossmint/client-sdk-react-ui 1.5.0 → 1.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossmint/client-sdk-react-ui",
3
- "version": "1.5.0",
3
+ "version": "1.6.1",
4
4
  "repository": "https://github.com/Crossmint/crossmint-sdk",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Paella Labs Inc",
@@ -31,11 +31,12 @@
31
31
  "tailwind-merge": "2.4.0",
32
32
  "tailwindcss": "3.4.10",
33
33
  "zod": "3.22.4",
34
- "@crossmint/client-sdk-auth-core": "1.3.0",
34
+ "@crossmint/client-sdk-auth": "0.2.0",
35
+ "@crossmint/client-sdk-base": "1.2.8",
35
36
  "@crossmint/client-sdk-smart-wallet": "0.1.19",
36
37
  "@crossmint/client-sdk-window": "0.0.10",
37
38
  "@crossmint/common-sdk-base": "0.2.0",
38
- "@crossmint/client-sdk-base": "1.2.8"
39
+ "@crossmint/common-sdk-auth": "0.2.0"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@types/lodash.isequal": "4.5.6",
@@ -1,13 +1,12 @@
1
1
  import { Dialog, Transition } from "@headlessui/react";
2
- import { type CSSProperties, Fragment, useEffect, useRef, useState } from "react";
2
+ import { type CSSProperties, Fragment, useRef } from "react";
3
3
  import { z } from "zod";
4
4
 
5
5
  import { IFrameWindow } from "@crossmint/client-sdk-window";
6
6
  import type { UIConfig } from "@crossmint/common-sdk-base";
7
+ import type { AuthMaterial } from "@crossmint/common-sdk-auth";
7
8
 
8
9
  import X from "../../icons/x";
9
- import { classNames } from "../../utils/classNames";
10
- import type { AuthMaterial } from "@/hooks/useRefreshToken";
11
10
 
12
11
  const authMaterialSchema = z.object({
13
12
  oneTimeSecret: z.string(),
@@ -37,27 +36,19 @@ export default function AuthModal({ setModalOpen, apiKey, fetchAuthMaterial, bas
37
36
  }
38
37
 
39
38
  const iframeRef = useRef<HTMLIFrameElement | null>(null);
40
- const [iframe, setIframe] = useState<IFrameWindow<IncomingModalIframeEventsType, Record<string, never>> | null>(
41
- null
42
- );
39
+ const iframeWindowRef = useRef<IFrameWindow<IncomingModalIframeEventsType, Record<string, never>> | null>(null);
43
40
 
44
- useEffect(() => {
45
- if (iframe == null) {
41
+ const setupIframeWindowListener = () => {
42
+ if (iframeWindowRef.current == null) {
46
43
  return;
47
44
  }
48
45
 
49
- iframe.on("authMaterialFromAuthFrame", (data) => {
46
+ iframeWindowRef.current.on("authMaterialFromAuthFrame", (data) => {
50
47
  fetchAuthMaterial(data.oneTimeSecret);
51
- iframe.off("authMaterialFromAuthFrame");
48
+ iframeWindowRef.current?.off("authMaterialFromAuthFrame");
52
49
  setModalOpen(false);
53
50
  });
54
-
55
- return () => {
56
- if (iframe) {
57
- iframe.off("authMaterialFromAuthFrame");
58
- }
59
- };
60
- }, [iframe, fetchAuthMaterial, setModalOpen]);
51
+ };
61
52
 
62
53
  const handleIframeLoaded = async () => {
63
54
  if (iframeRef.current == null) {
@@ -66,11 +57,15 @@ export default function AuthModal({ setModalOpen, apiKey, fetchAuthMaterial, bas
66
57
  return;
67
58
  }
68
59
 
69
- const initIframe = await IFrameWindow.init(iframeRef.current, {
70
- incomingEvents: incomingModalIframeEvents,
71
- outgoingEvents: {},
72
- });
73
- setIframe(initIframe);
60
+ if (iframeWindowRef.current == null) {
61
+ const initIframe = await IFrameWindow.init(iframeRef.current, {
62
+ incomingEvents: incomingModalIframeEvents,
63
+ outgoingEvents: {},
64
+ });
65
+
66
+ iframeWindowRef.current = initIframe;
67
+ setupIframeWindowListener();
68
+ }
74
69
  };
75
70
 
76
71
  return (
@@ -121,14 +116,16 @@ export default function AuthModal({ setModalOpen, apiKey, fetchAuthMaterial, bas
121
116
  src={iframeSrc}
122
117
  onLoad={handleIframeLoaded}
123
118
  title="Authentication Modal"
124
- className={classNames(
125
- "w-full h-[500px] border pt-12 pb-8",
126
- appearance?.colors?.border
127
- ? `border-[${appearance.colors.border}]`
128
- : "border-[#D0D5DD]",
129
- appearance?.borderRadius ? `rounded-[${appearance.borderRadius}]` : "rounded-2xl",
130
- appearance?.colors?.background ? `bg-[${appearance.colors.background}]` : "bg-white"
131
- )}
119
+ style={{
120
+ width: "100%",
121
+ height: "500px",
122
+ border: "1px solid",
123
+ borderColor: appearance?.colors?.border ?? "#D0D5DD",
124
+ borderRadius: appearance?.borderRadius ?? "1rem",
125
+ backgroundColor: appearance?.colors?.background ?? "white",
126
+ paddingTop: "3rem",
127
+ paddingBottom: "2rem",
128
+ }}
132
129
  />
133
130
  </div>
134
131
  </Transition.Child>
@@ -39,11 +39,19 @@ function PasskeyPromptCore({ title, content, primaryButton, secondaryAction, app
39
39
  />
40
40
  </Transition.Child>
41
41
  <div
42
- className={classNames(
43
- "flex flex-col items-center w-full max-w-[28rem] rounded-2xl shadow-sm z-30 border",
44
- appearance?.colors?.background != null ? `bg-[${appearance.colors.background}]` : "bg-white",
45
- appearance?.colors?.border != null ? `border-[${appearance.colors.border}]` : "border-[#D0D5DD]"
46
- )}
42
+ style={{
43
+ display: "flex",
44
+ flexDirection: "column",
45
+ alignItems: "center",
46
+ width: "100%",
47
+ maxWidth: "28rem",
48
+ borderRadius: "1rem",
49
+ boxShadow: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
50
+ zIndex: 30,
51
+ border: "1px solid",
52
+ backgroundColor: appearance?.colors?.background || "white",
53
+ borderColor: appearance?.colors?.border || "#D0D5DD",
54
+ }}
47
55
  onClick={(e) => e.stopPropagation()}
48
56
  >
49
57
  <div className="pt-12 pb-10 px-8">
@@ -52,24 +60,22 @@ function PasskeyPromptCore({ title, content, primaryButton, secondaryAction, app
52
60
  </div>
53
61
  <div className="flex justify-center">
54
62
  <p
55
- className={classNames(
56
- "text-lg font-bold",
57
- appearance?.colors?.textPrimary != null
58
- ? `text-[${appearance.colors.textPrimary}]`
59
- : "text-[#20343E]"
60
- )}
63
+ style={{
64
+ fontSize: "1.125rem",
65
+ lineHeight: "1.75rem",
66
+ fontWeight: "bold",
67
+ color: appearance?.colors?.textPrimary || "#20343E",
68
+ }}
61
69
  >
62
70
  {title}
63
71
  </p>
64
72
  </div>
65
73
  <div className="mt-4 mb-9">
66
74
  <div
67
- className={classNames(
68
- "font-normal",
69
- appearance?.colors?.textSecondary != null
70
- ? `text-[${appearance.colors.textSecondary}]`
71
- : "text-[#67797F]"
72
- )}
75
+ style={{
76
+ fontWeight: "normal",
77
+ color: appearance?.colors?.textSecondary || "#67797F",
78
+ }}
73
79
  >
74
80
  {content}
75
81
  </div>
@@ -199,15 +205,16 @@ export function PasskeyPrompt({ state, appearance }: PasskeyPromptProps) {
199
205
  href="https://docs.crossmint.com/wallets/smart-wallets/users/troubleshoot"
200
206
  rel="noopener noreferrer"
201
207
  target="_blank"
202
- className={classNames(
203
- "p-3.5 w-full text-center no-underline rounded-lg font-bold",
204
- appearance?.colors?.inputBackground != null
205
- ? `bg-[${appearance.colors.inputBackground}]`
206
- : "bg-[#F0F2F4]",
207
- appearance?.colors?.textSecondary != null
208
- ? `text-[${appearance.colors.textSecondary}]`
209
- : "text-[#00150D]"
210
- )}
208
+ style={{
209
+ padding: "0.875rem",
210
+ width: "100%",
211
+ textAlign: "center",
212
+ textDecoration: "none",
213
+ borderRadius: "0.5rem",
214
+ fontWeight: "bold",
215
+ backgroundColor: appearance?.colors?.inputBackground || "#F0F2F4",
216
+ color: appearance?.colors?.textSecondary || "#00150D",
217
+ }}
211
218
  >
212
219
  Troubleshoot
213
220
  </a>
@@ -1,13 +1,16 @@
1
1
  import { PoweredByLeaf } from "@/icons/poweredByLeaf";
2
- import { classNames } from "@/utils/classNames";
3
2
 
4
3
  export function PoweredByCrossmint({ color }: { color?: string }) {
5
4
  return (
6
5
  <p
7
- className={classNames(
8
- "flex text-xs font-normal -tracking-[0.2px] p-2",
9
- color ? `text-[${color}]` : "text-[#67797F]"
10
- )}
6
+ style={{
7
+ display: "flex",
8
+ fontSize: "0.75rem",
9
+ fontWeight: "400",
10
+ letterSpacing: "-0.2px",
11
+ padding: "0.5rem",
12
+ color: color || "#67797F",
13
+ }}
11
14
  >
12
15
  Powered by
13
16
  <span className="flex self-center pl-1 gap-1 items-center font-semibold">
@@ -1,13 +1,13 @@
1
1
  import { act, renderHook } from "@testing-library/react";
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
3
 
4
- import { type CrossmintAuthService, getJWTExpiration } from "@crossmint/client-sdk-auth-core/client";
4
+ import { type CrossmintAuthService, getJWTExpiration } from "@crossmint/client-sdk-auth";
5
5
  import { queueTask } from "@crossmint/client-sdk-base";
6
6
 
7
7
  import * as authCookies from "../utils/authCookies";
8
8
  import { type AuthMaterial, useRefreshToken } from "./useRefreshToken";
9
9
 
10
- vi.mock("@crossmint/client-sdk-auth-core", () => ({
10
+ vi.mock("@crossmint/client-sdk-auth", () => ({
11
11
  CrossmintAuthService: vi.fn(),
12
12
  getJWTExpiration: vi.fn(),
13
13
  }));
@@ -1,7 +1,8 @@
1
1
  import { useCallback, useEffect, useRef } from "react";
2
2
 
3
- import type { CrossmintAuthService } from "@crossmint/client-sdk-auth-core/client";
4
- import { getJWTExpiration } from "@crossmint/client-sdk-auth-core/client";
3
+ import type { AuthMaterial } from "@crossmint/common-sdk-auth";
4
+ import type { CrossmintAuthService } from "@crossmint/client-sdk-auth";
5
+ import { getJWTExpiration } from "@crossmint/client-sdk-auth";
5
6
  import { queueTask, type CancellableTask } from "@crossmint/client-sdk-base";
6
7
 
7
8
  import { REFRESH_TOKEN_PREFIX, getCookie } from "../utils/authCookies";
@@ -9,14 +10,6 @@ import { REFRESH_TOKEN_PREFIX, getCookie } from "../utils/authCookies";
9
10
  // 2 minutes before jwt expiration
10
11
  const TIME_BEFORE_EXPIRING_JWT_IN_SECONDS = 120;
11
12
 
12
- export type AuthMaterial = {
13
- jwtToken: string;
14
- refreshToken: {
15
- secret: string;
16
- expiresAt: string;
17
- };
18
- };
19
-
20
13
  type UseAuthTokenRefreshProps = {
21
14
  crossmintAuthService: CrossmintAuthService;
22
15
  setAuthMaterial: (authMaterial: AuthMaterial) => void;
@@ -4,7 +4,7 @@ import { type ReactNode, act } from "react";
4
4
  import { beforeEach, describe, expect, vi } from "vitest";
5
5
  import { mock } from "vitest-mock-extended";
6
6
 
7
- import { CrossmintAuthService, getJWTExpiration } from "@crossmint/client-sdk-auth-core/client";
7
+ import { CrossmintAuthService, getJWTExpiration } from "@crossmint/client-sdk-auth";
8
8
  import { type EVMSmartWallet, SmartWalletSDK } from "@crossmint/client-sdk-smart-wallet";
9
9
  import { createCrossmint } from "@crossmint/common-sdk-base";
10
10
 
@@ -32,8 +32,8 @@ vi.mock("@crossmint/common-sdk-base", async () => {
32
32
  };
33
33
  });
34
34
 
35
- vi.mock("@crossmint/client-sdk-auth-core/client", async () => {
36
- const actual = await vi.importActual("@crossmint/client-sdk-auth-core/client");
35
+ vi.mock("@crossmint/client-sdk-auth", async () => {
36
+ const actual = await vi.importActual("@crossmint/client-sdk-auth");
37
37
  return {
38
38
  ...actual,
39
39
  getJWTExpiration: vi.fn(),
@@ -44,6 +44,10 @@ vi.mock("@crossmint/client-sdk-auth-core/client", async () => {
44
44
  secret: "new-mock-refresh-token",
45
45
  expiresAt: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
46
46
  },
47
+ user: {
48
+ id: "123",
49
+ email: "test@test.com",
50
+ },
47
51
  }),
48
52
  })),
49
53
  };
@@ -2,13 +2,14 @@ import { REFRESH_TOKEN_PREFIX, SESSION_PREFIX, deleteCookie, getCookie, setCooki
2
2
  import { type ReactNode, createContext, useEffect, useState } from "react";
3
3
  import { createPortal } from "react-dom";
4
4
 
5
- import { CrossmintAuthService } from "@crossmint/client-sdk-auth-core/client";
5
+ import { CrossmintAuthService } from "@crossmint/client-sdk-auth";
6
6
  import type { EVMSmartWalletChain } from "@crossmint/client-sdk-smart-wallet";
7
7
  import { type UIConfig, validateApiKeyAndGetCrossmintBaseUrl } from "@crossmint/common-sdk-base";
8
8
 
9
9
  import AuthModal from "../components/auth/AuthModal";
10
- import { type AuthMaterial, useCrossmint, useRefreshToken, useWallet } from "../hooks";
10
+ import { useCrossmint, useRefreshToken, useWallet } from "../hooks";
11
11
  import { CrossmintWalletProvider } from "./CrossmintWalletProvider";
12
+ import type { AuthMaterial, SDKExternalUser } from "@crossmint/common-sdk-auth";
12
13
 
13
14
  export type CrossmintAuthWalletConfig = {
14
15
  defaultChain: EVMSmartWalletChain;
@@ -30,16 +31,20 @@ type AuthContextType = {
30
31
  logout: () => void;
31
32
  jwt?: string;
32
33
  refreshToken?: string;
34
+ user?: SDKExternalUser;
33
35
  status: AuthStatus;
36
+ getUser: () => void;
34
37
  };
35
38
 
36
39
  export const AuthContext = createContext<AuthContextType>({
37
40
  login: () => {},
38
41
  logout: () => {},
39
42
  status: "logged-out",
43
+ getUser: () => {},
40
44
  });
41
45
 
42
46
  export function CrossmintAuthProvider({ embeddedWallets, children, appearance }: CrossmintAuthProviderProps) {
47
+ const [user, setUser] = useState<SDKExternalUser | undefined>(undefined);
43
48
  const { crossmint, setJwt, setRefreshToken } = useCrossmint(
44
49
  "CrossmintAuthProvider must be used within CrossmintProvider"
45
50
  );
@@ -52,6 +57,7 @@ export function CrossmintAuthProvider({ embeddedWallets, children, appearance }:
52
57
  setCookie(REFRESH_TOKEN_PREFIX, authMaterial.refreshToken.secret, authMaterial.refreshToken.expiresAt);
53
58
  setJwt(authMaterial.jwtToken);
54
59
  setRefreshToken(authMaterial.refreshToken.secret);
60
+ setUser(authMaterial.user);
55
61
  };
56
62
 
57
63
  const logout = () => {
@@ -59,19 +65,11 @@ export function CrossmintAuthProvider({ embeddedWallets, children, appearance }:
59
65
  deleteCookie(REFRESH_TOKEN_PREFIX);
60
66
  setJwt(undefined);
61
67
  setRefreshToken(undefined);
68
+ setUser(undefined);
62
69
  };
63
70
 
64
71
  useRefreshToken({ crossmintAuthService, setAuthMaterial, logout });
65
72
 
66
- const login = () => {
67
- if (crossmint.jwt != null) {
68
- console.log("User already logged in");
69
- return;
70
- }
71
-
72
- setModalOpen(true);
73
- };
74
-
75
73
  useEffect(() => {
76
74
  if (crossmint.jwt == null) {
77
75
  const jwt = getCookie(SESSION_PREFIX);
@@ -87,6 +85,15 @@ export function CrossmintAuthProvider({ embeddedWallets, children, appearance }:
87
85
  setModalOpen(false);
88
86
  }, [crossmint.jwt]);
89
87
 
88
+ const login = () => {
89
+ if (crossmint.jwt != null) {
90
+ console.log("User already logged in");
91
+ return;
92
+ }
93
+
94
+ setModalOpen(true);
95
+ };
96
+
90
97
  const getAuthStatus = (): AuthStatus => {
91
98
  if (crossmint.jwt != null) {
92
99
  return "logged-in";
@@ -103,6 +110,16 @@ export function CrossmintAuthProvider({ embeddedWallets, children, appearance }:
103
110
  return authMaterial;
104
111
  };
105
112
 
113
+ const getUser = async () => {
114
+ if (crossmint.jwt == null) {
115
+ console.log("User not logged in");
116
+ return;
117
+ }
118
+
119
+ const user = await crossmintAuthService.getUserFromClient(crossmint.jwt);
120
+ setUser(user);
121
+ };
122
+
106
123
  return (
107
124
  <AuthContext.Provider
108
125
  value={{
@@ -110,7 +127,9 @@ export function CrossmintAuthProvider({ embeddedWallets, children, appearance }:
110
127
  logout,
111
128
  jwt: crossmint.jwt,
112
129
  refreshToken: crossmint.refreshToken,
130
+ user,
113
131
  status: getAuthStatus(),
132
+ getUser,
114
133
  }}
115
134
  >
116
135
  <CrossmintWalletProvider