@schemavaults/auth-client-sdk 0.5.4 → 0.7.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/auth-client.d.ts +1 -1
- package/dist/auth-client.js +65 -502
- package/dist/auth-client.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/assert-http-only-refresh-token-has-accompanying-expiry-marker.d.ts +2 -0
- package/dist/lib/assert-http-only-refresh-token-has-accompanying-expiry-marker.js +9 -0
- package/dist/lib/assert-http-only-refresh-token-has-accompanying-expiry-marker.js.map +1 -0
- package/dist/lib/authenticate-url-encoder.js +12 -1
- package/dist/lib/authenticate-url-encoder.js.map +1 -1
- package/dist/lib/authenticate-with-redirect.d.ts +13 -0
- package/dist/lib/authenticate-with-redirect.js +108 -0
- package/dist/lib/authenticate-with-redirect.js.map +1 -0
- package/dist/lib/check-if-authenticated-with-server.d.ts +8 -0
- package/dist/lib/check-if-authenticated-with-server.js +37 -0
- package/dist/lib/check-if-authenticated-with-server.js.map +1 -0
- package/dist/lib/exchange-auth-tokens.d.ts +16 -0
- package/dist/lib/exchange-auth-tokens.js +121 -0
- package/dist/lib/exchange-auth-tokens.js.map +1 -0
- package/dist/lib/handle-successful-authentication.d.ts +21 -0
- package/dist/lib/handle-successful-authentication.js +259 -0
- package/dist/lib/handle-successful-authentication.js.map +1 -0
- package/dist/lib/send-authenticate-request.js +15 -7
- package/dist/lib/send-authenticate-request.js.map +1 -1
- package/dist/types/IAuthClientConstructorOptions.d.ts +1 -1
- package/dist/types/ISchemaVaultsAuthClient.d.ts +9 -0
- package/dist/types/ISchemaVaultsAuthClientAdapter.d.ts +2 -6
- package/package.json +3 -3
package/dist/auth-client.d.ts
CHANGED
|
@@ -113,7 +113,6 @@ export declare class SchemaVaultsAuthClient extends EventTarget implements ISche
|
|
|
113
113
|
* @returns `UserData` | `null`
|
|
114
114
|
*/
|
|
115
115
|
get currentUser(): UserData | null;
|
|
116
|
-
private assertHttpOnlyRefreshTokenCookieHasAccompanyingMarkerCookie;
|
|
117
116
|
private handleSuccessfulExchangeAuthTokensResponse;
|
|
118
117
|
private exchangeAuthTokens;
|
|
119
118
|
private uuid;
|
|
@@ -126,4 +125,5 @@ export declare class SchemaVaultsAuthClient extends EventTarget implements ISche
|
|
|
126
125
|
*/
|
|
127
126
|
get successful_logout_redirect_uri(): string | undefined;
|
|
128
127
|
supports(feature_name: string): boolean;
|
|
128
|
+
checkIfAuthenticatedWithServer(): Promise<UserData | null>;
|
|
129
129
|
}
|
package/dist/auth-client.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// auth-client.ts
|
|
2
|
+
// @schemavaults/auth-client-sdk
|
|
3
|
+
import { PKCE_ProofKeyManager, requestTokensResultSchema, audienceSchema, } from "@schemavaults/auth-common";
|
|
3
4
|
import { sendAuthenticateRequest } from "./lib/send-authenticate-request";
|
|
4
5
|
import { appIdSchema, SCHEMAVAULTS_AUTH_APP_DEFINITION, schemaVaultsAppEnvironmentSchema, } from "@schemavaults/app-definitions";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
6
|
+
import authenticateWithRedirect from "./lib/authenticate-with-redirect";
|
|
7
|
+
import checkIfAuthenticatedWithServer from "./lib/check-if-authenticated-with-server";
|
|
8
|
+
import exchangeAuthTokens from "./lib/exchange-auth-tokens";
|
|
9
|
+
import assertHttpOnlyRefreshTokenCookieHasAccompanyingMarkerCookie from "./lib/assert-http-only-refresh-token-has-accompanying-expiry-marker";
|
|
10
|
+
import handleSuccessfulAuthentication from "./lib/handle-successful-authentication";
|
|
7
11
|
/**
|
|
8
12
|
* The SchemaVaultsAuthClient is a client SDK for the SchemaVaults Auth Server
|
|
9
13
|
* It is used to authenticate users, store tokens, and manage user data
|
|
@@ -238,105 +242,26 @@ export class SchemaVaultsAuthClient extends EventTarget {
|
|
|
238
242
|
return code_challenge;
|
|
239
243
|
}
|
|
240
244
|
async authenticateWithRedirect(type) {
|
|
241
|
-
if (this.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
else if (typeof code_verifier.challenge_time !== "number") {
|
|
258
|
-
throw new TypeError("Expected generated 'code_verifier.challenge_time' to be a number!");
|
|
259
|
-
}
|
|
260
|
-
else if (typeof code_verifier.code_verifier !== "string") {
|
|
261
|
-
throw new TypeError("Expected generated 'code_verifier.code_verifier' to be a string!");
|
|
262
|
-
}
|
|
263
|
-
// This is sent to the auth server-- it's a hash of the code verifier
|
|
264
|
-
// https://datatracker.ietf.org/doc/html/rfc7636#section-4.2
|
|
265
|
-
let code_challenge;
|
|
266
|
-
try {
|
|
267
|
-
const new_code_challenge = await PKCE_ProofKeyManager.createCodeChallenge(code_verifier);
|
|
268
|
-
code_challenge = new_code_challenge;
|
|
269
|
-
}
|
|
270
|
-
catch (e) {
|
|
271
|
-
console.error("Auth client failed to create code challenge from code verifier object: ", e);
|
|
272
|
-
const errMsg = e instanceof Error && typeof e.message === "string"
|
|
273
|
-
? e.message
|
|
274
|
-
: "Auth client encountered an unknown error while creating code challenge";
|
|
275
|
-
throw new Error(`Auth client failed to create code challenge from code verifier object (error: ${errMsg})`);
|
|
276
|
-
}
|
|
277
|
-
// Validate code challenge a bit
|
|
278
|
-
if (typeof code_challenge.challenge_time !== "number") {
|
|
279
|
-
throw new Error("Expected challenge_time to be set (from input code_verifier)");
|
|
280
|
-
}
|
|
281
|
-
else if (typeof code_challenge.code_challenge_method !== "string" ||
|
|
282
|
-
code_challenge.code_challenge_method !== "S256") {
|
|
283
|
-
throw new Error("Expected code_challenge_method to be set to S256");
|
|
284
|
-
}
|
|
285
|
-
else if (typeof code_challenge.code_challenge !== "string") {
|
|
286
|
-
throw new Error("Expected code_challenge to be set");
|
|
287
|
-
}
|
|
288
|
-
// Store the code verifier in a secure location
|
|
289
|
-
try {
|
|
290
|
-
this.storeCodeVerifier(code_verifier.code_verifier, code_challenge.challenge_time);
|
|
291
|
-
}
|
|
292
|
-
catch (e) {
|
|
293
|
-
console.error("Failed to store code verifier: ", e);
|
|
294
|
-
throw new Error("Failed to store code verifier!");
|
|
295
|
-
}
|
|
296
|
-
// If the authentication is successful, the auth server will redirect the user back to the client
|
|
297
|
-
// and the code_verifier will be used to prove that the client initiating the flow is the same as the client that the authorization server issued the code to
|
|
298
|
-
const app_id = this.app_id;
|
|
299
|
-
if (!app_id) {
|
|
300
|
-
console.error("App ID not set, but required for PKCE flow");
|
|
301
|
-
throw new Error("App ID not set, but required for PKCE flow");
|
|
302
|
-
}
|
|
303
|
-
// The user is about to be redirected to auth server. Where should they be redirected back to this app? (for PKCE flow)
|
|
304
|
-
const redirect_uri = this.authorize_uri;
|
|
305
|
-
if (typeof redirect_uri !== "string") {
|
|
306
|
-
throw new Error("A URL to redirect to when authentication is successful was not provided. Required for PKCE flow.");
|
|
307
|
-
}
|
|
308
|
-
if (this.DEBUG) {
|
|
309
|
-
console.log("[SchemaVaultsAuthClient] Attempting to build URL to open from client in order to start OAuth 2.0 PKCE flow with SchemaVaults Auth Server...");
|
|
310
|
-
}
|
|
311
|
-
// Redirect the user to the auth server
|
|
312
|
-
let authenticate_url;
|
|
313
|
-
try {
|
|
314
|
-
authenticate_url = AuthenticateURLEncoder.encode({
|
|
315
|
-
type,
|
|
316
|
-
code_challenge: code_challenge,
|
|
317
|
-
redirect_uri,
|
|
318
|
-
app_id,
|
|
319
|
-
auth_server_uri: this._authServerUri,
|
|
320
|
-
app_env: this.environment,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
catch (e) {
|
|
324
|
-
console.error("Failed to build authenticate URL: ", e);
|
|
325
|
-
throw new Error("Failed to build authenticate URL (i.e. where to login/register url not found)");
|
|
326
|
-
}
|
|
327
|
-
if (this.DEBUG) {
|
|
328
|
-
console.log("[SchemaVaultsAuthClient] Redirecting to authenticate URL: ", authenticate_url);
|
|
329
|
-
}
|
|
330
|
-
try {
|
|
331
|
-
await this.adapter.redirect(authenticate_url);
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
catch (e) {
|
|
335
|
-
console.error("Failed to redirect to authentication server using client adapter: ", e);
|
|
336
|
-
throw new Error("Failed to redirect to authentication server");
|
|
337
|
-
}
|
|
245
|
+
if (!this.authorize_uri ||
|
|
246
|
+
typeof this.authorize_uri !== "string" ||
|
|
247
|
+
this.authorize_uri.length === 0) {
|
|
248
|
+
throw new TypeError("Failed to resolve 'authorize_uri'!");
|
|
249
|
+
}
|
|
250
|
+
return await authenticateWithRedirect({
|
|
251
|
+
type,
|
|
252
|
+
adapter: this.adapter,
|
|
253
|
+
auth_server_uri: this._authServerUri,
|
|
254
|
+
client_app_id: this._app_id,
|
|
255
|
+
storeCodeVerifier: this.storeCodeVerifier.bind(this),
|
|
256
|
+
environment: this.environment,
|
|
257
|
+
authorize_uri: this.authorize_uri,
|
|
258
|
+
debug: this.debug,
|
|
259
|
+
});
|
|
338
260
|
}
|
|
339
261
|
async login() {
|
|
262
|
+
if (this.isClientForAuthServer) {
|
|
263
|
+
return await this.adapter.redirect("/auth/login");
|
|
264
|
+
}
|
|
340
265
|
if (this.DEBUG) {
|
|
341
266
|
console.log("[SchemaVaultsAuthClient] Attempting to sign in with redirect...");
|
|
342
267
|
}
|
|
@@ -351,6 +276,9 @@ export class SchemaVaultsAuthClient extends EventTarget {
|
|
|
351
276
|
}
|
|
352
277
|
} // login()
|
|
353
278
|
async register() {
|
|
279
|
+
if (this.isClientForAuthServer) {
|
|
280
|
+
return await this.adapter.redirect("/auth/register");
|
|
281
|
+
}
|
|
354
282
|
if (this.DEBUG) {
|
|
355
283
|
console.log("[SchemaVaultsAuthClient] Attempting to register with redirect...");
|
|
356
284
|
}
|
|
@@ -436,257 +364,22 @@ export class SchemaVaultsAuthClient extends EventTarget {
|
|
|
436
364
|
return codeVerifiers;
|
|
437
365
|
} // loadSavedAuthorizationCodeVerifiers()
|
|
438
366
|
async handleSuccessfulAuthentication(authorization_code, challenge_time, code_verifier) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}
|
|
455
|
-
const time_elapsed_since_challenge_time = Date.now() - challenge_time;
|
|
456
|
-
if (time_elapsed_since_challenge_time <= 0) {
|
|
457
|
-
throw new Error("Expected challenge time to be in the past");
|
|
458
|
-
}
|
|
459
|
-
if (time_elapsed_since_challenge_time > PKCE_ProofKeyManager.max_age) {
|
|
460
|
-
console.error("[SchemaVaultsAuthClient::handleSuccessfulAuthentication] Code verifier has expired based on challenge time");
|
|
461
|
-
if (this.debug) {
|
|
462
|
-
try {
|
|
463
|
-
console.table({
|
|
464
|
-
challenge_time,
|
|
465
|
-
current_time: Date.now(),
|
|
466
|
-
time_elapsed: time_elapsed_since_challenge_time,
|
|
467
|
-
max_age: PKCE_ProofKeyManager.max_age,
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
catch (e) {
|
|
471
|
-
void e; /** no-op */
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
throw new Error("Code verifier has expired");
|
|
475
|
-
}
|
|
476
|
-
// The auth server will redirect the user back to the client
|
|
477
|
-
// The client will have a code in the query parameters
|
|
478
|
-
// The client will use the code to get an access token
|
|
479
|
-
// PKCE: The client will use the code_verifier to prove that it is the same client
|
|
480
|
-
if (debug) {
|
|
481
|
-
console.log("[SchemaVaultsAuthClient] " +
|
|
482
|
-
"Attempting to load code verifier to prove authorization code validity...");
|
|
483
|
-
}
|
|
484
|
-
const cached_code_verifier = code_verifier ?? this.loadCodeVerifier(challenge_time);
|
|
485
|
-
if (!cached_code_verifier) {
|
|
486
|
-
const errorMessage = `[SchemaVaultsAuthClient] Failed to load code_verifier at challenge_time=${challenge_time}`;
|
|
487
|
-
console.error(errorMessage);
|
|
488
|
-
throw new Error(errorMessage);
|
|
489
|
-
}
|
|
490
|
-
cached_code_verifier;
|
|
491
|
-
const shouldClearCodeVerifierAfterLoad = this.environment !== "development";
|
|
492
|
-
if (shouldClearCodeVerifierAfterLoad) {
|
|
493
|
-
// Clear the code verifier from storage
|
|
494
|
-
try {
|
|
495
|
-
if (debug) {
|
|
496
|
-
console.log("[SchemaVaultsAuthClient] " +
|
|
497
|
-
"Code verifier was retrieved from storage, now clearing code verifier at challenge time: ", challenge_time);
|
|
498
|
-
}
|
|
499
|
-
this.adapter.clearCodeVerifier(challenge_time);
|
|
500
|
-
if (debug) {
|
|
501
|
-
console.log("[SchemaVaultsAuthClient] Cleared code verifiers from storage");
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
catch (e) {
|
|
505
|
-
console.error("[SchemaVaultsAuthClient] Failed to clear code verifiers: ", e);
|
|
506
|
-
if (debug) {
|
|
507
|
-
throw new Error("Failed to clear code verifiers");
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
else {
|
|
512
|
-
if (debug) {
|
|
513
|
-
console.log("[SchemaVaultsAuthClient] Not attempting to clear code verifiers in this app environment...");
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
// Get the endpoint to exchange the authorization code for an access token
|
|
517
|
-
// https://datatracker.ietf.org/doc/html/rfc7636#section-4.5
|
|
518
|
-
const token_endpoint = `${this.auth_server_uri}/api/auth/token/authorization_code`;
|
|
519
|
-
if (debug) {
|
|
520
|
-
console.log("[SchemaVaultsAuthClient::handleSuccessfulAuthentication()] Token Endpoint: ", token_endpoint);
|
|
521
|
-
}
|
|
522
|
-
const client_app_id = this.app_id;
|
|
523
|
-
if (debug) {
|
|
524
|
-
console.log("[SchemaVaultsAuthClient::handleSuccessfulAuthentication()] Client App ID: ", client_app_id);
|
|
525
|
-
}
|
|
526
|
-
let audience = this.defaultTokenAudiences;
|
|
527
|
-
if (debug) {
|
|
528
|
-
console.log("[SchemaVaultsAuthClient::handleSuccessfulAuthentication()] Initial access token audience(s): ", audience);
|
|
529
|
-
}
|
|
530
|
-
if (!audience || (Array.isArray(audience) && audience.length === 0)) {
|
|
531
|
-
console.warn("[SchemaVaultsAuthClient::handleSuccessfulAuthentication()] No access token audience(s) set");
|
|
532
|
-
audience = [];
|
|
533
|
-
}
|
|
534
|
-
// Exchange the authorization code for an access token
|
|
535
|
-
let request_body;
|
|
536
|
-
try {
|
|
537
|
-
const parsed = await authorizationCodePOSTbody.safeParseAsync({
|
|
538
|
-
grant_type: "authorization_code",
|
|
539
|
-
code: authorization_code,
|
|
540
|
-
code_verifier: cached_code_verifier,
|
|
541
|
-
client_app_id,
|
|
542
|
-
audience,
|
|
543
|
-
challenge_time,
|
|
544
|
-
});
|
|
545
|
-
if (!parsed.success)
|
|
546
|
-
throw parsed.error;
|
|
547
|
-
request_body = parsed.data;
|
|
548
|
-
}
|
|
549
|
-
catch (e) {
|
|
550
|
-
console.error(e);
|
|
551
|
-
throw new Error("Failed to prepare request body for authorization grant request");
|
|
552
|
-
}
|
|
553
|
-
// Send the request to the auth server
|
|
554
|
-
// The auth server will hash the code_verifier and compare it to the code_challenge
|
|
555
|
-
let response;
|
|
556
|
-
try {
|
|
557
|
-
if (this.debug) {
|
|
558
|
-
console.log(`[SchemaVaultsAuthClient] Exchanging authorization code for access token; sending req body to token endpoint: "${token_endpoint}"`, request_body);
|
|
559
|
-
}
|
|
560
|
-
response = await this.adapter.sendPOSTRequest(token_endpoint, request_body, {});
|
|
561
|
-
if (this.debug) {
|
|
562
|
-
console.log("[SchemaVaultsAuthClient] Received response in attempt to exchange authorization code for access token: ", response);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
catch (e) {
|
|
566
|
-
console.error("Failed to exchange authorization code for access token:", e);
|
|
567
|
-
throw new Error("Failed to exchange authorization code for access token");
|
|
568
|
-
}
|
|
569
|
-
if (!response || !response.ok || response.status !== 200) {
|
|
570
|
-
const errorMsg = "Failed to exchange authorization code for access token";
|
|
571
|
-
console.error(errorMsg);
|
|
572
|
-
throw new Error(errorMsg);
|
|
573
|
-
}
|
|
574
|
-
if (this.DEBUG) {
|
|
575
|
-
console.log("[SchemaVaultsAuthClient::handleSuccessfulAuthentication()] " +
|
|
576
|
-
"Successfully exchanged authorization code for token(s)");
|
|
577
|
-
}
|
|
578
|
-
let access_tokens;
|
|
579
|
-
let refresh_token;
|
|
580
|
-
let user;
|
|
581
|
-
try {
|
|
582
|
-
const tokens_data = await requestTokensResultSchema.safeParseAsync(response.data);
|
|
583
|
-
if (!tokens_data.success) {
|
|
584
|
-
console.error("[SchemaVaultsAuthClient::handleSuccessfulAuthentication()] " +
|
|
585
|
-
"Failed to parse tokens from auth server response:", tokens_data.error);
|
|
586
|
-
throw new Error("Failed to parse tokens from auth server response");
|
|
587
|
-
}
|
|
588
|
-
else if (!tokens_data.data.success) {
|
|
589
|
-
throw new Error(tokens_data.data.message);
|
|
590
|
-
}
|
|
591
|
-
if (this.DEBUG) {
|
|
592
|
-
console.log("[SchemaVaultsAuthClient::handleSuccessfulAuthentication()] Success response data: ", tokens_data.data);
|
|
593
|
-
}
|
|
594
|
-
const { tokens, userData } = tokens_data.data;
|
|
595
|
-
if (!tokens) {
|
|
596
|
-
console.error("Did not receive tokens in response from auth server");
|
|
597
|
-
throw new Error("Did not receive tokens in response from auth server");
|
|
598
|
-
}
|
|
599
|
-
if (!tokens.access) {
|
|
600
|
-
console.error("Did not receive any access tokens in response from auth server");
|
|
601
|
-
throw new Error("Did not receive any access tokens in response from auth server");
|
|
602
|
-
}
|
|
603
|
-
if (!tokens.refresh ||
|
|
604
|
-
(typeof tokens.refresh !== "object" &&
|
|
605
|
-
typeof tokens.refresh !== "string")) {
|
|
606
|
-
console.error("Did not receive (valid) refresh token in response from auth server.", `Type: ${typeof tokens.refresh}`, tokens.refresh);
|
|
607
|
-
throw new Error("Did not receive refresh token in response from auth server");
|
|
608
|
-
}
|
|
609
|
-
if (this.debug) {
|
|
610
|
-
debugPrintTokensAsTable(tokens);
|
|
611
|
-
}
|
|
612
|
-
access_tokens = tokens.access;
|
|
613
|
-
refresh_token = tokens.refresh;
|
|
614
|
-
if (!userData) {
|
|
615
|
-
console.error("Did not receive user data in response from auth server");
|
|
616
|
-
throw new Error("Did not receive user data in response from auth server");
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
if (this.debug) {
|
|
620
|
-
debugPrintUserDataAsTable(userData);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
user = userData;
|
|
624
|
-
}
|
|
625
|
-
catch (e) {
|
|
626
|
-
let errorMessage = "Unknown error";
|
|
627
|
-
if (e instanceof Error) {
|
|
628
|
-
errorMessage = e.message;
|
|
629
|
-
}
|
|
630
|
-
console.error("Failed to parse tokens response: ", errorMessage);
|
|
631
|
-
throw new Error(`Failed to parse tokens response: ${errorMessage}`);
|
|
632
|
-
}
|
|
633
|
-
// Store refresh token
|
|
634
|
-
const doStoreReceivedRefreshToken = () => {
|
|
635
|
-
if (typeof refresh_token === "object" &&
|
|
636
|
-
refresh_token.type === "refresh") {
|
|
637
|
-
try {
|
|
638
|
-
if (debug) {
|
|
639
|
-
console.log("[SchemaVaultsAuthClient] Storing refresh token...");
|
|
640
|
-
}
|
|
641
|
-
this.storeRefreshToken(refresh_token);
|
|
642
|
-
if (debug) {
|
|
643
|
-
console.log("[SchemaVaultsAuthClient] Stored refresh token!");
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
catch (e) {
|
|
647
|
-
console.error(e);
|
|
648
|
-
throw new Error("Failed to store refresh token");
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
else if (typeof refresh_token === "string" &&
|
|
652
|
-
refresh_token === "AS_HTTP_ONLY_COOKIE") {
|
|
653
|
-
this.assertHttpOnlyRefreshTokenCookieHasAccompanyingMarkerCookie();
|
|
654
|
-
if (debug) {
|
|
655
|
-
console.log("[SchemaVaultsAuthClient] Detected HTTP-only cookie refresh token (accompanying-cookie).");
|
|
656
|
-
}
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
else {
|
|
660
|
-
throw new TypeError("Invalid type for refresh token!");
|
|
661
|
-
}
|
|
662
|
-
};
|
|
663
|
-
doStoreReceivedRefreshToken();
|
|
664
|
-
// Store access tokens
|
|
665
|
-
this.storeMultipleAccessTokens(access_tokens);
|
|
666
|
-
try {
|
|
667
|
-
if (debug) {
|
|
668
|
-
console.log("[SchemaVaultsAuthClient] Storing user data...");
|
|
669
|
-
}
|
|
670
|
-
this.storeUserData(user);
|
|
671
|
-
if (debug) {
|
|
672
|
-
console.log("[SchemaVaultsAuthClient] Stored user data.");
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
catch (e) {
|
|
676
|
-
console.error("Failed to store user data: ", e);
|
|
677
|
-
throw new Error("Failed to store user data");
|
|
678
|
-
}
|
|
679
|
-
if (debug) {
|
|
680
|
-
console.log("[SchemaVaultsAuthClient] Triggering auth state changed!");
|
|
681
|
-
}
|
|
682
|
-
this.triggerAuthStateChanged();
|
|
683
|
-
if (debug) {
|
|
684
|
-
console.log("[SchemaVaultsAuthClient] Finished triggering auth state change.");
|
|
685
|
-
}
|
|
686
|
-
if (debug) {
|
|
687
|
-
console.log("[SchemaVaultsAuthClient] handleSuccessfulAuthentication success!");
|
|
688
|
-
}
|
|
689
|
-
return;
|
|
367
|
+
return await handleSuccessfulAuthentication({
|
|
368
|
+
authorization_code,
|
|
369
|
+
challenge_time,
|
|
370
|
+
code_verifier,
|
|
371
|
+
loadCodeVerifier: this.loadCodeVerifier.bind(this),
|
|
372
|
+
auth_server_uri: this.auth_server_uri,
|
|
373
|
+
client_app_id: this.app_id,
|
|
374
|
+
adapter: this.adapter,
|
|
375
|
+
storeUserData: this.storeUserData.bind(this),
|
|
376
|
+
storeRefreshToken: this.storeRefreshToken.bind(this),
|
|
377
|
+
storeMultipleAccessTokens: this.storeMultipleAccessTokens.bind(this),
|
|
378
|
+
environment: this.environment,
|
|
379
|
+
debug: this.DEBUG,
|
|
380
|
+
triggerAuthStateChanged: this.triggerAuthStateChanged.bind(this),
|
|
381
|
+
defaultTokenAudiences: this.defaultTokenAudiences,
|
|
382
|
+
});
|
|
690
383
|
} // handleSuccessfulAuthentication()
|
|
691
384
|
async logout() {
|
|
692
385
|
if (this.debug) {
|
|
@@ -895,31 +588,7 @@ export class SchemaVaultsAuthClient extends EventTarget {
|
|
|
895
588
|
* @description Getter that returns true/false based on whether a user is currently signed into their account
|
|
896
589
|
*/
|
|
897
590
|
get isAuthenticated() {
|
|
898
|
-
|
|
899
|
-
console.log("[SchemaVaultsAuthClient::isAuthenticated] Checking whether auth client has a refresh token to see if authenticated...");
|
|
900
|
-
}
|
|
901
|
-
const refreshToken = this.getRefreshTokenFromCache();
|
|
902
|
-
if (refreshToken) {
|
|
903
|
-
if (this.DEBUG) {
|
|
904
|
-
console.log("[SchemaVaultsAuthClient::isAuthenticated] There is a refresh token stored, checking if it is expired...");
|
|
905
|
-
}
|
|
906
|
-
const refreshTokenExpiryTime = refreshToken.exp;
|
|
907
|
-
const now = this.getCurrentTimestamp();
|
|
908
|
-
if (now < refreshTokenExpiryTime) {
|
|
909
|
-
return true;
|
|
910
|
-
}
|
|
911
|
-
else {
|
|
912
|
-
if (this.DEBUG) {
|
|
913
|
-
console.warn("[SchemaVaultsAuthClient::isAuthenticated] There is a refresh token stored, but it appears to be expired!");
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
else {
|
|
918
|
-
if (this.DEBUG) {
|
|
919
|
-
console.warn("[SchemaVaultsAuthClient::isAuthenticated] No refresh token found from cache!");
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
return false;
|
|
591
|
+
return this.adapter.hasRefreshToken();
|
|
923
592
|
}
|
|
924
593
|
/**
|
|
925
594
|
* @name sendAuthenticateRequest
|
|
@@ -950,14 +619,6 @@ export class SchemaVaultsAuthClient extends EventTarget {
|
|
|
950
619
|
const userData = this.getUserData();
|
|
951
620
|
return userData;
|
|
952
621
|
}
|
|
953
|
-
assertHttpOnlyRefreshTokenCookieHasAccompanyingMarkerCookie() {
|
|
954
|
-
if (typeof this.adapter.hasHttpOnlyRefreshToken === "function" &&
|
|
955
|
-
!this.adapter.hasHttpOnlyRefreshToken()) {
|
|
956
|
-
throw new Error("Adapter does not indicate having an HTTP-only refresh token after exchange," +
|
|
957
|
-
" " +
|
|
958
|
-
"despite response of AS_HTTP_ONLY_COOKIE!");
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
622
|
async handleSuccessfulExchangeAuthTokensResponse(tokens_response) {
|
|
962
623
|
const parsed_tokens_data = await requestTokensResultSchema.safeParseAsync(tokens_response);
|
|
963
624
|
if (!parsed_tokens_data.success) {
|
|
@@ -991,7 +652,7 @@ export class SchemaVaultsAuthClient extends EventTarget {
|
|
|
991
652
|
}
|
|
992
653
|
else if (typeof tokens.refresh === "string" &&
|
|
993
654
|
tokens.refresh === "AS_HTTP_ONLY_COOKIE") {
|
|
994
|
-
this.
|
|
655
|
+
assertHttpOnlyRefreshTokenCookieHasAccompanyingMarkerCookie(this.adapter);
|
|
995
656
|
if (this.debug) {
|
|
996
657
|
console.log("[SchemaVaultsAuthClient] Detected HTTP-only cookie refresh token from exchange response.");
|
|
997
658
|
}
|
|
@@ -1003,122 +664,17 @@ export class SchemaVaultsAuthClient extends EventTarget {
|
|
|
1003
664
|
return tokens;
|
|
1004
665
|
}
|
|
1005
666
|
async exchangeAuthTokens(refreshToken, audience, replaceRefreshToo) {
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
const parsed = await refreshTokenPOSTbody.safeParseAsync({
|
|
1018
|
-
grant_type: "refresh_token",
|
|
1019
|
-
client_app_id,
|
|
1020
|
-
audience: audience ?? this.defaultTokenAudiences,
|
|
1021
|
-
replaceRefreshToo: replaceRefreshToo ?? false,
|
|
1022
|
-
});
|
|
1023
|
-
if (!parsed.success) {
|
|
1024
|
-
console.error(parsed.error);
|
|
1025
|
-
throw new Error("Failed to parse tokens from exchange auth tokens POST request!");
|
|
1026
|
-
}
|
|
1027
|
-
request_body = parsed.data;
|
|
1028
|
-
}
|
|
1029
|
-
catch (e) {
|
|
1030
|
-
if (this.DEBUG) {
|
|
1031
|
-
console.error("Failed to prepare request body for authorization grant request: ", e);
|
|
1032
|
-
}
|
|
1033
|
-
throw new Error("Failed to prepare request body for authorization grant request");
|
|
1034
|
-
}
|
|
1035
|
-
const exchangeAuthTokensReqHeaders = {};
|
|
1036
|
-
if (!refreshToken) {
|
|
1037
|
-
throw new Error("Did not receive a refresh token to exchange for access token!");
|
|
1038
|
-
}
|
|
1039
|
-
if (typeof refreshToken === "object" && refreshToken.type === "refresh") {
|
|
1040
|
-
if (typeof refreshToken.token !== "string" ||
|
|
1041
|
-
refreshToken.token.length === 0) {
|
|
1042
|
-
throw new TypeError("Expected 'token' to be a non-empty string!");
|
|
1043
|
-
}
|
|
1044
|
-
exchangeAuthTokensReqHeaders["Authorization"] =
|
|
1045
|
-
`Bearer ${refreshToken.token}`;
|
|
1046
|
-
}
|
|
1047
|
-
else if (typeof refreshToken === "string" &&
|
|
1048
|
-
refreshToken === "AS_HTTP_ONLY_COOKIE") {
|
|
1049
|
-
const doesSupportHttpOnlyRefreshToken = this.adapter.doesSupportHttpOnlyRefreshToken;
|
|
1050
|
-
if (typeof doesSupportHttpOnlyRefreshToken !== "function" ||
|
|
1051
|
-
!doesSupportHttpOnlyRefreshToken()) {
|
|
1052
|
-
throw new Error("Adapter does not support HTTP-only refresh tokens!");
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
else {
|
|
1056
|
-
throw new Error("Did not receive a valid refresh token (or valid method of acquiring refresh token)");
|
|
1057
|
-
}
|
|
1058
|
-
if (this.DEBUG) {
|
|
1059
|
-
console.log("[SchemaVaultsAuthClient::exchangeAuthTokens()] " +
|
|
1060
|
-
`Sending POST request to "${token_endpoint}" with body & headers:`, request_body, exchangeAuthTokensReqHeaders);
|
|
1061
|
-
}
|
|
1062
|
-
let tokens_response_data;
|
|
1063
|
-
try {
|
|
1064
|
-
if (this.DEBUG) {
|
|
1065
|
-
console.log(`POST => ${token_endpoint}`);
|
|
1066
|
-
}
|
|
1067
|
-
const response = await this.adapter.sendPOSTRequest(token_endpoint,
|
|
1068
|
-
// body
|
|
1069
|
-
request_body,
|
|
1070
|
-
// headers
|
|
1071
|
-
exchangeAuthTokensReqHeaders);
|
|
1072
|
-
if (!response ||
|
|
1073
|
-
typeof response !== "object" ||
|
|
1074
|
-
response.status !== 200) {
|
|
1075
|
-
if (response.status === 403 || response.status === 401) {
|
|
1076
|
-
console.error("401/403 error response from exchange token attempt, client is not logged in!");
|
|
1077
|
-
await this.logout();
|
|
1078
|
-
}
|
|
1079
|
-
throw new Error("HTTP request failed to exchange refresh token for access token(s) object");
|
|
1080
|
-
}
|
|
1081
|
-
const parsed_failed_tokens_result = await requestTokensResultSchema.safeParseAsync(response.data);
|
|
1082
|
-
if (!parsed_failed_tokens_result.success) {
|
|
1083
|
-
console.error("Failed to parse tokens response from server: ", parsed_failed_tokens_result.error);
|
|
1084
|
-
throw new Error("Failed to parse tokens response from server!");
|
|
1085
|
-
}
|
|
1086
|
-
tokens_response_data = parsed_failed_tokens_result.data;
|
|
1087
|
-
}
|
|
1088
|
-
catch (e) {
|
|
1089
|
-
if (this.DEBUG) {
|
|
1090
|
-
console.error("[this.adapter.sendPOSTRequest] FAILED: ", e);
|
|
1091
|
-
throw new Error("Failed to ");
|
|
1092
|
-
}
|
|
1093
|
-
if (e instanceof Error) {
|
|
1094
|
-
const errMsg = e.message;
|
|
1095
|
-
const eMsg = errMsg.toLowerCase();
|
|
1096
|
-
if (eMsg.includes("expired") ||
|
|
1097
|
-
eMsg.includes("jwtexpired") ||
|
|
1098
|
-
eMsg.includes("err_jwt_expired")) {
|
|
1099
|
-
console.error("Refresh token appears to have expired!");
|
|
1100
|
-
throw new Error("Refresh token has expired!");
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
if (this.DEBUG) {
|
|
1104
|
-
console.error("Failed to exchange refresh token for access token: ", e);
|
|
1105
|
-
}
|
|
1106
|
-
throw new Error("Failed to exchange refresh token for access token");
|
|
1107
|
-
} // end of this.adapter.sendPOSTRequest catch block
|
|
1108
|
-
try {
|
|
1109
|
-
// Parse tokens from response JSON body
|
|
1110
|
-
return await this.handleSuccessfulExchangeAuthTokensResponse(tokens_response_data);
|
|
1111
|
-
}
|
|
1112
|
-
catch (e) {
|
|
1113
|
-
if (this.DEBUG) {
|
|
1114
|
-
console.log("typeof e === ", typeof e);
|
|
1115
|
-
if (e instanceof Error) {
|
|
1116
|
-
console.error("Parse tokens error message: ", e.message);
|
|
1117
|
-
}
|
|
1118
|
-
console.error("Failed to parse authentication tokens from exchange tokens POST request: ", e);
|
|
1119
|
-
}
|
|
1120
|
-
throw new Error("Failed to parse authentication tokens from exchange tokens POST request!");
|
|
1121
|
-
}
|
|
667
|
+
return await exchangeAuthTokens({
|
|
668
|
+
refreshToken,
|
|
669
|
+
replaceRefreshToo,
|
|
670
|
+
audience: audience ?? this.defaultTokenAudiences,
|
|
671
|
+
logout: this.logout.bind(this),
|
|
672
|
+
debug: this.DEBUG,
|
|
673
|
+
handleSuccessfulExchangeAuthTokensResponse: this.handleSuccessfulExchangeAuthTokensResponse.bind(this),
|
|
674
|
+
client_app_id: this.app_id,
|
|
675
|
+
adapter: this.adapter,
|
|
676
|
+
auth_server_uri: this.auth_server_uri,
|
|
677
|
+
});
|
|
1122
678
|
} // exchangeAuthTokens()
|
|
1123
679
|
uuid() {
|
|
1124
680
|
let id;
|
|
@@ -1189,5 +745,12 @@ export class SchemaVaultsAuthClient extends EventTarget {
|
|
|
1189
745
|
}
|
|
1190
746
|
return false;
|
|
1191
747
|
}
|
|
748
|
+
async checkIfAuthenticatedWithServer() {
|
|
749
|
+
return await checkIfAuthenticatedWithServer({
|
|
750
|
+
adapter: this.adapter,
|
|
751
|
+
auth_server_uri: this.auth_server_uri,
|
|
752
|
+
client_app_id: this.app_id,
|
|
753
|
+
});
|
|
754
|
+
}
|
|
1192
755
|
}
|
|
1193
756
|
//# sourceMappingURL=auth-client.js.map
|