@authly/sdk 1.1.2 → 1.2.0
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/globals/clients/AuthlyClient.d.ts +76 -0
- package/dist/globals/clients/AuthlyClient.js +227 -0
- package/dist/globals/configuration/AuthlyConfiguration.d.ts +12 -0
- package/dist/globals/configuration/AuthlyConfiguration.js +12 -0
- package/dist/globals/internal/PKCEUtils.d.ts +23 -0
- package/dist/globals/internal/PKCEUtils.js +59 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/models/globals/clients/interfaces/IAuthlyClientOptions.d.ts +23 -0
- package/dist/models/globals/clients/interfaces/IAuthlyStorage.d.ts +22 -0
- package/dist/models/globals/clients/interfaces/IAuthlyStorage.js +2 -0
- package/dist/models/globals/clients/interfaces/ITokenResponse.d.ts +29 -0
- package/dist/models/globals/clients/interfaces/ITokenResponse.js +2 -0
- package/dist/models/globals/clients/interfaces/IUserProfile.d.ts +42 -0
- package/dist/models/globals/clients/interfaces/IUserProfile.js +2 -0
- package/dist/react/AuthlyCallback.d.ts +20 -0
- package/dist/react/AuthlyCallback.js +45 -0
- package/dist/react/AuthlyContext.d.ts +16 -0
- package/dist/react/AuthlyContext.js +14 -0
- package/dist/react/AuthlyProvider.d.ts +8 -0
- package/dist/react/AuthlyProvider.js +54 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.js +19 -0
- package/package.json +25 -2
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { IAuthlyClientOptions } from "../../models/globals/clients/interfaces/IAuthlyClientOptions";
|
|
2
2
|
import type { IAuthorizeUrlOptions } from "../../models/globals/clients/interfaces/IAuthorizeUrlOptions";
|
|
3
3
|
import type { IDecodedTokenClaim } from "../../models/globals/clients/interfaces/IDecodedTokenClaim";
|
|
4
|
+
import type { ITokenResponse } from "../../models/globals/clients/interfaces/ITokenResponse";
|
|
5
|
+
import type { IUserProfile } from "../../models/globals/clients/interfaces/IUserProfile";
|
|
4
6
|
/**
|
|
5
7
|
* @summary A client for interacting with Authly.
|
|
6
8
|
* @description This client handles the validation of tokens against a specific issuer and audience,
|
|
@@ -8,6 +10,7 @@ import type { IDecodedTokenClaim } from "../../models/globals/clients/interfaces
|
|
|
8
10
|
* starting the OAuth2 flow.
|
|
9
11
|
*/
|
|
10
12
|
declare class AuthlyClient {
|
|
13
|
+
private static readonly ACCESS_TOKEN_KEY;
|
|
11
14
|
/**
|
|
12
15
|
* @summary The JWT verifier for the client.
|
|
13
16
|
*/
|
|
@@ -24,17 +27,90 @@ declare class AuthlyClient {
|
|
|
24
27
|
* @summary The authorize path of the client.
|
|
25
28
|
*/
|
|
26
29
|
private readonly authorizePath;
|
|
30
|
+
/**
|
|
31
|
+
* @summary The token path of the client.
|
|
32
|
+
*/
|
|
33
|
+
private readonly tokenPath;
|
|
34
|
+
/**
|
|
35
|
+
* @summary The user info path of the client.
|
|
36
|
+
*/
|
|
37
|
+
private readonly userInfoPath;
|
|
38
|
+
/**
|
|
39
|
+
* @summary The redirect URI for the client.
|
|
40
|
+
*/
|
|
41
|
+
private readonly redirectUri?;
|
|
42
|
+
/**
|
|
43
|
+
* @summary The storage implementation.
|
|
44
|
+
*/
|
|
45
|
+
private readonly storage?;
|
|
46
|
+
/**
|
|
47
|
+
* @summary Memory cache for the access token.
|
|
48
|
+
*/
|
|
49
|
+
private accessToken;
|
|
27
50
|
/**
|
|
28
51
|
* @summary Constructs a new AuthlyClient.
|
|
29
52
|
* @param options - The options for the client.
|
|
30
53
|
*/
|
|
31
54
|
constructor(options: IAuthlyClientOptions);
|
|
55
|
+
/**
|
|
56
|
+
* @summary Prepares the authorization request, stores PKCE state, and returns the URL.
|
|
57
|
+
* @param options - Optional overrides for the authorization request.
|
|
58
|
+
* @returns A promise that resolves to the authorization URL.
|
|
59
|
+
*/
|
|
60
|
+
authorize(options?: Partial<IAuthorizeUrlOptions>): Promise<string>;
|
|
32
61
|
/**
|
|
33
62
|
* @summary Generate the authorization URL to redirect the user to.
|
|
34
63
|
* @param options - Options for generating the URL.
|
|
35
64
|
* @returns The full authorization URL.
|
|
36
65
|
*/
|
|
37
66
|
getAuthorizeUrl(options: IAuthorizeUrlOptions): string;
|
|
67
|
+
/**
|
|
68
|
+
* @summary Exchanges the authorization code for tokens using PKCE flow and state validation.
|
|
69
|
+
* @param urlParams - The URLSearchParams containing 'code' and 'state'.
|
|
70
|
+
* @returns A promise that resolves to the token response.
|
|
71
|
+
*/
|
|
72
|
+
exchangeToken(urlParams: URLSearchParams): Promise<ITokenResponse>;
|
|
73
|
+
/**
|
|
74
|
+
* @summary Set the current session.
|
|
75
|
+
* @param response - The token response from Authly.
|
|
76
|
+
*/
|
|
77
|
+
setSession(response: ITokenResponse): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* @summary Get the current access token.
|
|
80
|
+
* @returns The access token or null if not found.
|
|
81
|
+
*/
|
|
82
|
+
getAccessToken(): Promise<string | null>;
|
|
83
|
+
/**
|
|
84
|
+
* @summary Refreshes the access token using the refresh_token grant.
|
|
85
|
+
* @description In the browser, this relies on the 'session' cookie if no explicit refresh token is provided.
|
|
86
|
+
* @param refreshToken - Optional explicit refresh token.
|
|
87
|
+
* @returns A promise that resolves to the new access token.
|
|
88
|
+
*/
|
|
89
|
+
refreshToken(refreshToken?: string): Promise<string | null>;
|
|
90
|
+
/**
|
|
91
|
+
* @summary Fetches the user profile from the userinfo endpoint.
|
|
92
|
+
* @description Automatically handles token expiration by attempting to refresh the token once.
|
|
93
|
+
* @returns A promise that resolves to the user profile or null if not authenticated.
|
|
94
|
+
*/
|
|
95
|
+
getUser(): Promise<IUserProfile | null>;
|
|
96
|
+
/**
|
|
97
|
+
* @summary Synchronously check if the user is authenticated based on token presence and expiration.
|
|
98
|
+
* @description This only checks the token locally and does not perform a network request.
|
|
99
|
+
* @returns True if a valid token exists and is not expired.
|
|
100
|
+
*/
|
|
101
|
+
isAuthenticated(): Promise<boolean>;
|
|
102
|
+
/**
|
|
103
|
+
* @summary Logs out the user by clearing the session from storage and memory.
|
|
104
|
+
*/
|
|
105
|
+
logout(): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* @summary Exchange the authorization code for an access token.
|
|
108
|
+
* @param code - The authorization code received from the callback.
|
|
109
|
+
* @param redirectUri - The redirect URI used in the authorize request.
|
|
110
|
+
* @param codeVerifier - The PKCE code verifier (required if used in authorize).
|
|
111
|
+
* @returns A promise that resolves to the token response.
|
|
112
|
+
*/
|
|
113
|
+
exchangeCodeForToken(code: string, redirectUri: string, codeVerifier?: string): Promise<ITokenResponse>;
|
|
38
114
|
/**
|
|
39
115
|
* @summary Verify a JWT token and return its decoded claims.
|
|
40
116
|
* @description This method verifies the token's signature using the provider's JWKS,
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AuthlyClient = void 0;
|
|
4
|
+
const jose_1 = require("jose");
|
|
4
5
|
const JWTVerifier_1 = require("../internal/JWTVerifier");
|
|
6
|
+
const HttpClient_1 = require("../internal/HttpClient");
|
|
7
|
+
const PKCEUtils_1 = require("../internal/PKCEUtils");
|
|
5
8
|
const AuthlyConfiguration_1 = require("../configuration/AuthlyConfiguration");
|
|
6
9
|
/**
|
|
7
10
|
* @summary A client for interacting with Authly.
|
|
@@ -10,6 +13,7 @@ const AuthlyConfiguration_1 = require("../configuration/AuthlyConfiguration");
|
|
|
10
13
|
* starting the OAuth2 flow.
|
|
11
14
|
*/
|
|
12
15
|
class AuthlyClient {
|
|
16
|
+
static ACCESS_TOKEN_KEY = "authly_access_token";
|
|
13
17
|
/**
|
|
14
18
|
* @summary The JWT verifier for the client.
|
|
15
19
|
*/
|
|
@@ -26,6 +30,26 @@ class AuthlyClient {
|
|
|
26
30
|
* @summary The authorize path of the client.
|
|
27
31
|
*/
|
|
28
32
|
authorizePath;
|
|
33
|
+
/**
|
|
34
|
+
* @summary The token path of the client.
|
|
35
|
+
*/
|
|
36
|
+
tokenPath;
|
|
37
|
+
/**
|
|
38
|
+
* @summary The user info path of the client.
|
|
39
|
+
*/
|
|
40
|
+
userInfoPath;
|
|
41
|
+
/**
|
|
42
|
+
* @summary The redirect URI for the client.
|
|
43
|
+
*/
|
|
44
|
+
redirectUri;
|
|
45
|
+
/**
|
|
46
|
+
* @summary The storage implementation.
|
|
47
|
+
*/
|
|
48
|
+
storage;
|
|
49
|
+
/**
|
|
50
|
+
* @summary Memory cache for the access token.
|
|
51
|
+
*/
|
|
52
|
+
accessToken = null;
|
|
29
53
|
/**
|
|
30
54
|
* @summary Constructs a new AuthlyClient.
|
|
31
55
|
* @param options - The options for the client.
|
|
@@ -34,6 +58,13 @@ class AuthlyClient {
|
|
|
34
58
|
this.issuer = options.issuer.replace(/\/$/, "");
|
|
35
59
|
this.serviceId = options.serviceId;
|
|
36
60
|
this.authorizePath = options.authorizePath || AuthlyConfiguration_1.AuthlyConfiguration.DEFAULT_AUTHORIZE_PATH;
|
|
61
|
+
this.tokenPath = options.tokenPath || AuthlyConfiguration_1.AuthlyConfiguration.DEFAULT_TOKEN_PATH;
|
|
62
|
+
this.userInfoPath = options.userInfoPath || AuthlyConfiguration_1.AuthlyConfiguration.DEFAULT_USER_INFO_PATH;
|
|
63
|
+
this.redirectUri = options.redirectUri;
|
|
64
|
+
this.storage = options.storage;
|
|
65
|
+
if (!this.storage && typeof window !== "undefined" && window.sessionStorage) {
|
|
66
|
+
this.storage = window.sessionStorage;
|
|
67
|
+
}
|
|
37
68
|
this.verifier = new JWTVerifier_1.JWTVerifier({
|
|
38
69
|
issuer: this.issuer,
|
|
39
70
|
audience: options.audience,
|
|
@@ -41,6 +72,31 @@ class AuthlyClient {
|
|
|
41
72
|
algorithms: options.algorithms,
|
|
42
73
|
});
|
|
43
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* @summary Prepares the authorization request, stores PKCE state, and returns the URL.
|
|
77
|
+
* @param options - Optional overrides for the authorization request.
|
|
78
|
+
* @returns A promise that resolves to the authorization URL.
|
|
79
|
+
*/
|
|
80
|
+
async authorize(options) {
|
|
81
|
+
if (!this.storage) {
|
|
82
|
+
throw new Error("Storage is not configured. Cannot save state and code verifier.");
|
|
83
|
+
}
|
|
84
|
+
if (!this.redirectUri && !options?.redirectUri) {
|
|
85
|
+
throw new Error("Redirect URI is required but not configured.");
|
|
86
|
+
}
|
|
87
|
+
const state = options?.state || PKCEUtils_1.PKCEUtils.generateRandomString();
|
|
88
|
+
const codeVerifier = PKCEUtils_1.PKCEUtils.generateRandomString();
|
|
89
|
+
const codeChallenge = await PKCEUtils_1.PKCEUtils.generateCodeChallenge(codeVerifier);
|
|
90
|
+
await this.storage.setItem("pkce_state", state);
|
|
91
|
+
await this.storage.setItem("pkce_verifier", codeVerifier);
|
|
92
|
+
return this.getAuthorizeUrl({
|
|
93
|
+
redirectUri: (options?.redirectUri || this.redirectUri),
|
|
94
|
+
...options,
|
|
95
|
+
state,
|
|
96
|
+
codeChallenge,
|
|
97
|
+
codeChallengeMethod: "S256",
|
|
98
|
+
});
|
|
99
|
+
}
|
|
44
100
|
/**
|
|
45
101
|
* @summary Generate the authorization URL to redirect the user to.
|
|
46
102
|
* @param options - Options for generating the URL.
|
|
@@ -57,6 +113,177 @@ class AuthlyClient {
|
|
|
57
113
|
url.searchParams.set("code_challenge_method", options.codeChallengeMethod || "s256");
|
|
58
114
|
return url.toString();
|
|
59
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* @summary Exchanges the authorization code for tokens using PKCE flow and state validation.
|
|
118
|
+
* @param urlParams - The URLSearchParams containing 'code' and 'state'.
|
|
119
|
+
* @returns A promise that resolves to the token response.
|
|
120
|
+
*/
|
|
121
|
+
async exchangeToken(urlParams) {
|
|
122
|
+
const code = urlParams.get("code");
|
|
123
|
+
const state = urlParams.get("state");
|
|
124
|
+
if (!code) {
|
|
125
|
+
throw new Error("No authorization code found in URL parameters.");
|
|
126
|
+
}
|
|
127
|
+
if (!this.storage) {
|
|
128
|
+
throw new Error("Storage is not configured. Cannot retrieve state and code verifier.");
|
|
129
|
+
}
|
|
130
|
+
const storedState = await this.storage.getItem("pkce_state");
|
|
131
|
+
if (!state || state !== storedState) {
|
|
132
|
+
throw new Error("Invalid state. Possible CSRF attack.");
|
|
133
|
+
}
|
|
134
|
+
const codeVerifier = await this.storage.getItem("pkce_verifier");
|
|
135
|
+
if (!codeVerifier) {
|
|
136
|
+
throw new Error("No code verifier found in storage.");
|
|
137
|
+
}
|
|
138
|
+
if (!this.redirectUri) {
|
|
139
|
+
throw new Error("Redirect URI is not configured in AuthlyClient options.");
|
|
140
|
+
}
|
|
141
|
+
const tokenResponse = await this.exchangeCodeForToken(code, this.redirectUri, codeVerifier);
|
|
142
|
+
await this.setSession(tokenResponse);
|
|
143
|
+
// Clear temporary storage
|
|
144
|
+
await this.storage.removeItem("pkce_state");
|
|
145
|
+
await this.storage.removeItem("pkce_verifier");
|
|
146
|
+
return tokenResponse;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* @summary Set the current session.
|
|
150
|
+
* @param response - The token response from Authly.
|
|
151
|
+
*/
|
|
152
|
+
async setSession(response) {
|
|
153
|
+
this.accessToken = response.access_token;
|
|
154
|
+
if (this.storage) {
|
|
155
|
+
await this.storage.setItem(AuthlyClient.ACCESS_TOKEN_KEY, response.access_token);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* @summary Get the current access token.
|
|
160
|
+
* @returns The access token or null if not found.
|
|
161
|
+
*/
|
|
162
|
+
async getAccessToken() {
|
|
163
|
+
if (this.accessToken)
|
|
164
|
+
return this.accessToken;
|
|
165
|
+
if (this.storage) {
|
|
166
|
+
this.accessToken = await this.storage.getItem(AuthlyClient.ACCESS_TOKEN_KEY);
|
|
167
|
+
}
|
|
168
|
+
return this.accessToken;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* @summary Refreshes the access token using the refresh_token grant.
|
|
172
|
+
* @description In the browser, this relies on the 'session' cookie if no explicit refresh token is provided.
|
|
173
|
+
* @param refreshToken - Optional explicit refresh token.
|
|
174
|
+
* @returns A promise that resolves to the new access token.
|
|
175
|
+
*/
|
|
176
|
+
async refreshToken(refreshToken) {
|
|
177
|
+
const url = `${this.issuer}${this.tokenPath}`;
|
|
178
|
+
const body = {
|
|
179
|
+
grant_type: "refresh_token",
|
|
180
|
+
client_id: this.serviceId,
|
|
181
|
+
};
|
|
182
|
+
if (refreshToken) {
|
|
183
|
+
body.refresh_token = refreshToken;
|
|
184
|
+
}
|
|
185
|
+
const response = await HttpClient_1.HttpClient.post(url, {
|
|
186
|
+
headers: {
|
|
187
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
188
|
+
},
|
|
189
|
+
body: new URLSearchParams(body).toString(),
|
|
190
|
+
});
|
|
191
|
+
if (!response.success) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
await this.setSession(response.data);
|
|
195
|
+
return response.data.access_token;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* @summary Fetches the user profile from the userinfo endpoint.
|
|
199
|
+
* @description Automatically handles token expiration by attempting to refresh the token once.
|
|
200
|
+
* @returns A promise that resolves to the user profile or null if not authenticated.
|
|
201
|
+
*/
|
|
202
|
+
async getUser() {
|
|
203
|
+
let token = await this.getAccessToken();
|
|
204
|
+
if (!token)
|
|
205
|
+
return null;
|
|
206
|
+
const fetchInfo = async (currentBuffer) => {
|
|
207
|
+
return HttpClient_1.HttpClient.get(`${this.issuer}${this.userInfoPath}`, {
|
|
208
|
+
headers: {
|
|
209
|
+
Authorization: `Bearer ${currentBuffer}`,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
let response = await fetchInfo(token);
|
|
214
|
+
// If unauthorized (401), try to refresh token once
|
|
215
|
+
if (!response.success && response.error?.code === "UNAUTHORIZED") {
|
|
216
|
+
token = await this.refreshToken();
|
|
217
|
+
if (token) {
|
|
218
|
+
response = await fetchInfo(token);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (!response.success) {
|
|
222
|
+
if (response.error?.code === "UNAUTHORIZED") {
|
|
223
|
+
await this.logout();
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
return response.data;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* @summary Synchronously check if the user is authenticated based on token presence and expiration.
|
|
231
|
+
* @description This only checks the token locally and does not perform a network request.
|
|
232
|
+
* @returns True if a valid token exists and is not expired.
|
|
233
|
+
*/
|
|
234
|
+
async isAuthenticated() {
|
|
235
|
+
const token = await this.getAccessToken();
|
|
236
|
+
if (!token)
|
|
237
|
+
return false;
|
|
238
|
+
try {
|
|
239
|
+
const decoded = (0, jose_1.decodeJwt)(token);
|
|
240
|
+
if (!decoded.exp)
|
|
241
|
+
return true;
|
|
242
|
+
const now = Math.floor(Date.now() / 1000);
|
|
243
|
+
return decoded.exp > now + 10;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* @summary Logs out the user by clearing the session from storage and memory.
|
|
251
|
+
*/
|
|
252
|
+
async logout() {
|
|
253
|
+
this.accessToken = null;
|
|
254
|
+
if (this.storage) {
|
|
255
|
+
await this.storage.removeItem(AuthlyClient.ACCESS_TOKEN_KEY);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* @summary Exchange the authorization code for an access token.
|
|
260
|
+
* @param code - The authorization code received from the callback.
|
|
261
|
+
* @param redirectUri - The redirect URI used in the authorize request.
|
|
262
|
+
* @param codeVerifier - The PKCE code verifier (required if used in authorize).
|
|
263
|
+
* @returns A promise that resolves to the token response.
|
|
264
|
+
*/
|
|
265
|
+
async exchangeCodeForToken(code, redirectUri, codeVerifier) {
|
|
266
|
+
const url = `${this.issuer}${this.tokenPath}`;
|
|
267
|
+
const body = {
|
|
268
|
+
grant_type: "authorization_code",
|
|
269
|
+
client_id: this.serviceId,
|
|
270
|
+
code,
|
|
271
|
+
redirect_uri: redirectUri,
|
|
272
|
+
};
|
|
273
|
+
if (codeVerifier) {
|
|
274
|
+
body.code_verifier = codeVerifier;
|
|
275
|
+
}
|
|
276
|
+
const response = await HttpClient_1.HttpClient.post(url, {
|
|
277
|
+
headers: {
|
|
278
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
279
|
+
},
|
|
280
|
+
body: new URLSearchParams(body).toString(),
|
|
281
|
+
});
|
|
282
|
+
if (!response.success) {
|
|
283
|
+
throw new Error(response.error?.message || "Failed to exchange code for token");
|
|
284
|
+
}
|
|
285
|
+
return response.data;
|
|
286
|
+
}
|
|
60
287
|
/**
|
|
61
288
|
* @summary Verify a JWT token and return its decoded claims.
|
|
62
289
|
* @description This method verifies the token's signature using the provider's JWKS,
|
|
@@ -18,6 +18,18 @@ declare class AuthlyConfiguration {
|
|
|
18
18
|
* @default "/authorize"
|
|
19
19
|
*/
|
|
20
20
|
static readonly DEFAULT_AUTHORIZE_PATH: string;
|
|
21
|
+
/**
|
|
22
|
+
* @summary The default token path.
|
|
23
|
+
* @readonly This property is readonly and cannot be changed.
|
|
24
|
+
* @default "/oauth/token"
|
|
25
|
+
*/
|
|
26
|
+
static readonly DEFAULT_TOKEN_PATH: string;
|
|
27
|
+
/**
|
|
28
|
+
* @summary The default user info path.
|
|
29
|
+
* @readonly This property is readonly and cannot be changed.
|
|
30
|
+
* @default "/v1/oauth/userinfo"
|
|
31
|
+
*/
|
|
32
|
+
static readonly DEFAULT_USER_INFO_PATH: string;
|
|
21
33
|
/**
|
|
22
34
|
* @summary The default algorithms.
|
|
23
35
|
* @readonly This property is readonly and cannot be changed.
|
|
@@ -21,6 +21,18 @@ class AuthlyConfiguration {
|
|
|
21
21
|
* @default "/authorize"
|
|
22
22
|
*/
|
|
23
23
|
static DEFAULT_AUTHORIZE_PATH = "/authorize";
|
|
24
|
+
/**
|
|
25
|
+
* @summary The default token path.
|
|
26
|
+
* @readonly This property is readonly and cannot be changed.
|
|
27
|
+
* @default "/oauth/token"
|
|
28
|
+
*/
|
|
29
|
+
static DEFAULT_TOKEN_PATH = "/oauth/token";
|
|
30
|
+
/**
|
|
31
|
+
* @summary The default user info path.
|
|
32
|
+
* @readonly This property is readonly and cannot be changed.
|
|
33
|
+
* @default "/v1/oauth/userinfo"
|
|
34
|
+
*/
|
|
35
|
+
static DEFAULT_USER_INFO_PATH = "/v1/oauth/userinfo";
|
|
24
36
|
/**
|
|
25
37
|
* @summary The default algorithms.
|
|
26
38
|
* @readonly This property is readonly and cannot be changed.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @summary Utilities for PKCE (Proof Key for Code Exchange).
|
|
3
|
+
*/
|
|
4
|
+
export declare class PKCEUtils {
|
|
5
|
+
/**
|
|
6
|
+
* @summary Generate a random string for state or code verifier.
|
|
7
|
+
* @param length - The length of the string.
|
|
8
|
+
* @returns The generated string.
|
|
9
|
+
*/
|
|
10
|
+
static generateRandomString(length?: number): string;
|
|
11
|
+
/**
|
|
12
|
+
* @summary Generate the code challenge from the code verifier.
|
|
13
|
+
* @param codeVerifier - The code verifier.
|
|
14
|
+
* @returns A promise that resolves to the code challenge.
|
|
15
|
+
*/
|
|
16
|
+
static generateCodeChallenge(codeVerifier: string): Promise<string>;
|
|
17
|
+
/**
|
|
18
|
+
* @summary Base64 URL encode a buffer.
|
|
19
|
+
* @param buffer - The buffer to encode.
|
|
20
|
+
* @returns The base64 URL encoded string.
|
|
21
|
+
*/
|
|
22
|
+
private static base64URLEncode;
|
|
23
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PKCEUtils = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @summary Utilities for PKCE (Proof Key for Code Exchange).
|
|
6
|
+
*/
|
|
7
|
+
class PKCEUtils {
|
|
8
|
+
/**
|
|
9
|
+
* @summary Generate a random string for state or code verifier.
|
|
10
|
+
* @param length - The length of the string.
|
|
11
|
+
* @returns The generated string.
|
|
12
|
+
*/
|
|
13
|
+
static generateRandomString(length = 43) {
|
|
14
|
+
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
15
|
+
let result = "";
|
|
16
|
+
const randomValues = new Uint8Array(length);
|
|
17
|
+
if (typeof window !== "undefined" && window.crypto) {
|
|
18
|
+
window.crypto.getRandomValues(randomValues);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
for (let i = 0; i < length; i++) {
|
|
22
|
+
randomValues[i] = Math.floor(Math.random() * 256);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (let i = 0; i < length; i++) {
|
|
26
|
+
result += charset[randomValues[i] % charset.length];
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @summary Generate the code challenge from the code verifier.
|
|
32
|
+
* @param codeVerifier - The code verifier.
|
|
33
|
+
* @returns A promise that resolves to the code challenge.
|
|
34
|
+
*/
|
|
35
|
+
static async generateCodeChallenge(codeVerifier) {
|
|
36
|
+
const encoder = new TextEncoder();
|
|
37
|
+
const data = encoder.encode(codeVerifier);
|
|
38
|
+
if (typeof window !== "undefined" && window.crypto && window.crypto.subtle) {
|
|
39
|
+
const hashBuffer = await window.crypto.subtle.digest("SHA-256", data);
|
|
40
|
+
return this.base64URLEncode(hashBuffer);
|
|
41
|
+
}
|
|
42
|
+
// TODO: Add a fallback for non-browser environments. (crypto module)
|
|
43
|
+
throw new Error("Crypto API is not available in this environment.");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @summary Base64 URL encode a buffer.
|
|
47
|
+
* @param buffer - The buffer to encode.
|
|
48
|
+
* @returns The base64 URL encoded string.
|
|
49
|
+
*/
|
|
50
|
+
static base64URLEncode(buffer) {
|
|
51
|
+
const bytes = new Uint8Array(buffer);
|
|
52
|
+
let binary = "";
|
|
53
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
54
|
+
binary += String.fromCharCode(bytes[i]);
|
|
55
|
+
}
|
|
56
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.PKCEUtils = PKCEUtils;
|
package/dist/index.d.ts
CHANGED
|
@@ -21,3 +21,6 @@ export * from "./models/builders/process-errors/tokens/AuthlyTokenInvalidError";
|
|
|
21
21
|
export * from "./models/globals/clients/interfaces/IAuthlyClientOptions";
|
|
22
22
|
export * from "./models/globals/clients/interfaces/IAuthorizeUrlOptions";
|
|
23
23
|
export * from "./models/globals/clients/interfaces/IDecodedTokenClaim";
|
|
24
|
+
export * from "./models/globals/clients/interfaces/ITokenResponse";
|
|
25
|
+
export * from "./models/globals/clients/interfaces/IAuthlyStorage";
|
|
26
|
+
export * from "./models/globals/clients/interfaces/IUserProfile";
|
package/dist/index.js
CHANGED
|
@@ -37,3 +37,6 @@ __exportStar(require("./models/builders/process-errors/tokens/AuthlyTokenInvalid
|
|
|
37
37
|
__exportStar(require("./models/globals/clients/interfaces/IAuthlyClientOptions"), exports);
|
|
38
38
|
__exportStar(require("./models/globals/clients/interfaces/IAuthorizeUrlOptions"), exports);
|
|
39
39
|
__exportStar(require("./models/globals/clients/interfaces/IDecodedTokenClaim"), exports);
|
|
40
|
+
__exportStar(require("./models/globals/clients/interfaces/ITokenResponse"), exports);
|
|
41
|
+
__exportStar(require("./models/globals/clients/interfaces/IAuthlyStorage"), exports);
|
|
42
|
+
__exportStar(require("./models/globals/clients/interfaces/IUserProfile"), exports);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IAuthlyStorage } from "./IAuthlyStorage";
|
|
1
2
|
/**
|
|
2
3
|
* @summary Options for initializing the AuthlyClient.
|
|
3
4
|
*/
|
|
@@ -17,6 +18,16 @@ interface IAuthlyClientOptions {
|
|
|
17
18
|
* @example "1234567890"
|
|
18
19
|
*/
|
|
19
20
|
readonly serviceId: string;
|
|
21
|
+
/**
|
|
22
|
+
* @summary The redirect URI to use for the authorization flow.
|
|
23
|
+
* @example "https://app.example.com/api/auth/callback"
|
|
24
|
+
*/
|
|
25
|
+
readonly redirectUri?: string;
|
|
26
|
+
/**
|
|
27
|
+
* @summary The storage implementation to use for PKCE state and verifier.
|
|
28
|
+
* @default localStorage (in browser)
|
|
29
|
+
*/
|
|
30
|
+
readonly storage?: IAuthlyStorage;
|
|
20
31
|
/**
|
|
21
32
|
* @summary The path to the JWKS endpoint relative to the issuer.
|
|
22
33
|
* @example "/.well-known/jwks.json"
|
|
@@ -29,6 +40,18 @@ interface IAuthlyClientOptions {
|
|
|
29
40
|
* @default "/authorize"
|
|
30
41
|
*/
|
|
31
42
|
readonly authorizePath?: string;
|
|
43
|
+
/**
|
|
44
|
+
* @summary The path to the token endpoint relative to the issuer.
|
|
45
|
+
* @example "/v1/oauth/token"
|
|
46
|
+
* @default "/oauth/token"
|
|
47
|
+
*/
|
|
48
|
+
readonly tokenPath?: string;
|
|
49
|
+
/**
|
|
50
|
+
* @summary The path to the user info endpoint relative to the issuer.
|
|
51
|
+
* @example "/v1/oauth/userinfo"
|
|
52
|
+
* @default "/v1/oauth/userinfo"
|
|
53
|
+
*/
|
|
54
|
+
readonly userInfoPath?: string;
|
|
32
55
|
/**
|
|
33
56
|
* @summary A list of allowed signing algorithms.
|
|
34
57
|
* @example ["RS256"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @summary Interface for storage used by AuthlyClient.
|
|
3
|
+
*/
|
|
4
|
+
export interface IAuthlyStorage {
|
|
5
|
+
/**
|
|
6
|
+
* @summary Retrieve an item from storage.
|
|
7
|
+
* @param _key - The key of the item to retrieve.
|
|
8
|
+
* @returns The value of the item, or null if not found.
|
|
9
|
+
*/
|
|
10
|
+
getItem(_key: string): string | null | Promise<string | null>;
|
|
11
|
+
/**
|
|
12
|
+
* @summary Add key and value to storage.
|
|
13
|
+
* @param _key - The key of the item to add.
|
|
14
|
+
* @param _value - The value of the item to add.
|
|
15
|
+
*/
|
|
16
|
+
setItem(_key: string, _value: string): void | Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* @summary Remove an item from storage.
|
|
19
|
+
* @param _key - The key of the item to remove.
|
|
20
|
+
*/
|
|
21
|
+
removeItem(_key: string): void | Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @summary Interface for the token response.
|
|
3
|
+
*/
|
|
4
|
+
export interface ITokenResponse {
|
|
5
|
+
/**
|
|
6
|
+
* @summary The access token.
|
|
7
|
+
*/
|
|
8
|
+
access_token: string;
|
|
9
|
+
/**
|
|
10
|
+
* @summary The token type.
|
|
11
|
+
*/
|
|
12
|
+
token_type: string;
|
|
13
|
+
/**
|
|
14
|
+
* @summary The expiration time in seconds.
|
|
15
|
+
*/
|
|
16
|
+
expires_in: number;
|
|
17
|
+
/**
|
|
18
|
+
* @summary The refresh token.
|
|
19
|
+
*/
|
|
20
|
+
refresh_token?: string;
|
|
21
|
+
/**
|
|
22
|
+
* @summary The ID token.
|
|
23
|
+
*/
|
|
24
|
+
id_token?: string;
|
|
25
|
+
/**
|
|
26
|
+
* @summary The scope of the token.
|
|
27
|
+
*/
|
|
28
|
+
scope?: string;
|
|
29
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @summary Represents the user profile information returned by Authly.
|
|
3
|
+
*/
|
|
4
|
+
export interface IUserProfile {
|
|
5
|
+
/**
|
|
6
|
+
* @summary The unique identifier for the user.
|
|
7
|
+
*/
|
|
8
|
+
sub: string;
|
|
9
|
+
/**
|
|
10
|
+
* @summary The user's email address.
|
|
11
|
+
*/
|
|
12
|
+
email?: string;
|
|
13
|
+
/**
|
|
14
|
+
* @summary Whether the user's email address has been verified.
|
|
15
|
+
*/
|
|
16
|
+
email_verified?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* @summary The user's full name.
|
|
19
|
+
*/
|
|
20
|
+
name?: string;
|
|
21
|
+
/**
|
|
22
|
+
* @summary The user's given (first) name.
|
|
23
|
+
*/
|
|
24
|
+
given_name?: string;
|
|
25
|
+
/**
|
|
26
|
+
* @summary The user's family (last) name.
|
|
27
|
+
*/
|
|
28
|
+
family_name?: string;
|
|
29
|
+
/**
|
|
30
|
+
* @summary The user's preferred username.
|
|
31
|
+
*/
|
|
32
|
+
preferred_username?: string;
|
|
33
|
+
/**
|
|
34
|
+
* @summary The user's permissions in Authly.
|
|
35
|
+
* @description A record where keys are resource names and values are bitmask integers.
|
|
36
|
+
*/
|
|
37
|
+
permissions?: Record<string, number>;
|
|
38
|
+
/**
|
|
39
|
+
* @summary Additional custom claims.
|
|
40
|
+
*/
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface AuthlyCallbackProps {
|
|
3
|
+
/**
|
|
4
|
+
* @summary The URL to redirect to after successful login.
|
|
5
|
+
* @default "/"
|
|
6
|
+
*/
|
|
7
|
+
onSuccess?: string;
|
|
8
|
+
/**
|
|
9
|
+
* @summary The URL to redirect to after failed login.
|
|
10
|
+
* @default "/login"
|
|
11
|
+
*/
|
|
12
|
+
onFailure?: string;
|
|
13
|
+
/**
|
|
14
|
+
* @summary Optional custom navigation function (e.g. router.push from Next.js or navigate from React Router).
|
|
15
|
+
* @description If not provided, window.location.href will be used.
|
|
16
|
+
*/
|
|
17
|
+
navigate?: (path: string) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare const AuthlyCallback: React.FC<AuthlyCallbackProps>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthlyCallback = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const AuthlyContext_1 = require("./AuthlyContext");
|
|
7
|
+
const AuthlyCallback = ({ onSuccess = "/", onFailure = "/login", navigate }) => {
|
|
8
|
+
const { client, refresh } = (0, AuthlyContext_1.useAuthly)();
|
|
9
|
+
const processedRef = (0, react_1.useRef)(false);
|
|
10
|
+
(0, react_1.useEffect)(() => {
|
|
11
|
+
const handleCallback = async () => {
|
|
12
|
+
if (processedRef.current)
|
|
13
|
+
return;
|
|
14
|
+
processedRef.current = true;
|
|
15
|
+
const params = new URLSearchParams(window.location.search);
|
|
16
|
+
const code = params.get("code");
|
|
17
|
+
const state = params.get("state");
|
|
18
|
+
if (!code || !state) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
await client.exchangeToken(params);
|
|
23
|
+
await refresh();
|
|
24
|
+
if (navigate) {
|
|
25
|
+
navigate(onSuccess);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
window.location.href = onSuccess;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error("Authly callback failed:", error);
|
|
33
|
+
if (navigate) {
|
|
34
|
+
navigate(onFailure);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
window.location.href = onFailure;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
handleCallback();
|
|
42
|
+
}, [client, onSuccess, onFailure, navigate, refresh]);
|
|
43
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: { display: "flex", justifyContent: "center", alignItems: "center", height: "100vh" }, children: (0, jsx_runtime_1.jsx)("p", { children: "Authenticating..." }) }));
|
|
44
|
+
};
|
|
45
|
+
exports.AuthlyCallback = AuthlyCallback;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AuthlyClient } from "../globals/clients/AuthlyClient";
|
|
2
|
+
import type { IUserProfile } from "../models/globals/clients/interfaces/IUserProfile";
|
|
3
|
+
export interface IAuthlyContext {
|
|
4
|
+
isAuthenticated: boolean;
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
user: IUserProfile | null;
|
|
7
|
+
login: (options?: Parameters<AuthlyClient["authorize"]>[0]) => Promise<void>;
|
|
8
|
+
logout: () => Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* @summary Manually refresh the session state (e.g. after callback).
|
|
11
|
+
*/
|
|
12
|
+
refresh: () => Promise<void>;
|
|
13
|
+
client: AuthlyClient;
|
|
14
|
+
}
|
|
15
|
+
export declare const AuthlyContext: import("react").Context<IAuthlyContext | undefined>;
|
|
16
|
+
export declare const useAuthly: () => IAuthlyContext;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.useAuthly = exports.AuthlyContext = void 0;
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
exports.AuthlyContext = (0, react_1.createContext)(undefined);
|
|
7
|
+
const useAuthly = () => {
|
|
8
|
+
const context = (0, react_1.useContext)(exports.AuthlyContext);
|
|
9
|
+
if (!context) {
|
|
10
|
+
throw new Error("useAuthly must be used within an AuthlyProvider");
|
|
11
|
+
}
|
|
12
|
+
return context;
|
|
13
|
+
};
|
|
14
|
+
exports.useAuthly = useAuthly;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
import type { AuthlyClient } from "../globals/clients/AuthlyClient";
|
|
3
|
+
interface AuthlyProviderProps {
|
|
4
|
+
client: AuthlyClient;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
export declare const AuthlyProvider: React.FC<AuthlyProviderProps>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.AuthlyProvider = void 0;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const AuthlyContext_1 = require("./AuthlyContext");
|
|
8
|
+
const AuthlyProvider = ({ client, children }) => {
|
|
9
|
+
const [isAuthenticated, setIsAuthenticated] = (0, react_1.useState)(false);
|
|
10
|
+
const [isLoading, setIsLoading] = (0, react_1.useState)(true);
|
|
11
|
+
const [user, setUser] = (0, react_1.useState)(null);
|
|
12
|
+
const checkSession = (0, react_1.useCallback)(async () => {
|
|
13
|
+
try {
|
|
14
|
+
const authenticated = await client.isAuthenticated();
|
|
15
|
+
setIsAuthenticated(authenticated);
|
|
16
|
+
if (authenticated) {
|
|
17
|
+
const profile = await client.getUser();
|
|
18
|
+
setUser(profile);
|
|
19
|
+
if (!profile) {
|
|
20
|
+
setIsAuthenticated(false);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
setUser(null);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error("Authly session check failed:", error);
|
|
29
|
+
setIsAuthenticated(false);
|
|
30
|
+
setUser(null);
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
setIsLoading(false);
|
|
34
|
+
}
|
|
35
|
+
}, [client]);
|
|
36
|
+
(0, react_1.useEffect)(() => {
|
|
37
|
+
checkSession();
|
|
38
|
+
}, [checkSession]);
|
|
39
|
+
const login = (0, react_1.useCallback)(async (options) => {
|
|
40
|
+
const url = await client.authorize(options);
|
|
41
|
+
if (typeof window !== "undefined") {
|
|
42
|
+
window.location.href = url;
|
|
43
|
+
}
|
|
44
|
+
}, [client]);
|
|
45
|
+
const logout = (0, react_1.useCallback)(async () => {
|
|
46
|
+
await client.logout();
|
|
47
|
+
setIsAuthenticated(false);
|
|
48
|
+
setUser(null);
|
|
49
|
+
if (typeof window !== "undefined") {
|
|
50
|
+
}
|
|
51
|
+
}, [client]);
|
|
52
|
+
return ((0, jsx_runtime_1.jsx)(AuthlyContext_1.AuthlyContext.Provider, { value: { isAuthenticated, isLoading, user, login, logout, refresh: checkSession, client }, children: children }));
|
|
53
|
+
};
|
|
54
|
+
exports.AuthlyProvider = AuthlyProvider;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./AuthlyContext"), exports);
|
|
18
|
+
__exportStar(require("./AuthlyProvider"), exports);
|
|
19
|
+
__exportStar(require("./AuthlyCallback"), exports);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@authly/sdk",
|
|
3
3
|
"description": "A library for building authentication systems using Authly.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Anvoria",
|
|
7
7
|
"url": "https://github.com/Anvoria"
|
|
@@ -27,6 +27,16 @@
|
|
|
27
27
|
},
|
|
28
28
|
"main": "dist/index.js",
|
|
29
29
|
"types": "dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./react": {
|
|
36
|
+
"types": "./dist/react/index.d.ts",
|
|
37
|
+
"default": "./dist/react/index.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
30
40
|
"files": [
|
|
31
41
|
"dist"
|
|
32
42
|
],
|
|
@@ -57,18 +67,31 @@
|
|
|
57
67
|
"@eslint/json": "^0.14.0",
|
|
58
68
|
"@eslint/markdown": "^7.5.1",
|
|
59
69
|
"@types/bun": "^1.3.4",
|
|
60
|
-
"@types/node": "^25.0.
|
|
70
|
+
"@types/node": "^25.0.9",
|
|
71
|
+
"@types/react": "^19.2.8",
|
|
61
72
|
"eslint": "^9.39.2",
|
|
62
73
|
"globals": "^17.0.0",
|
|
63
74
|
"husky": "^9.1.7",
|
|
64
75
|
"jiti": "^2.6.1",
|
|
65
76
|
"lint-staged": "^16.2.7",
|
|
66
77
|
"prettier": "^3.7.4",
|
|
78
|
+
"react-dom": "^19.2.3",
|
|
67
79
|
"tsx": "^4.21.0",
|
|
68
80
|
"typescript": "^5.9.3",
|
|
69
81
|
"typescript-eslint": "^8.50.0"
|
|
70
82
|
},
|
|
71
83
|
"dependencies": {
|
|
72
84
|
"jose": "^6.1.3"
|
|
85
|
+
},
|
|
86
|
+
"peerDependencies": {
|
|
87
|
+
"react": ""
|
|
88
|
+
},
|
|
89
|
+
"peerDependenciesMeta": {
|
|
90
|
+
"react": {
|
|
91
|
+
"optional": true
|
|
92
|
+
},
|
|
93
|
+
"next": {
|
|
94
|
+
"optional": true
|
|
95
|
+
}
|
|
73
96
|
}
|
|
74
97
|
}
|