@crossmint/client-sdk-auth 1.1.3 → 1.1.5
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/dist/CrossmintAuthClient.cjs +1 -12
- package/dist/CrossmintAuthClient.js +1 -12
- package/dist/chunk-22GIA4MK.js +1 -0
- package/dist/chunk-3K3UA645.js +1 -0
- package/dist/chunk-CK4JCQY6.cjs +1 -0
- package/dist/chunk-FTVKNCGF.cjs +1 -0
- package/dist/chunk-HU56N5SW.js +1 -0
- package/dist/chunk-NIH25R3L.js +1 -0
- package/dist/chunk-NYYORERK.cjs +1 -0
- package/dist/chunk-NZ2DAY24.cjs +1 -0
- package/dist/chunk-REIMEXVF.js +1 -0
- package/dist/chunk-U76ID4TS.js +0 -0
- package/dist/chunk-VDJHVTKI.cjs +1 -0
- package/dist/chunk-XHYLTO6W.cjs +1 -0
- package/dist/index.cjs +1 -29
- package/dist/index.js +1 -29
- package/dist/utils/constants.cjs +1 -8
- package/dist/utils/constants.js +1 -8
- package/dist/utils/cookies.cjs +1 -12
- package/dist/utils/cookies.js +1 -12
- package/dist/utils/index.cjs +1 -21
- package/dist/utils/index.js +1 -21
- package/dist/utils/jwt.cjs +1 -8
- package/dist/utils/jwt.js +1 -8
- package/package.json +4 -5
- package/dist/CrossmintAuthClient.cjs.map +0 -1
- package/dist/CrossmintAuthClient.js.map +0 -1
- package/dist/chunk-5JXPQKM4.js +0 -19
- package/dist/chunk-5JXPQKM4.js.map +0 -1
- package/dist/chunk-BGMXXFQ4.cjs +0 -19
- package/dist/chunk-BGMXXFQ4.cjs.map +0 -1
- package/dist/chunk-BMYZMMVR.js +0 -7
- package/dist/chunk-BMYZMMVR.js.map +0 -1
- package/dist/chunk-F45I5NLI.cjs +0 -46
- package/dist/chunk-F45I5NLI.cjs.map +0 -1
- package/dist/chunk-FEGDFSUW.cjs +0 -275
- package/dist/chunk-FEGDFSUW.cjs.map +0 -1
- package/dist/chunk-JPRRZPBL.js +0 -46
- package/dist/chunk-JPRRZPBL.js.map +0 -1
- package/dist/chunk-MLMLBCSI.js +0 -11
- package/dist/chunk-MLMLBCSI.js.map +0 -1
- package/dist/chunk-QY4RIGNM.cjs +0 -11
- package/dist/chunk-QY4RIGNM.cjs.map +0 -1
- package/dist/chunk-SGSYRRRS.js +0 -275
- package/dist/chunk-SGSYRRRS.js.map +0 -1
- package/dist/chunk-TIUX4OOQ.cjs +0 -7
- package/dist/chunk-TIUX4OOQ.cjs.map +0 -1
- package/dist/chunk-TOXKCKTY.js +0 -1
- package/dist/chunk-TOXKCKTY.js.map +0 -1
- package/dist/chunk-VQ3HTIQ3.cjs +0 -1
- package/dist/chunk-VQ3HTIQ3.cjs.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/utils/constants.cjs.map +0 -1
- package/dist/utils/constants.js.map +0 -1
- package/dist/utils/cookies.cjs.map +0 -1
- package/dist/utils/cookies.js.map +0 -1
- package/dist/utils/index.cjs.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/jwt.cjs.map +0 -1
- package/dist/utils/jwt.js.map +0 -1
- package/src/CrossmintAuthClient.test.ts +0 -416
- package/src/CrossmintAuthClient.ts +0 -294
- package/src/index.ts +0 -3
- package/src/utils/constants.ts +0 -1
- package/src/utils/cookies.test.ts +0 -44
- package/src/utils/cookies.ts +0 -13
- package/src/utils/index.ts +0 -3
- package/src/utils/jwt.ts +0 -6
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import type { UseSignInData } from "@farcaster/auth-kit";
|
|
2
|
-
import {
|
|
3
|
-
AUTH_SDK_ROOT_ENDPOINT,
|
|
4
|
-
type AuthMaterialWithUser,
|
|
5
|
-
CROSSMINT_API_VERSION,
|
|
6
|
-
CrossmintAuth,
|
|
7
|
-
CrossmintAuthenticationError,
|
|
8
|
-
type CrossmintAuthOptions,
|
|
9
|
-
type OAuthProvider,
|
|
10
|
-
REFRESH_TOKEN_PREFIX,
|
|
11
|
-
SESSION_PREFIX,
|
|
12
|
-
} from "@crossmint/common-sdk-auth";
|
|
13
|
-
import type { Crossmint, CrossmintApiClient } from "@crossmint/common-sdk-base";
|
|
14
|
-
import { type CancellableTask, queueTask } from "@crossmint/client-sdk-base";
|
|
15
|
-
import { deleteCookie, getCookie, getJWTExpiration, setCookie, TIME_BEFORE_EXPIRING_JWT_IN_SECONDS } from "./utils";
|
|
16
|
-
|
|
17
|
-
type CrossmintAuthClientConfig = CrossmintAuthOptions & {
|
|
18
|
-
callbacks?: CrossmintAuthClientCallbacks;
|
|
19
|
-
logoutRoute?: string;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export class CrossmintAuthClient extends CrossmintAuth {
|
|
23
|
-
private callbacks: CrossmintAuthClientCallbacks;
|
|
24
|
-
private refreshTask: CancellableTask | null = null;
|
|
25
|
-
private refreshPromise: Promise<AuthMaterialWithUser> | null = null;
|
|
26
|
-
private logoutRoute: string | null;
|
|
27
|
-
|
|
28
|
-
private constructor(crossmint: Crossmint, apiClient: CrossmintApiClient, config: CrossmintAuthClientConfig = {}) {
|
|
29
|
-
super(crossmint, apiClient, config);
|
|
30
|
-
this.callbacks = config.callbacks ?? {};
|
|
31
|
-
this.logoutRoute = config.logoutRoute ?? null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public static from(crossmint: Crossmint, config: CrossmintAuthClientConfig = {}): CrossmintAuthClient {
|
|
35
|
-
const authClient = new CrossmintAuthClient(crossmint, CrossmintAuth.defaultApiClient(crossmint), config);
|
|
36
|
-
// In case an instance is created on the server, we can't refresh as this stores cookies
|
|
37
|
-
if (typeof window !== "undefined") {
|
|
38
|
-
authClient.handleRefreshAuthMaterial();
|
|
39
|
-
}
|
|
40
|
-
return authClient;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
public async getUser() {
|
|
44
|
-
try {
|
|
45
|
-
const response = await this.apiClient.get(`api/${CROSSMINT_API_VERSION}/sdk/auth/user`, {
|
|
46
|
-
headers: { "Content-Type": "application/json" },
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
if (!response.ok) {
|
|
50
|
-
throw await response.text();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return await response.json();
|
|
54
|
-
} catch (error) {
|
|
55
|
-
throw new CrossmintAuthenticationError(
|
|
56
|
-
`Failed to fetch user: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
public storeAuthMaterial(authMaterial: AuthMaterialWithUser) {
|
|
62
|
-
setCookie(SESSION_PREFIX, authMaterial.jwt);
|
|
63
|
-
setCookie(REFRESH_TOKEN_PREFIX, authMaterial.refreshToken.secret, authMaterial.refreshToken.expiresAt);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public async logout() {
|
|
67
|
-
// Store the old refresh token to pass it to the logout route before deleting the cookies
|
|
68
|
-
const oldRefreshToken = getCookie(REFRESH_TOKEN_PREFIX);
|
|
69
|
-
|
|
70
|
-
// Even if there's a server error, we want to clear the cookies and we do it first to load faster
|
|
71
|
-
deleteCookie(REFRESH_TOKEN_PREFIX);
|
|
72
|
-
deleteCookie(SESSION_PREFIX);
|
|
73
|
-
this.callbacks.onLogout?.();
|
|
74
|
-
try {
|
|
75
|
-
if (this.logoutRoute != null) {
|
|
76
|
-
await this.logoutFromCustomRoute();
|
|
77
|
-
} else if (oldRefreshToken != null) {
|
|
78
|
-
await this.logoutFromDefaultRoute(oldRefreshToken);
|
|
79
|
-
}
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error(error);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
public async handleRefreshAuthMaterial(refreshTokenSecret?: string): Promise<void> {
|
|
86
|
-
const refreshToken = refreshTokenSecret ?? getCookie(REFRESH_TOKEN_PREFIX);
|
|
87
|
-
// If there is a custom refresh route, that endpoint will fetch the cookies itself
|
|
88
|
-
if (refreshToken == null && this.refreshRoute == null) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
// Create new refresh promise if none exists
|
|
94
|
-
if (this.refreshPromise == null) {
|
|
95
|
-
this.refreshPromise = this.refreshAuthMaterial(refreshToken);
|
|
96
|
-
}
|
|
97
|
-
const authMaterial = await this.refreshPromise;
|
|
98
|
-
|
|
99
|
-
// If a custom refresh route is set, storing in cookies is handled in the server
|
|
100
|
-
if (this.refreshRoute == null) {
|
|
101
|
-
this.storeAuthMaterial(authMaterial);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
this.callbacks.onTokenRefresh?.(authMaterial);
|
|
105
|
-
|
|
106
|
-
this.scheduleNextRefresh(authMaterial.jwt);
|
|
107
|
-
} catch (error) {
|
|
108
|
-
console.error(error);
|
|
109
|
-
this.logout();
|
|
110
|
-
} finally {
|
|
111
|
-
this.refreshPromise = null;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
public async getOAuthUrl(provider: OAuthProvider) {
|
|
116
|
-
try {
|
|
117
|
-
const response = await this.apiClient.get(`${AUTH_SDK_ROOT_ENDPOINT}/social/${provider}/start`, {
|
|
118
|
-
headers: { "Content-Type": "application/json" },
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
if (!response.ok) {
|
|
122
|
-
throw await response.text();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const data = await response.json();
|
|
126
|
-
return data.oauthUrl;
|
|
127
|
-
} catch (error) {
|
|
128
|
-
throw new CrossmintAuthenticationError(
|
|
129
|
-
`Failed to get OAuth URL for provider ${provider}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
public async sendEmailOtp(email: string) {
|
|
135
|
-
try {
|
|
136
|
-
const response = await this.apiClient.post(`${AUTH_SDK_ROOT_ENDPOINT}/otps/send`, {
|
|
137
|
-
headers: { "Content-Type": "application/json" },
|
|
138
|
-
body: JSON.stringify({ email }),
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
if (!response.ok) {
|
|
142
|
-
throw await response.text();
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return await response.json();
|
|
146
|
-
} catch (error) {
|
|
147
|
-
throw new CrossmintAuthenticationError(
|
|
148
|
-
`Failed to send email OTP: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
public async confirmEmailOtp(email: string, emailId: string, token: string) {
|
|
154
|
-
try {
|
|
155
|
-
const queryParams = new URLSearchParams({
|
|
156
|
-
email,
|
|
157
|
-
signinAuthenticationMethod: "email",
|
|
158
|
-
token,
|
|
159
|
-
locale: "en",
|
|
160
|
-
state: emailId,
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
const response = await this.apiClient.post(`${AUTH_SDK_ROOT_ENDPOINT}/authenticate?${queryParams}`, {
|
|
164
|
-
headers: { "Content-Type": "application/json" },
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
if (!response.ok) {
|
|
168
|
-
throw await response.text();
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const resData = await response.json();
|
|
172
|
-
return resData.oneTimeSecret;
|
|
173
|
-
} catch (error) {
|
|
174
|
-
throw new CrossmintAuthenticationError(
|
|
175
|
-
`Failed to confirm email OTP: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
public async signInWithFarcaster(data: UseSignInData) {
|
|
181
|
-
try {
|
|
182
|
-
const queryParams = new URLSearchParams({
|
|
183
|
-
signinAuthenticationMethod: "farcaster",
|
|
184
|
-
callbackUrl: `${this.apiClient.baseUrl}/${AUTH_SDK_ROOT_ENDPOINT}/callback`,
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
const response = await this.apiClient.post(`${AUTH_SDK_ROOT_ENDPOINT}/authenticate?${queryParams}`, {
|
|
188
|
-
headers: { "Content-Type": "application/json" },
|
|
189
|
-
body: JSON.stringify({
|
|
190
|
-
...data,
|
|
191
|
-
domain: data.signatureParams.domain,
|
|
192
|
-
redirect: true,
|
|
193
|
-
callbackUrl: `${this.apiClient.baseUrl}/${AUTH_SDK_ROOT_ENDPOINT}/callback`,
|
|
194
|
-
}),
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
if (!response.ok) {
|
|
198
|
-
throw await response.text();
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const resData = await response.json();
|
|
202
|
-
return resData.oneTimeSecret;
|
|
203
|
-
} catch (error) {
|
|
204
|
-
throw new CrossmintAuthenticationError(
|
|
205
|
-
`Failed to sign in with Farcaster: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
public async signInWithSmartWallet(address: string) {
|
|
211
|
-
try {
|
|
212
|
-
const queryParams = new URLSearchParams({ signinAuthenticationMethod: "evm" });
|
|
213
|
-
const response = await this.apiClient.post(
|
|
214
|
-
`${AUTH_SDK_ROOT_ENDPOINT}/crypto_wallets/authenticate/start?${queryParams}`,
|
|
215
|
-
{
|
|
216
|
-
headers: { "Content-Type": "application/json" },
|
|
217
|
-
body: JSON.stringify({ walletAddress: address }),
|
|
218
|
-
}
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
if (!response.ok) {
|
|
222
|
-
throw await response.text();
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return await response.json();
|
|
226
|
-
} catch (error) {
|
|
227
|
-
throw new CrossmintAuthenticationError(
|
|
228
|
-
`Failed to initiate smart wallet sign in: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
public async authenticateSmartWallet(address: string, signature: string) {
|
|
234
|
-
try {
|
|
235
|
-
const queryParams = new URLSearchParams({
|
|
236
|
-
signinAuthenticationMethod: "evm",
|
|
237
|
-
callbackUrl: `${this.apiClient.baseUrl}/${AUTH_SDK_ROOT_ENDPOINT}/callback`,
|
|
238
|
-
});
|
|
239
|
-
const response = await this.apiClient.post(
|
|
240
|
-
`${AUTH_SDK_ROOT_ENDPOINT}/crypto_wallets/authenticate?${queryParams}`,
|
|
241
|
-
{
|
|
242
|
-
headers: { "Content-Type": "application/json" },
|
|
243
|
-
body: JSON.stringify({ walletAddress: address, signature }),
|
|
244
|
-
}
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
if (!response.ok) {
|
|
248
|
-
throw await response.text();
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return await response.json();
|
|
252
|
-
} catch (error) {
|
|
253
|
-
throw new CrossmintAuthenticationError(
|
|
254
|
-
`Failed to authenticate smart wallet: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
private async logoutFromCustomRoute(): Promise<Response> {
|
|
260
|
-
if (!this.logoutRoute) {
|
|
261
|
-
throw new Error("Custom logout route is not set");
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return await fetch(this.logoutRoute, { method: "POST" });
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
private scheduleNextRefresh(jwt: string): void {
|
|
268
|
-
const jwtExpiration = getJWTExpiration(jwt);
|
|
269
|
-
if (!jwtExpiration) {
|
|
270
|
-
throw new Error("Invalid JWT");
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const currentTime = Date.now() / 1000;
|
|
274
|
-
const timeToExpire = jwtExpiration - currentTime - TIME_BEFORE_EXPIRING_JWT_IN_SECONDS;
|
|
275
|
-
|
|
276
|
-
if (timeToExpire > 0) {
|
|
277
|
-
const endTime = Date.now() + timeToExpire * 1000;
|
|
278
|
-
this.cancelScheduledRefresh();
|
|
279
|
-
this.refreshTask = queueTask(() => this.handleRefreshAuthMaterial(), endTime);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
private cancelScheduledRefresh(): void {
|
|
284
|
-
if (this.refreshTask) {
|
|
285
|
-
this.refreshTask.cancel();
|
|
286
|
-
this.refreshTask = null;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
type CrossmintAuthClientCallbacks = {
|
|
292
|
-
onTokenRefresh?: (authMaterial: AuthMaterialWithUser) => void;
|
|
293
|
-
onLogout?: () => void;
|
|
294
|
-
};
|
package/src/index.ts
DELETED
package/src/utils/constants.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const TIME_BEFORE_EXPIRING_JWT_IN_SECONDS = 120;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { deleteCookie, getCookie, setCookie } from "./cookies";
|
|
3
|
-
|
|
4
|
-
describe("cookies", () => {
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
// Clear all cookies before each test
|
|
7
|
-
document.cookie.split(";").forEach((cookie) => {
|
|
8
|
-
document.cookie = cookie
|
|
9
|
-
.replace(/^ +/, "")
|
|
10
|
-
.replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/");
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("should return undefined for non-existent cookie", () => {
|
|
15
|
-
expect(getCookie("non-existent")).toBeUndefined();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("should return the correct value for an existing cookie", async () => {
|
|
19
|
-
document.cookie = "test-cookie=test-value";
|
|
20
|
-
await waitForSettledState(() => {
|
|
21
|
-
expect(getCookie("test-cookie")).toBe("test-value");
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("should set a cookie without expiration", async () => {
|
|
26
|
-
setCookie("test-cookie", "test-value");
|
|
27
|
-
await waitForSettledState(() => {
|
|
28
|
-
expect(document.cookie).toContain("test-cookie=test-value");
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("should delete an existing cookie", async () => {
|
|
33
|
-
document.cookie = "test-cookie=test-value";
|
|
34
|
-
deleteCookie("test-cookie");
|
|
35
|
-
await waitForSettledState(() => {
|
|
36
|
-
expect(document.cookie).not.toContain("test-cookie");
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const waitForSettledState = async (callback: () => void) => {
|
|
42
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
43
|
-
callback();
|
|
44
|
-
};
|
package/src/utils/cookies.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export function getCookie(name: string): string | undefined {
|
|
2
|
-
const crossmintRefreshToken = document.cookie.split("; ").find((row) => row.startsWith(name));
|
|
3
|
-
return crossmintRefreshToken ? crossmintRefreshToken.split("=")[1] : undefined;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export function setCookie(name: string, value: string, expiresAt?: string) {
|
|
7
|
-
const expiresInUtc = expiresAt ? new Date(expiresAt).toUTCString() : "";
|
|
8
|
-
document.cookie = `${name}=${value}; ${expiresAt ? `expires=${expiresInUtc};` : ""} path=/; SameSite=Lax;`;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function deleteCookie(name: string) {
|
|
12
|
-
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
|
|
13
|
-
}
|
package/src/utils/index.ts
DELETED