@crossauth/sveltekit 1.1.2 → 1.1.4
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/sveltekitadminendpoints.d.ts +1 -1
- package/dist/sveltekitadminendpoints.js +1 -1
- package/dist/sveltekitoauthclient.d.ts +9 -9
- package/dist/sveltekitoauthclient.js +166 -83
- package/dist/sveltekitoauthserver.d.ts +20 -2
- package/dist/sveltekitoauthserver.js +276 -42
- package/dist/sveltekitserver.js +76 -62
- package/dist/sveltekitsession.js +95 -88
- package/package.json +3 -3
|
@@ -201,7 +201,7 @@ export interface SearchUsersActionData {
|
|
|
201
201
|
* | baseEndpoint | This PageData is returned by all endpoints' load function. | - `user` logged in {@link @crossauth/common!User} | *Not provided* | | |
|
|
202
202
|
* | | | - `csrfToken` CSRF token if enabled | | | | | loginPage |
|
|
203
203
|
* | -------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------- | --------- |
|
|
204
|
-
* | searchUsersEndpoint | Returns a paginated set of users or those matchign search | See return of
|
|
204
|
+
* | searchUsersEndpoint | Returns a paginated set of users or those matchign search | See return of searchUsers | *Not provided* | | |
|
|
205
205
|
* | -------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------- | --------- |
|
|
206
206
|
* | updateUserEndpoint | Update a user's details | - `allowedFactor2` see {@link SvelteKitAdminEndpoints}.`signupEndpoint` | `default`: | `default`: | `id` |
|
|
207
207
|
* | | | - `editUser` the {@link @crossauth/common!User} being edited | - see {@link SvelteKitAdminEndpoints.updateUser} return | - see {@link SvelteKitAdminEndpoints.updateUser} event | |
|
|
@@ -86,7 +86,7 @@ async function defaultUserSearchFn(searchTerm, userStorage, skip = 0, _take = 10
|
|
|
86
86
|
* | baseEndpoint | This PageData is returned by all endpoints' load function. | - `user` logged in {@link @crossauth/common!User} | *Not provided* | | |
|
|
87
87
|
* | | | - `csrfToken` CSRF token if enabled | | | | | loginPage |
|
|
88
88
|
* | -------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------- | --------- |
|
|
89
|
-
* | searchUsersEndpoint | Returns a paginated set of users or those matchign search | See return of
|
|
89
|
+
* | searchUsersEndpoint | Returns a paginated set of users or those matchign search | See return of searchUsers | *Not provided* | | |
|
|
90
90
|
* | -------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------- | --------- |
|
|
91
91
|
* | updateUserEndpoint | Update a user's details | - `allowedFactor2` see {@link SvelteKitAdminEndpoints}.`signupEndpoint` | `default`: | `default`: | `id` |
|
|
92
92
|
* | | | - `editUser` the {@link @crossauth/common!User} being edited | - see {@link SvelteKitAdminEndpoints.updateUser} return | - see {@link SvelteKitAdminEndpoints.updateUser} event | |
|
|
@@ -62,7 +62,7 @@ export interface SvelteKitOAuthClientOptions extends OAuthClientOptions {
|
|
|
62
62
|
*/
|
|
63
63
|
receiveTokenFn?: (oauthResponse: OAuthTokenResponse, client: SvelteKitOAuthClient, event: RequestEvent, silent: boolean, setUserFn: (event: RequestEvent, token: {
|
|
64
64
|
[key: string]: any;
|
|
65
|
-
}) => Promise<void>) => Promise<Response | TokenReturn
|
|
65
|
+
}) => Promise<void>) => Promise<Response | TokenReturn>;
|
|
66
66
|
/**
|
|
67
67
|
* The function to call when there is an OAuth error and
|
|
68
68
|
* {@link SvelteKitOAuthClientOptions.errorResponseType}
|
|
@@ -440,19 +440,19 @@ export declare class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
440
440
|
load: (event: RequestEvent) => Promise<AuthorizationCodeFlowReturn>;
|
|
441
441
|
};
|
|
442
442
|
readonly redirectUriEndpoint: {
|
|
443
|
-
get: (event: RequestEvent) => Promise<Response
|
|
443
|
+
get: (event: RequestEvent) => Promise<Response>;
|
|
444
444
|
load: (event: RequestEvent) => Promise<RedirectUriReturn>;
|
|
445
445
|
};
|
|
446
446
|
readonly clientCredentialsFlowEndpoint: {
|
|
447
447
|
post: (event: RequestEvent) => Promise<Response>;
|
|
448
448
|
actions: {
|
|
449
|
-
default: (event: RequestEvent) => Promise<
|
|
449
|
+
default: (event: RequestEvent) => Promise<Response | TokenReturn>;
|
|
450
450
|
};
|
|
451
451
|
};
|
|
452
452
|
readonly refreshTokenFlowEndpoint: {
|
|
453
453
|
post: (event: RequestEvent) => Promise<Response>;
|
|
454
454
|
actions: {
|
|
455
|
-
default: (event: RequestEvent) => Promise<
|
|
455
|
+
default: (event: RequestEvent) => Promise<Response | TokenReturn>;
|
|
456
456
|
};
|
|
457
457
|
};
|
|
458
458
|
readonly refreshTokensIfExpiredEndpoint: {
|
|
@@ -505,9 +505,9 @@ export declare class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
505
505
|
ok: boolean;
|
|
506
506
|
}>;
|
|
507
507
|
actions: {
|
|
508
|
-
password: (event: RequestEvent) => Promise<
|
|
509
|
-
passwordOtp: (event: RequestEvent) => Promise<
|
|
510
|
-
passwordOob: (event: RequestEvent) => Promise<
|
|
508
|
+
password: (event: RequestEvent) => Promise<Response | OAuthTokenResponse>;
|
|
509
|
+
passwordOtp: (event: RequestEvent) => Promise<Response | OAuthTokenResponse>;
|
|
510
|
+
passwordOob: (event: RequestEvent) => Promise<Response | OAuthTokenResponse>;
|
|
511
511
|
};
|
|
512
512
|
};
|
|
513
513
|
readonly passwordOtpEndpoint: {
|
|
@@ -532,7 +532,7 @@ export declare class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
532
532
|
ok: boolean;
|
|
533
533
|
}>;
|
|
534
534
|
actions: {
|
|
535
|
-
default: (event: RequestEvent) => Promise<
|
|
535
|
+
default: (event: RequestEvent) => Promise<Response | OAuthTokenResponse>;
|
|
536
536
|
};
|
|
537
537
|
};
|
|
538
538
|
readonly passwordOobEndpoint: {
|
|
@@ -557,7 +557,7 @@ export declare class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
557
557
|
ok: boolean;
|
|
558
558
|
}>;
|
|
559
559
|
actions: {
|
|
560
|
-
default: (event: RequestEvent) => Promise<
|
|
560
|
+
default: (event: RequestEvent) => Promise<Response | OAuthTokenResponse>;
|
|
561
561
|
};
|
|
562
562
|
};
|
|
563
563
|
readonly deleteTokensEndpoint: {
|
|
@@ -7,6 +7,7 @@ import { SvelteKitServer } from './sveltekitserver';
|
|
|
7
7
|
import { json } from '@sveltejs/kit';
|
|
8
8
|
import { JsonOrFormData } from './utils';
|
|
9
9
|
import {} from './tests/sveltemocks';
|
|
10
|
+
import {} from './sveltekitoauthserver';
|
|
10
11
|
////////////////////////////////////////////////////////////////////////////
|
|
11
12
|
// DEFAULT FUNCTIONS
|
|
12
13
|
async function jsonError(_server, _event, ce) {
|
|
@@ -125,8 +126,31 @@ async function saveInSessionAndRedirect(oauthResponse, client, event, silent, se
|
|
|
125
126
|
if (payload)
|
|
126
127
|
await setUserFn(event, payload);
|
|
127
128
|
}
|
|
129
|
+
let url = client.authorizedUrl;
|
|
130
|
+
var data = new JsonOrFormData();
|
|
131
|
+
await data.loadData(event);
|
|
132
|
+
let formData = data.toObject();
|
|
133
|
+
let msg = formData.msg ?? event.url.searchParams.get("msg");
|
|
134
|
+
let error = formData.error ?? event.url.searchParams.get("error");
|
|
135
|
+
if (msg) {
|
|
136
|
+
if (url.includes("?")) {
|
|
137
|
+
url += "&msg=" + encodeURIComponent(msg);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
url += "?msg=" + encodeURIComponent(msg);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (error) {
|
|
144
|
+
if (url.includes("?")) {
|
|
145
|
+
url += "&error=" + encodeURIComponent(error);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
url += "?error=" + encodeURIComponent(error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
128
151
|
if (!silent)
|
|
129
|
-
return client.redirect(302,
|
|
152
|
+
return client.redirect(302, url);
|
|
153
|
+
return json({});
|
|
130
154
|
}
|
|
131
155
|
catch (e) {
|
|
132
156
|
if (SvelteKitServer.isSvelteKitError(e) || SvelteKitServer.isSvelteKitRedirect(e))
|
|
@@ -151,8 +175,6 @@ async function saveInSessionAndReturn(oauthResponse, client, event, silent, setU
|
|
|
151
175
|
await setUserFn(event, payload);
|
|
152
176
|
}
|
|
153
177
|
return json({ ok: true, ...oauthResponse });
|
|
154
|
-
if (!silent)
|
|
155
|
-
return client.redirect(302, client.authorizedUrl);
|
|
156
178
|
}
|
|
157
179
|
catch (e) {
|
|
158
180
|
if (SvelteKitServer.isSvelteKitError(e) || SvelteKitServer.isSvelteKitRedirect(e))
|
|
@@ -540,40 +562,49 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
540
562
|
if (this, this.loginProtectedFlows.length > 0 && this.loginUrl == "") {
|
|
541
563
|
throw new CrossauthError(ErrorCode.Configuration, "loginUrl must be set if protecting oauth endpoints");
|
|
542
564
|
}
|
|
565
|
+
// this doesn't work as it relies on data stored in the session, which cannot be done
|
|
566
|
+
// as tokens are fetched from a backend POST, thus no cookies
|
|
543
567
|
this.hook = async ({ event }) => {
|
|
544
|
-
CrossauthLogger.logger.debug(j({ msg: "OAuth hook, user " + event.locals.user }));
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
if (!validIdToken && sessionData && sessionData["refresh_token"]) {
|
|
562
|
-
CrossauthLogger.logger.debug(j({ msg: "No ID token found but refresh token found - attemping refresh flow" }));
|
|
563
|
-
const resp = await this.refreshTokens(event, "silent", false);
|
|
564
|
-
if (!resp?.ok) {
|
|
565
|
-
const error = resp instanceof Response || resp == undefined ? "server_error" : (resp.error ?? "server_error");
|
|
566
|
-
const error_description = resp instanceof Response || resp == undefined ? "Unknown error" : (resp.error_description ?? "Unknown error");
|
|
567
|
-
const ce = CrossauthError.fromOAuthError(error, error_description);
|
|
568
|
-
CrossauthLogger.logger.debug(j({ err: ce }));
|
|
569
|
-
CrossauthLogger.logger.warn(j({ msg: "Error refreshing token", cerr: ce }));
|
|
568
|
+
CrossauthLogger.logger.debug(j({ msg: "OAuth hook, user " + event.locals.user?.username }));
|
|
569
|
+
try {
|
|
570
|
+
if (event.locals.user)
|
|
571
|
+
return undefined;
|
|
572
|
+
if (!server.sessionAdapter)
|
|
573
|
+
return undefined;
|
|
574
|
+
let sessionData = await server.sessionAdapter.getSessionData(event, this.sessionDataName);
|
|
575
|
+
let validIdToken = false;
|
|
576
|
+
//CrossauthLogger.logger.debug(j({msg:"Session data " + (sessionData && sessionData["id_payload"]) ? JSON.stringify(sessionData?.id_payload) : "none)"}));
|
|
577
|
+
if (sessionData && sessionData["id_payload"]) {
|
|
578
|
+
let expiry = sessionData["expires_at"];
|
|
579
|
+
if (expiry && expiry > Date.now() && sessionData["id_payload"].sub) {
|
|
580
|
+
CrossauthLogger.logger.debug(j({ msg: "ID token is valid" }));
|
|
581
|
+
await this.setEventLocalsUser(event, sessionData["id_payload"]);
|
|
582
|
+
if (event.locals.user)
|
|
583
|
+
validIdToken = true;
|
|
584
|
+
}
|
|
570
585
|
}
|
|
571
|
-
|
|
572
|
-
|
|
586
|
+
if (!validIdToken && sessionData && sessionData["refresh_token"]) {
|
|
587
|
+
CrossauthLogger.logger.debug(j({ msg: "No ID token found but refresh token found - attemping refresh flow" }));
|
|
588
|
+
const resp = await this.refreshTokens(event, "silent", false);
|
|
589
|
+
if (!resp?.ok) {
|
|
590
|
+
const error = resp instanceof Response || resp == undefined ? "server_error" : (resp.error ?? "server_error");
|
|
591
|
+
const error_description = resp instanceof Response || resp == undefined ? "Unknown error" : (resp.error_description ?? "Unknown error");
|
|
592
|
+
const ce = CrossauthError.fromOAuthError(error, error_description);
|
|
593
|
+
CrossauthLogger.logger.debug(j({ err: ce }));
|
|
594
|
+
CrossauthLogger.logger.warn(j({ msg: "Error refreshing token", cerr: ce }));
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
await this.setEventLocalsUser(event, sessionData["id_payload"]);
|
|
598
|
+
}
|
|
573
599
|
}
|
|
600
|
+
if (this.testMiddleware)
|
|
601
|
+
this.testEvent = event;
|
|
602
|
+
}
|
|
603
|
+
catch (e) {
|
|
604
|
+
let ce = CrossauthError.asCrossauthError(e);
|
|
605
|
+
CrossauthLogger.logger.debug(j({ err: ce }));
|
|
606
|
+
CrossauthLogger.logger.error(j({ msg: "Error in oauth client hook", cerr: ce }));
|
|
574
607
|
}
|
|
575
|
-
if (this.testMiddleware)
|
|
576
|
-
this.testEvent = event;
|
|
577
608
|
return undefined;
|
|
578
609
|
};
|
|
579
610
|
}
|
|
@@ -1030,7 +1061,7 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1030
1061
|
try {
|
|
1031
1062
|
if (!this.server.sessionAdapter)
|
|
1032
1063
|
throw new CrossauthError(ErrorCode.Configuration, "Session server must be instantiated to use bff()");
|
|
1033
|
-
if (!this.server.oAuthClient)
|
|
1064
|
+
if (!this.server.oAuthClient && !this.server.oAuthClients)
|
|
1034
1065
|
throw new CrossauthError(ErrorCode.Configuration, "OAuth Client not found"); // pathological but prevents TS errors
|
|
1035
1066
|
if (!this.bffBaseUrl)
|
|
1036
1067
|
throw new CrossauthError(ErrorCode.Configuration, "Must set bffBaseUrl to use bff()");
|
|
@@ -1168,7 +1199,7 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1168
1199
|
CrossauthLogger.logger.debug(j({ msg: "Called allBff", url: event.url.toString() }));
|
|
1169
1200
|
if (!this.server.sessionAdapter)
|
|
1170
1201
|
throw new CrossauthError(ErrorCode.Configuration, "Session server must be instantiated to use bff()");
|
|
1171
|
-
if (!this.server.oAuthClient)
|
|
1202
|
+
if (!this.server.oAuthClient && !this.server.oAuthClients)
|
|
1172
1203
|
throw new CrossauthError(ErrorCode.Configuration, "OAuth Client not found"); // pathological but prevents TS errors
|
|
1173
1204
|
if (!this.bffBaseUrl)
|
|
1174
1205
|
throw new CrossauthError(ErrorCode.Configuration, "Must set bffBaseUrl to use bff()");
|
|
@@ -1238,7 +1269,7 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1238
1269
|
const decode = data.getAsBoolean("decode") ?? true;
|
|
1239
1270
|
if (!this.server.sessionAdapter)
|
|
1240
1271
|
throw new CrossauthError(ErrorCode.Configuration, "Session server must be instantiated to use bff()");
|
|
1241
|
-
if (!this.server.oAuthClient)
|
|
1272
|
+
if (!this.server.oAuthClient && !this.server.oAuthClients)
|
|
1242
1273
|
throw new CrossauthError(ErrorCode.Configuration, "OAuth Client not found"); // pathological but prevents TS errors
|
|
1243
1274
|
if (!this.tokenEndpoints || this.tokenEndpoints.length == 0)
|
|
1244
1275
|
throw new CrossauthError(ErrorCode.Unauthorized, "No tokens have been made available");
|
|
@@ -1304,7 +1335,7 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1304
1335
|
const resp = await this.tokens(event, token);
|
|
1305
1336
|
if (resp.body)
|
|
1306
1337
|
return json(resp.body, { status: resp.status });
|
|
1307
|
-
return json(null, { status: resp.status });
|
|
1338
|
+
return json({ error: "null body" }, { status: resp.status });
|
|
1308
1339
|
}
|
|
1309
1340
|
async startDeviceCodeFlow_internal(event) {
|
|
1310
1341
|
let formData = undefined;
|
|
@@ -1469,8 +1500,10 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1469
1500
|
get: async (event) => {
|
|
1470
1501
|
if (this.tokenResponseType == "saveInSessionAndLoad" || this.tokenResponseType == "sendInPage") {
|
|
1471
1502
|
const ce = new CrossauthError(ErrorCode.Configuration, "If tokenResponseType is " + this.tokenResponseType + ", use load not get");
|
|
1472
|
-
|
|
1503
|
+
CrossauthLogger.logger.debug(j({ err: ce }));
|
|
1504
|
+
return await this.errorFn(this.server, event, ce);
|
|
1473
1505
|
}
|
|
1506
|
+
let redirectUrl = undefined;
|
|
1474
1507
|
try {
|
|
1475
1508
|
if (!(this.validFlows.includes(OAuthFlows.AuthorizationCode))) {
|
|
1476
1509
|
const ce = new CrossauthError(ErrorCode.Unauthorized, "Authorization flow is not supported");
|
|
@@ -1480,13 +1513,22 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1480
1513
|
throw new CrossauthError(ErrorCode.Configuration, "Need session server or adapter for authorization code flow");
|
|
1481
1514
|
}
|
|
1482
1515
|
let scope = event.url.searchParams.get("scope") ?? undefined;
|
|
1516
|
+
let upstream = event.url.searchParams.get("upstream") ?? undefined;
|
|
1517
|
+
let next = event.url.searchParams.get("next") ?? undefined;
|
|
1483
1518
|
if (scope == "")
|
|
1484
1519
|
scope = undefined;
|
|
1485
1520
|
const state = this.randomValue(this.stateLength);
|
|
1486
1521
|
const sessionData = { scope, state };
|
|
1487
1522
|
// we need a session to save the state
|
|
1488
1523
|
await this.storeSessionData(event, sessionData);
|
|
1489
|
-
|
|
1524
|
+
// if we have an upstream client, this call will save the original /authorize call and redirect to
|
|
1525
|
+
// the upstream auth server's /authorize endpoint
|
|
1526
|
+
if ((upstream || this.server.oAuthAuthServer?.authServer.upstreamClient) && next && this.server.oAuthAuthServer) {
|
|
1527
|
+
await this.server.oAuthAuthServer.saveDownstreamAuthzCodeFlow(event, new URL(next, event.url), upstream);
|
|
1528
|
+
}
|
|
1529
|
+
// the following call returns a constructed url for the
|
|
1530
|
+
// auth server's /authorize endpoint
|
|
1531
|
+
const { url, error, error_description } = await this.startAuthorizationCodeFlow(state, { scope, upstream });
|
|
1490
1532
|
if (error || !url) {
|
|
1491
1533
|
const ce = CrossauthError.fromOAuthError(error ?? "server_error", error_description);
|
|
1492
1534
|
return await this.errorFn(this.server, event, ce);
|
|
@@ -1499,7 +1541,7 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1499
1541
|
msg: `OAuth redirect`,
|
|
1500
1542
|
}));
|
|
1501
1543
|
}
|
|
1502
|
-
|
|
1544
|
+
redirectUrl = url;
|
|
1503
1545
|
}
|
|
1504
1546
|
catch (e) {
|
|
1505
1547
|
if (SvelteKitServer.isSvelteKitRedirect(e))
|
|
@@ -1510,8 +1552,12 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1510
1552
|
CrossauthLogger.logger.debug({ err: e });
|
|
1511
1553
|
CrossauthLogger.logger.error({ cerr: e });
|
|
1512
1554
|
//throw this.error(ce.httpStatus, ce.message);
|
|
1513
|
-
return
|
|
1555
|
+
return jsonError(this.server, event, ce);
|
|
1556
|
+
//return await this.errorFn(this.server, event, ce);
|
|
1514
1557
|
}
|
|
1558
|
+
if (!redirectUrl)
|
|
1559
|
+
jsonError(this.server, event, new CrossauthError(ErrorCode.UnknownError, "Unexpectedly got redirect URL"));
|
|
1560
|
+
throw this.redirect(302, redirectUrl); // to auth server's authorize endpoint
|
|
1515
1561
|
},
|
|
1516
1562
|
load: async (event) => {
|
|
1517
1563
|
if ( /*this.tokenResponseType == "saveInSessionAndRedirect" ||*/this.tokenResponseType == "sendJson" || this.tokenResponseType == "saveInSessionAndLoad") {
|
|
@@ -1537,11 +1583,20 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1537
1583
|
let scope = event.url.searchParams.get("scope") ?? undefined;
|
|
1538
1584
|
if (scope == "")
|
|
1539
1585
|
scope = undefined;
|
|
1586
|
+
let upstream = event.url.searchParams.get("upstream") ?? undefined;
|
|
1587
|
+
let next = event.url.searchParams.get("next") ?? undefined;
|
|
1540
1588
|
const state = this.randomValue(this.stateLength);
|
|
1541
1589
|
const sessionData = { scope, state };
|
|
1542
1590
|
// we need a session to save the state
|
|
1543
1591
|
await this.storeSessionData(event, sessionData);
|
|
1544
|
-
|
|
1592
|
+
// if we have an upstream client, this call will save the original /authorize call and redirect to
|
|
1593
|
+
// the upstream auth server's /authorize endpoint
|
|
1594
|
+
if ((upstream || this.server.oAuthAuthServer?.authServer.upstreamClient) && next && this.server.oAuthAuthServer) {
|
|
1595
|
+
await this.server.oAuthAuthServer.saveDownstreamAuthzCodeFlow(event, new URL(next), upstream);
|
|
1596
|
+
}
|
|
1597
|
+
// the following call returns a constructed url for the
|
|
1598
|
+
// auth server's /authorize endpoint
|
|
1599
|
+
const { url, error, error_description } = await this.startAuthorizationCodeFlow(state, { scope, upstream });
|
|
1545
1600
|
if (error || !url) {
|
|
1546
1601
|
const ce = CrossauthError.fromOAuthError(error ?? "server_error", error_description);
|
|
1547
1602
|
return {
|
|
@@ -1558,7 +1613,7 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1558
1613
|
msg: `OAuth redirect`,
|
|
1559
1614
|
}));
|
|
1560
1615
|
}
|
|
1561
|
-
throw this.redirect(302, url);
|
|
1616
|
+
throw this.redirect(302, url); // to auth server's authorize endpoint
|
|
1562
1617
|
}
|
|
1563
1618
|
catch (e) {
|
|
1564
1619
|
if (SvelteKitServer.isSvelteKitRedirect(e))
|
|
@@ -1594,12 +1649,21 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1594
1649
|
let scope = event.url.searchParams.get("scope") ?? undefined;
|
|
1595
1650
|
if (scope == "")
|
|
1596
1651
|
scope = undefined;
|
|
1652
|
+
let upstream = event.url.searchParams.get("upstream") ?? undefined;
|
|
1653
|
+
let next = event.url.searchParams.get("next") ?? undefined;
|
|
1597
1654
|
const state = this.randomValue(this.stateLength);
|
|
1598
1655
|
const { codeChallenge, codeVerifier } = await this.codeChallengeAndVerifier();
|
|
1599
1656
|
const sessionData = { scope, state, codeChallenge, codeVerifier };
|
|
1600
1657
|
// we need a session to save the state
|
|
1601
1658
|
await this.storeSessionData(event, sessionData);
|
|
1602
|
-
|
|
1659
|
+
// if we have an upstream client, this call will save the original /authorize call and redirect to
|
|
1660
|
+
// the upstream auth server's /authorize endpoint
|
|
1661
|
+
if ((upstream || this.server.oAuthAuthServer?.authServer.upstreamClient) && next && this.server.oAuthAuthServer) {
|
|
1662
|
+
await this.server.oAuthAuthServer.saveDownstreamAuthzCodeFlow(event, new URL(next), upstream);
|
|
1663
|
+
}
|
|
1664
|
+
// the following call returns a constructed url for the
|
|
1665
|
+
// auth server's /authorize endpoint
|
|
1666
|
+
const { url, error, error_description } = await this.startAuthorizationCodeFlow(state, { scope, codeChallenge, pkce: true, upstream });
|
|
1603
1667
|
if (error || !url) {
|
|
1604
1668
|
const ce = CrossauthError.fromOAuthError(error ?? "server_error", error_description);
|
|
1605
1669
|
return await this.errorFn(this.server, event, ce);
|
|
@@ -1612,7 +1676,7 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1612
1676
|
msg: `OAuth redirect`,
|
|
1613
1677
|
}));
|
|
1614
1678
|
}
|
|
1615
|
-
throw this.redirect(302, url);
|
|
1679
|
+
throw this.redirect(302, url); // to auth server's authorize endpoint
|
|
1616
1680
|
}
|
|
1617
1681
|
catch (e) {
|
|
1618
1682
|
if (SvelteKitServer.isSvelteKitRedirect(e))
|
|
@@ -1653,12 +1717,21 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1653
1717
|
let scope = event.url.searchParams.get("scope") ?? undefined;
|
|
1654
1718
|
if (scope == "")
|
|
1655
1719
|
scope = undefined;
|
|
1720
|
+
let upstream = event.url.searchParams.get("upstream") ?? undefined;
|
|
1721
|
+
let next = event.url.searchParams.get("next") ?? undefined;
|
|
1656
1722
|
const state = this.randomValue(this.stateLength);
|
|
1657
1723
|
const { codeChallenge, codeVerifier } = await this.codeChallengeAndVerifier();
|
|
1658
1724
|
const sessionData = { scope, state, codeChallenge, codeVerifier };
|
|
1659
1725
|
// we need a session to save the state
|
|
1660
1726
|
await this.storeSessionData(event, sessionData);
|
|
1661
|
-
|
|
1727
|
+
// if we have an upstream client, this call will save the original /authorize call and redirect to
|
|
1728
|
+
// the upstream auth server's /authorize endpoint
|
|
1729
|
+
if ((upstream || this.server.oAuthAuthServer?.authServer.upstreamClient) && next && this.server.oAuthAuthServer) {
|
|
1730
|
+
await this.server.oAuthAuthServer.saveDownstreamAuthzCodeFlow(event, new URL(next), upstream);
|
|
1731
|
+
}
|
|
1732
|
+
// the following call returns a constructed url for the
|
|
1733
|
+
// auth server's /authorize endpoint
|
|
1734
|
+
const { url, error, error_description } = await this.startAuthorizationCodeFlow(state, { scope, codeChallenge, pkce: true, upstream });
|
|
1662
1735
|
if (error || !url) {
|
|
1663
1736
|
const ce = CrossauthError.fromOAuthError(error ?? "server_error", error_description);
|
|
1664
1737
|
return {
|
|
@@ -1675,7 +1748,7 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1675
1748
|
msg: `OAuth redirect`,
|
|
1676
1749
|
}));
|
|
1677
1750
|
}
|
|
1678
|
-
throw this.redirect(302, url);
|
|
1751
|
+
throw this.redirect(302, url); // to auth server's authorize endpoint
|
|
1679
1752
|
}
|
|
1680
1753
|
catch (e) {
|
|
1681
1754
|
if (SvelteKitServer.isSvelteKitRedirect(e))
|
|
@@ -1700,42 +1773,39 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1700
1773
|
const ce = new CrossauthError(ErrorCode.Configuration, "If tokenResponseType is " + this.tokenResponseType + ", use load not get");
|
|
1701
1774
|
return this.errorFn(this.server, event, ce);
|
|
1702
1775
|
}
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
const ce = new CrossauthError(ErrorCode.Unauthorized, "Authorization flows are not supported");
|
|
1708
|
-
return this.errorFn(this.server, event, ce);
|
|
1709
|
-
}
|
|
1710
|
-
CrossauthLogger.logger.debug(j({ msg: "redirectUriEndpoint, token response type " + this.tokenResponseType }));
|
|
1711
|
-
const code = event.url.searchParams.get("code") ?? "";
|
|
1712
|
-
const state = event.url.searchParams.get("state") ?? undefined;
|
|
1713
|
-
const error = event.url.searchParams.get("error") ?? undefined;
|
|
1714
|
-
const error_description = event.url.searchParams.get("error") ?? undefined;
|
|
1715
|
-
const oauthData = await this.server.sessionAdapter?.getSessionData(event, this.sessionDataName);
|
|
1716
|
-
if (oauthData?.state != state) {
|
|
1717
|
-
throw new CrossauthError(ErrorCode.Unauthorized, "State does not match");
|
|
1718
|
-
}
|
|
1719
|
-
const resp = this.errorIfIdTokenInvalid(await this.redirectEndpoint(code, oauthData?.scope, oauthData?.codeVerifier, error, error_description));
|
|
1720
|
-
if (resp.error)
|
|
1721
|
-
return this.errorFn(this.server, event, CrossauthError.fromOAuthError(resp.error, resp.error_description));
|
|
1722
|
-
if (resp.error) {
|
|
1723
|
-
const ce = CrossauthError.fromOAuthError(resp.error, resp.error_description);
|
|
1724
|
-
return await this.errorFn(this.server, event, ce);
|
|
1725
|
-
}
|
|
1726
|
-
return await this.receiveTokenFn(resp, this, event, false, this.setEventLocalsUser);
|
|
1727
|
-
}
|
|
1728
|
-
catch (e) {
|
|
1729
|
-
if (SvelteKitServer.isSvelteKitRedirect(e))
|
|
1730
|
-
throw e;
|
|
1731
|
-
if (SvelteKitServer.isSvelteKitError(e))
|
|
1732
|
-
throw e;
|
|
1733
|
-
const ce = CrossauthError.asCrossauthError(e);
|
|
1734
|
-
CrossauthLogger.logger.debug({ err: e });
|
|
1735
|
-
CrossauthLogger.logger.error({ cerr: e });
|
|
1736
|
-
//throw this.error(ce.httpStatus, ce.message);
|
|
1776
|
+
if (!(this.validFlows.includes(OAuthFlows.AuthorizationCode) ||
|
|
1777
|
+
this.validFlows.includes(OAuthFlows.AuthorizationCodeWithPKCE) ||
|
|
1778
|
+
this.validFlows.includes(OAuthFlows.OidcAuthorizationCode))) {
|
|
1779
|
+
const ce = new CrossauthError(ErrorCode.Unauthorized, "Authorization flows are not supported");
|
|
1737
1780
|
return this.errorFn(this.server, event, ce);
|
|
1738
1781
|
}
|
|
1782
|
+
CrossauthLogger.logger.debug(j({ msg: "redirectUriEndpoint, token response type " + this.tokenResponseType }));
|
|
1783
|
+
const code = event.url.searchParams.get("code") ?? "";
|
|
1784
|
+
const state = event.url.searchParams.get("state") ?? undefined;
|
|
1785
|
+
const error = event.url.searchParams.get("error") ?? undefined;
|
|
1786
|
+
const error_description = event.url.searchParams.get("error") ?? undefined;
|
|
1787
|
+
const oauthData = await this.server.sessionAdapter?.getSessionData(event, this.sessionDataName);
|
|
1788
|
+
if (oauthData?.state != state) {
|
|
1789
|
+
return json({
|
|
1790
|
+
error: "access_denied",
|
|
1791
|
+
error_description: "state does not match"
|
|
1792
|
+
}, { status: 403 });
|
|
1793
|
+
}
|
|
1794
|
+
// call the auth server's token endpoint to exchange the code for new tokens
|
|
1795
|
+
const resp = this.errorIfIdTokenInvalid(await this.redirectEndpoint({
|
|
1796
|
+
code,
|
|
1797
|
+
scope: oauthData?.scope,
|
|
1798
|
+
codeVerifier: oauthData?.codeVerifier,
|
|
1799
|
+
error,
|
|
1800
|
+
errorDescription: error_description
|
|
1801
|
+
}));
|
|
1802
|
+
if (resp.error)
|
|
1803
|
+
return this.errorFn(this.server, event, CrossauthError.fromOAuthError(resp.error, resp.error_description));
|
|
1804
|
+
if (resp.error) {
|
|
1805
|
+
const ce = CrossauthError.fromOAuthError(resp.error, resp.error_description);
|
|
1806
|
+
return await this.errorFn(this.server, event, ce);
|
|
1807
|
+
}
|
|
1808
|
+
return await this.receiveTokenFn(resp, this, event, false, this.setEventLocalsUser);
|
|
1739
1809
|
},
|
|
1740
1810
|
load: async (event) => {
|
|
1741
1811
|
if ( /*this.tokenResponseType == "saveInSessionAndRedirect" ||*/this.tokenResponseType == "sendJson" || this.tokenResponseType == "saveInSessionAndLoad") {
|
|
@@ -1765,7 +1835,13 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1765
1835
|
if (oauthData?.state != state) {
|
|
1766
1836
|
throw new CrossauthError(ErrorCode.Unauthorized, "State does not match");
|
|
1767
1837
|
}
|
|
1768
|
-
const resp = this.errorIfIdTokenInvalid(await this.redirectEndpoint(
|
|
1838
|
+
const resp = this.errorIfIdTokenInvalid(await this.redirectEndpoint({
|
|
1839
|
+
code,
|
|
1840
|
+
scope: oauthData?.scope,
|
|
1841
|
+
codeVerifier: oauthData?.codeVerifier,
|
|
1842
|
+
error,
|
|
1843
|
+
errorDescription: error_description
|
|
1844
|
+
}));
|
|
1769
1845
|
if (resp.error)
|
|
1770
1846
|
return {
|
|
1771
1847
|
ok: false,
|
|
@@ -1919,6 +1995,7 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1919
1995
|
const ce = new CrossauthError(ErrorCode.Configuration, "If tokenResponseType is " + this.tokenResponseType + ", use actions not post");
|
|
1920
1996
|
return this.errorFn(this.server, event, ce);
|
|
1921
1997
|
}
|
|
1998
|
+
CrossauthLogger.logger.debug(j({ msg: "Refresh token flow started" }));
|
|
1922
1999
|
let formData = undefined;
|
|
1923
2000
|
try {
|
|
1924
2001
|
if (!(this.validFlows.includes(OAuthFlows.RefreshToken))) {
|
|
@@ -1988,6 +2065,7 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
1988
2065
|
const ce = new CrossauthError(ErrorCode.Configuration, "If tokenResponseType is " + this.tokenResponseType + ", use post not load");
|
|
1989
2066
|
throw ce;
|
|
1990
2067
|
}
|
|
2068
|
+
CrossauthLogger.logger.debug(j({ msg: "Refresh token flow started" }));
|
|
1991
2069
|
let formData = undefined;
|
|
1992
2070
|
try {
|
|
1993
2071
|
if (!(this.validFlows.includes(OAuthFlows.RefreshToken))) {
|
|
@@ -2033,7 +2111,12 @@ export class SvelteKitOAuthClient extends OAuthClientBackend {
|
|
|
2033
2111
|
throw ce;
|
|
2034
2112
|
}
|
|
2035
2113
|
const resp = this.errorIfIdTokenInvalid(await this.refreshTokenFlow(refreshToken));
|
|
2036
|
-
const resp2 = await this.receiveTokenFn(resp, this, event, false, this.setEventLocalsUser)
|
|
2114
|
+
const resp2 = await this.receiveTokenFn(resp, this, event, false, this.setEventLocalsUser);
|
|
2115
|
+
if (!resp2) {
|
|
2116
|
+
return {
|
|
2117
|
+
ok: true,
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2037
2120
|
if (resp2 instanceof Response)
|
|
2038
2121
|
throw new CrossauthError(ErrorCode.Configuration, "Refresh token flow should return an object not Response");
|
|
2039
2122
|
return resp2;
|
|
@@ -14,6 +14,7 @@ export interface AuthorizeQueryType {
|
|
|
14
14
|
state: string;
|
|
15
15
|
code_challenge?: string;
|
|
16
16
|
code_challenge_method?: string;
|
|
17
|
+
next?: string;
|
|
17
18
|
}
|
|
18
19
|
export interface ReturnBase {
|
|
19
20
|
ok: boolean;
|
|
@@ -177,6 +178,11 @@ export interface SvelteKitAuthorizationServerOptions extends OAuthAuthorizationS
|
|
|
177
178
|
redirect?: any;
|
|
178
179
|
/** Pass the Sveltekit error function */
|
|
179
180
|
error?: any;
|
|
181
|
+
/**
|
|
182
|
+
* This is used when there is an upstream client to save the tokens.
|
|
183
|
+
* Default `oauth`.
|
|
184
|
+
*/
|
|
185
|
+
sessionDataName?: string;
|
|
180
186
|
}
|
|
181
187
|
/**
|
|
182
188
|
* This class implements an OAuth authorization server, serving endpoints
|
|
@@ -263,6 +269,7 @@ export declare class SvelteKitAuthorizationServer {
|
|
|
263
269
|
private jwksEndpointUrl;
|
|
264
270
|
readonly redirect: any;
|
|
265
271
|
readonly error: any;
|
|
272
|
+
private sessionDataName;
|
|
266
273
|
/**
|
|
267
274
|
* Constructor
|
|
268
275
|
* @param svelteKitServer the SvelteKit server this belongs to
|
|
@@ -297,7 +304,10 @@ export declare class SvelteKitAuthorizationServer {
|
|
|
297
304
|
private setRefreshTokenCookie;
|
|
298
305
|
private requireGetParam;
|
|
299
306
|
private requireBodyParam;
|
|
300
|
-
|
|
307
|
+
getAuthorizeQuery(url: URL): {
|
|
308
|
+
query?: AuthorizeQueryType;
|
|
309
|
+
error: ReturnBase;
|
|
310
|
+
};
|
|
301
311
|
private getMfaChallengeQuery;
|
|
302
312
|
private mfaAuthenticators;
|
|
303
313
|
private mfaChallenge;
|
|
@@ -345,6 +355,14 @@ export declare class SvelteKitAuthorizationServer {
|
|
|
345
355
|
[key: string]: any;
|
|
346
356
|
}, sessionDataName: string): Promise<void>;
|
|
347
357
|
private redirectError;
|
|
358
|
+
/**
|
|
359
|
+
* Called from the client side of an auth server with an upstream auth server
|
|
360
|
+
* @param event Sveltekit event
|
|
361
|
+
* @param url URL the originating client originally made to our auth server's /authorize endpoint
|
|
362
|
+
* @param upstream the label of the upstream client if we have seeral
|
|
363
|
+
* @returns
|
|
364
|
+
*/
|
|
365
|
+
saveDownstreamAuthzCodeFlow(event: RequestEvent, url: URL, upstream?: string): Promise<ReturnBase | undefined>;
|
|
348
366
|
/**
|
|
349
367
|
* `load` and `actions` functions for the authorize endpoint.
|
|
350
368
|
*
|
|
@@ -366,7 +384,7 @@ export declare class SvelteKitAuthorizationServer {
|
|
|
366
384
|
post: (event: RequestEvent) => Promise<Response>;
|
|
367
385
|
};
|
|
368
386
|
readonly upstreamRedirectUriEndpoint: {
|
|
369
|
-
get: (event: RequestEvent) => Promise<void | Response>;
|
|
387
|
+
get: (event: RequestEvent, upstream?: string) => Promise<void | Response>;
|
|
370
388
|
};
|
|
371
389
|
/**
|
|
372
390
|
* `get` and `post` functions for the mfa/authenticators endpoint.
|