@crossauth/sveltekit 1.1.2 → 1.1.3
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
|
@@ -5,6 +5,8 @@ import { CrossauthError, CrossauthLogger, j, OAuthFlows, ErrorCode } from '@cros
|
|
|
5
5
|
import { json } from '@sveltejs/kit';
|
|
6
6
|
import { JsonOrFormData } from './utils';
|
|
7
7
|
import {} from 'cookie';
|
|
8
|
+
import { OAuthClientBackend } from '@crossauth/backend';
|
|
9
|
+
import {} from './tests/sveltemocks';
|
|
8
10
|
const DEFAULT_UPSTREAM_SESSION_DATA_NAME = "upstreamoauth";
|
|
9
11
|
;
|
|
10
12
|
;
|
|
@@ -97,6 +99,7 @@ export class SvelteKitAuthorizationServer {
|
|
|
97
99
|
jwksEndpointUrl = "/oauth/jwks";
|
|
98
100
|
redirect;
|
|
99
101
|
error;
|
|
102
|
+
sessionDataName = "oauth";
|
|
100
103
|
/**
|
|
101
104
|
* Constructor
|
|
102
105
|
* @param svelteKitServer the SvelteKit server this belongs to
|
|
@@ -115,6 +118,7 @@ export class SvelteKitAuthorizationServer {
|
|
|
115
118
|
this.error = options.error;
|
|
116
119
|
this.authServer =
|
|
117
120
|
new OAuthAuthorizationServer(this.clientStorage, keyStorage, authenticators, options);
|
|
121
|
+
let server = svelteKitServer;
|
|
118
122
|
setParameter("loginUrl", ParamType.String, this, options, "LOGIN_URL");
|
|
119
123
|
setParameter("refreshTokenType", ParamType.String, this, options, "OAUTH_REFRESH_TOKEN_TYPE");
|
|
120
124
|
setParameter("refreshTokenCookieName", ParamType.String, this, options, "OAUTH_REFRESH_TOKEN_COOKIE_NAME");
|
|
@@ -126,6 +130,7 @@ export class SvelteKitAuthorizationServer {
|
|
|
126
130
|
setParameter("authorizeEndpointUrl", ParamType.String, this, options, "OAUTH_AUTHORIZE_ENDPOINT");
|
|
127
131
|
setParameter("tokenEndpointUrl", ParamType.String, this, options, "OAUTH_TOKEN_ENDPOINT");
|
|
128
132
|
setParameter("jwksEndpointUrl", ParamType.String, this, options, "OAUTH_JWKS_ENDPOINT");
|
|
133
|
+
setParameter("sessionDataName", ParamType.String, this, options, "OAUTH_SESSION_DATA_NAME");
|
|
129
134
|
if (this.refreshTokenType != "json") {
|
|
130
135
|
if (this.svelteKitServer.sessionServer?.enableCsrfProtection == true) {
|
|
131
136
|
this.csrfTokens = this.svelteKitServer.sessionServer.sessionManager.csrfTokens;
|
|
@@ -250,8 +255,8 @@ export class SvelteKitAuthorizationServer {
|
|
|
250
255
|
cookieParams.secure = true;
|
|
251
256
|
event.cookies.set(this.refreshTokenCookieName, token, cookieParams);
|
|
252
257
|
}
|
|
253
|
-
requireGetParam(
|
|
254
|
-
const val =
|
|
258
|
+
requireGetParam(url, name) {
|
|
259
|
+
const val = url.searchParams.get(name);
|
|
255
260
|
if (!val)
|
|
256
261
|
return {
|
|
257
262
|
ok: false,
|
|
@@ -269,26 +274,27 @@ export class SvelteKitAuthorizationServer {
|
|
|
269
274
|
};
|
|
270
275
|
return undefined;
|
|
271
276
|
}
|
|
272
|
-
getAuthorizeQuery(
|
|
273
|
-
let error = this.requireGetParam(
|
|
277
|
+
getAuthorizeQuery(url) {
|
|
278
|
+
let error = this.requireGetParam(url, "response_type");
|
|
274
279
|
if (error)
|
|
275
280
|
return { error };
|
|
276
|
-
error = this.requireGetParam(
|
|
281
|
+
error = this.requireGetParam(url, "client_id");
|
|
277
282
|
if (error)
|
|
278
283
|
return { error };
|
|
279
|
-
error = this.requireGetParam(
|
|
284
|
+
error = this.requireGetParam(url, "redirect_uri");
|
|
280
285
|
if (error)
|
|
281
286
|
return { error };
|
|
282
|
-
error = this.requireGetParam(
|
|
287
|
+
error = this.requireGetParam(url, "state");
|
|
283
288
|
if (error)
|
|
284
289
|
return { error };
|
|
285
|
-
const response_type =
|
|
286
|
-
const client_id =
|
|
287
|
-
const redirect_uri =
|
|
288
|
-
const scope =
|
|
289
|
-
const state =
|
|
290
|
-
const code_challenge =
|
|
291
|
-
const code_challenge_method =
|
|
290
|
+
const response_type = url.searchParams.get("response_type") ?? "";
|
|
291
|
+
const client_id = url.searchParams.get("client_id") ?? "";
|
|
292
|
+
const redirect_uri = url.searchParams.get("redirect_uri") ?? "";
|
|
293
|
+
const scope = url.searchParams.get("scope") ?? undefined;
|
|
294
|
+
const state = url.searchParams.get("state") ?? "";
|
|
295
|
+
const code_challenge = url.searchParams.get("code_challenge") ?? undefined;
|
|
296
|
+
const code_challenge_method = url.searchParams.get("code_challenge_method") ?? undefined;
|
|
297
|
+
const next = url.searchParams.get("next") ?? undefined;
|
|
292
298
|
let query = {
|
|
293
299
|
response_type,
|
|
294
300
|
client_id,
|
|
@@ -297,6 +303,7 @@ export class SvelteKitAuthorizationServer {
|
|
|
297
303
|
state,
|
|
298
304
|
code_challenge,
|
|
299
305
|
code_challenge_method,
|
|
306
|
+
next,
|
|
300
307
|
};
|
|
301
308
|
return { query, error: { error: "Unknown error", error_description: "Unknown error", ok: true } };
|
|
302
309
|
}
|
|
@@ -604,6 +611,117 @@ export class SvelteKitAuthorizationServer {
|
|
|
604
611
|
throw this.error(500, error_description);
|
|
605
612
|
}
|
|
606
613
|
}
|
|
614
|
+
/**
|
|
615
|
+
* Called from the client side of an auth server with an upstream auth server
|
|
616
|
+
* @param event Sveltekit event
|
|
617
|
+
* @param url URL the originating client originally made to our auth server's /authorize endpoint
|
|
618
|
+
* @param upstream the label of the upstream client if we have seeral
|
|
619
|
+
* @returns
|
|
620
|
+
*/
|
|
621
|
+
async saveDownstreamAuthzCodeFlow(event, url, upstream) {
|
|
622
|
+
let upstreamClient = undefined;
|
|
623
|
+
let upstreamClientOptions = undefined;
|
|
624
|
+
let upstreamLabel = upstream;
|
|
625
|
+
if (this.authServer.upstreamClient && this.authServer.upstreamClientOptions) {
|
|
626
|
+
upstreamClient = this.authServer.upstreamClient;
|
|
627
|
+
upstreamClientOptions = this.authServer.upstreamClientOptions;
|
|
628
|
+
}
|
|
629
|
+
else if (this.authServer.upstreamClients && this.authServer.upstreamClientOptionss) {
|
|
630
|
+
if (upstreamLabel) {
|
|
631
|
+
if (upstreamLabel in this.authServer.upstreamClients) {
|
|
632
|
+
upstreamClient = this.authServer.upstreamClients[upstreamLabel];
|
|
633
|
+
upstreamClientOptions = this.authServer.upstreamClientOptionss[upstreamLabel];
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
CrossauthLogger.logger.error(j({ "msg": "No upstream client defined for " + upstreamLabel }));
|
|
637
|
+
return {
|
|
638
|
+
ok: false,
|
|
639
|
+
error: "server_error",
|
|
640
|
+
error_description: "No upstream client defined for " + upstreamLabel
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
// this may be legitimate, if the request is to log in with a local account
|
|
646
|
+
CrossauthLogger.logger.warn(j({ "msg": "1 upstreamClients defined but no upstream parameter given in authorize request" }));
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
if (upstreamClient && upstreamClientOptions) {
|
|
650
|
+
const state = Crypto.randomValue(32);
|
|
651
|
+
let resp = this.getAuthorizeQuery(url);
|
|
652
|
+
if (!resp.query)
|
|
653
|
+
return resp.error;
|
|
654
|
+
let query = resp.query;
|
|
655
|
+
CrossauthLogger.logger.debug(j({ "msg": `Have upstream client with redirect_uri ${query.redirect_uri}` }));
|
|
656
|
+
if (query.response_type == "code") {
|
|
657
|
+
// validate client
|
|
658
|
+
let client;
|
|
659
|
+
try {
|
|
660
|
+
client = await this.clientStorage.getClientById(query.client_id);
|
|
661
|
+
}
|
|
662
|
+
catch (e) {
|
|
663
|
+
CrossauthLogger.logger.debug(j({ err: e }));
|
|
664
|
+
throw new CrossauthError(ErrorCode.Unauthorized, "Client is unauthorized");
|
|
665
|
+
}
|
|
666
|
+
let scopes = undefined;
|
|
667
|
+
if (query.scope)
|
|
668
|
+
scopes = decodeURIComponent(query.scope).split(" ");
|
|
669
|
+
scopes = scopes?.filter((a) => (a.length > 0));
|
|
670
|
+
// construct a url to call the /authorize endpoint on the upstream auth server
|
|
671
|
+
const resp = await upstreamClient.startAuthorizationCodeFlow(state, { scope: query.scope, codeChallenge: query.code_challenge, pkce: query.code_challenge != undefined });
|
|
672
|
+
if (resp.error) {
|
|
673
|
+
return {
|
|
674
|
+
ok: false,
|
|
675
|
+
error: resp.error,
|
|
676
|
+
error_description: resp.error_description,
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
// create an authorization code
|
|
681
|
+
const codeResp = await this.authServer.getAuthorizationCode(client, query.redirect_uri, scopes, state, query.code_challenge, query.code_challenge_method);
|
|
682
|
+
if (!codeResp.code) {
|
|
683
|
+
return {
|
|
684
|
+
ok: false,
|
|
685
|
+
error: "server_error",
|
|
686
|
+
error_description: "Couldn't create authorization code",
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
// store the original /authorize request in the session
|
|
690
|
+
const sessionData = {
|
|
691
|
+
scope: query.scope,
|
|
692
|
+
state,
|
|
693
|
+
code: codeResp.code,
|
|
694
|
+
orig_client_id: query.client_id,
|
|
695
|
+
orig_redirect_uri: query.redirect_uri,
|
|
696
|
+
orig_state: query.state,
|
|
697
|
+
upstream_label: upstreamLabel,
|
|
698
|
+
next: query.next,
|
|
699
|
+
};
|
|
700
|
+
if (!upstreamClientOptions.options.redirect_uri) {
|
|
701
|
+
return {
|
|
702
|
+
ok: false,
|
|
703
|
+
error: "server_error",
|
|
704
|
+
error_description: "redirect uri not given for upstream client",
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
const sessionDataName = upstreamClientOptions.sessionDataName ?? DEFAULT_UPSTREAM_SESSION_DATA_NAME;
|
|
708
|
+
await this.storeSessionData(event, sessionData, sessionDataName);
|
|
709
|
+
let url = resp.url;
|
|
710
|
+
CrossauthLogger.logger.debug(j({ msg: "upstream url " + url }));
|
|
711
|
+
//CrossauthLogger.logger.debug(j({msg: "upstream base url " + this.authServer.upstreamAuthServerBaseUrl}))
|
|
712
|
+
// Redirect to the /authorize endpoint on the upstream server
|
|
713
|
+
throw this.redirect(302, url);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
return {
|
|
718
|
+
ok: false,
|
|
719
|
+
error: "invalid_request",
|
|
720
|
+
error_description: "authorize can only be called with response_type code",
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
607
725
|
/**
|
|
608
726
|
* `load` and `actions` functions for the authorize endpoint.
|
|
609
727
|
*
|
|
@@ -618,12 +736,40 @@ export class SvelteKitAuthorizationServer {
|
|
|
618
736
|
}
|
|
619
737
|
// handle the case where we have an upstream authz server
|
|
620
738
|
// either throw redirect or return error
|
|
739
|
+
let upstreamClient = undefined;
|
|
740
|
+
let upstreamClientOptions = undefined;
|
|
741
|
+
let upstreamLabel = undefined;
|
|
621
742
|
if (this.authServer.upstreamClient && this.authServer.upstreamClientOptions) {
|
|
743
|
+
upstreamClient = this.authServer.upstreamClient;
|
|
744
|
+
upstreamClientOptions = this.authServer.upstreamClientOptions;
|
|
745
|
+
}
|
|
746
|
+
else if (this.authServer.upstreamClients && this.authServer.upstreamClientOptionss) {
|
|
747
|
+
upstreamLabel = event.url.searchParams.get("upstream") ?? undefined;
|
|
748
|
+
if (upstreamLabel) {
|
|
749
|
+
if (upstreamLabel in this.authServer.upstreamClients) {
|
|
750
|
+
upstreamClient = this.authServer.upstreamClients[upstreamLabel];
|
|
751
|
+
upstreamClientOptions = this.authServer.upstreamClientOptionss[upstreamLabel];
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
CrossauthLogger.logger.error(j({ "msg": "No upstream client defined for " + upstreamLabel }));
|
|
755
|
+
return {
|
|
756
|
+
ok: false,
|
|
757
|
+
error: "server_error",
|
|
758
|
+
error_description: "No upstream client defined for " + upstreamLabel
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
CrossauthLogger.logger.warn(j({ "msg": "2 upstreamClients defined but no upstream parameter given in authorize request" }));
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (upstreamClient && upstreamClientOptions) {
|
|
622
767
|
const state = Crypto.randomValue(32);
|
|
623
|
-
let resp = this.getAuthorizeQuery(event);
|
|
768
|
+
let resp = this.getAuthorizeQuery(event.url);
|
|
624
769
|
if (!resp.query)
|
|
625
770
|
return resp.error;
|
|
626
771
|
let query = resp.query;
|
|
772
|
+
CrossauthLogger.logger.debug(j({ "msg": `Have upstream client with redirect_uri ${query.redirect_uri}` }));
|
|
627
773
|
if (query.response_type == "code") {
|
|
628
774
|
// validate client
|
|
629
775
|
let client;
|
|
@@ -639,7 +785,8 @@ export class SvelteKitAuthorizationServer {
|
|
|
639
785
|
if (query.scope)
|
|
640
786
|
scopes = decodeURIComponent(query.scope).split(" ");
|
|
641
787
|
scopes = scopes?.filter((a) => (a.length > 0));
|
|
642
|
-
|
|
788
|
+
// construct a url to call the /authorize endpoint on the upstream auth server
|
|
789
|
+
const resp = await upstreamClient.startAuthorizationCodeFlow(state, { scope: query.scope, codeChallenge: query.code_challenge, pkce: query.code_challenge != undefined });
|
|
643
790
|
if (resp.error) {
|
|
644
791
|
return {
|
|
645
792
|
ok: false,
|
|
@@ -648,6 +795,7 @@ export class SvelteKitAuthorizationServer {
|
|
|
648
795
|
};
|
|
649
796
|
}
|
|
650
797
|
else {
|
|
798
|
+
// create an authorization code
|
|
651
799
|
const codeResp = await this.authServer.getAuthorizationCode(client, query.redirect_uri, scopes, state, query.code_challenge, query.code_challenge_method);
|
|
652
800
|
if (!codeResp.code) {
|
|
653
801
|
return {
|
|
@@ -656,15 +804,18 @@ export class SvelteKitAuthorizationServer {
|
|
|
656
804
|
error_description: "Couldn't create authorization code",
|
|
657
805
|
};
|
|
658
806
|
}
|
|
807
|
+
// store the original /authorize request in the session
|
|
659
808
|
const sessionData = {
|
|
660
809
|
scope: query.scope,
|
|
661
810
|
state,
|
|
662
811
|
code: codeResp.code,
|
|
663
812
|
orig_client_id: query.client_id,
|
|
664
813
|
orig_redirect_uri: query.redirect_uri,
|
|
665
|
-
orig_state: query.state
|
|
814
|
+
orig_state: query.state,
|
|
815
|
+
upstream_label: upstreamLabel,
|
|
816
|
+
next: query.next,
|
|
666
817
|
};
|
|
667
|
-
if (!
|
|
818
|
+
if (!upstreamClientOptions.options.redirect_uri) {
|
|
668
819
|
return {
|
|
669
820
|
ok: false,
|
|
670
821
|
error: "server_error",
|
|
@@ -672,16 +823,8 @@ export class SvelteKitAuthorizationServer {
|
|
|
672
823
|
};
|
|
673
824
|
}
|
|
674
825
|
// we need a session to save the state
|
|
675
|
-
const sessionDataName =
|
|
826
|
+
const sessionDataName = upstreamClientOptions.sessionDataName ?? DEFAULT_UPSTREAM_SESSION_DATA_NAME;
|
|
676
827
|
await this.storeSessionData(event, sessionData, sessionDataName);
|
|
677
|
-
//await this.authServer.setAuthorizationCodeData(codeResp.code, sessionData);
|
|
678
|
-
/*let url = this.authServer.upstreamClientOptions.authServerBaseUrl +
|
|
679
|
-
"?response_type=code" +
|
|
680
|
-
"&redirect_uri=" + encodeURIComponent(this.authServer.upstreamClientOptions.options.redirect_uri)
|
|
681
|
-
"&client_id=" + encodeURIComponent(this.authServer.upstreamClient.client_id);
|
|
682
|
-
if (this.authServer.upstreamClient.client_secret) url += "&client_secret=" + encodeURIComponent(this.authServer.upstreamClient.client_secret);
|
|
683
|
-
if (query.state) url += "&state=" + encodeURIComponent(query.state);
|
|
684
|
-
if (this.authServer.upstreamClientOptions.scopes) url += encodeURIComponent(this.authServer.upstreamClientOptions.scopes.join(" "));*/
|
|
685
828
|
let url = resp.url;
|
|
686
829
|
CrossauthLogger.logger.debug(j({ msg: "upstream url " + url }));
|
|
687
830
|
//CrossauthLogger.logger.debug(j({msg: "upstream base url " + this.authServer.upstreamAuthServerBaseUrl}))
|
|
@@ -699,7 +842,7 @@ export class SvelteKitAuthorizationServer {
|
|
|
699
842
|
// we don't have an upstream server
|
|
700
843
|
if (!event.locals.user)
|
|
701
844
|
return this.redirect(302, this.loginUrl + "?next=" + encodeURIComponent(event.request.url));
|
|
702
|
-
let resp = this.getAuthorizeQuery(event);
|
|
845
|
+
let resp = this.getAuthorizeQuery(event.url);
|
|
703
846
|
if (!resp.query)
|
|
704
847
|
return resp.error;
|
|
705
848
|
let query = resp.query;
|
|
@@ -718,7 +861,7 @@ export class SvelteKitAuthorizationServer {
|
|
|
718
861
|
}
|
|
719
862
|
else {
|
|
720
863
|
CrossauthLogger.logger.error(j({
|
|
721
|
-
msg: "authorize parameter
|
|
864
|
+
msg: "authorize parameter invalid",
|
|
722
865
|
user: event.locals.user?.username
|
|
723
866
|
}));
|
|
724
867
|
}
|
|
@@ -903,8 +1046,28 @@ export class SvelteKitAuthorizationServer {
|
|
|
903
1046
|
const { client_id, client_secret } = this.getClientIdAndSecret(formData, event);
|
|
904
1047
|
// if the grant type is authorization_code and we have an upstream client
|
|
905
1048
|
// defined, we should already have tokens stored with the authoriazion code
|
|
1049
|
+
let upstreamClient = undefined;
|
|
1050
|
+
let upstreamClientOptions = undefined;
|
|
1051
|
+
let upstreamLabel = undefined;
|
|
1052
|
+
if (formData.grant_type == "authorization_code") {
|
|
1053
|
+
if (this.authServer.upstreamClient && this.authServer.upstreamClientOptions) {
|
|
1054
|
+
upstreamClient = this.authServer.upstreamClient;
|
|
1055
|
+
upstreamClientOptions = this.authServer.upstreamClientOptions;
|
|
1056
|
+
}
|
|
1057
|
+
else {
|
|
1058
|
+
const codeData = await this.authServer.getAuthorizationCodeData(formData.code);
|
|
1059
|
+
if (codeData?.upstream && this.authServer.upstreamClients && this.authServer.upstreamClientOptionss) {
|
|
1060
|
+
upstreamClient = this.authServer.upstreamClients[codeData?.upstream];
|
|
1061
|
+
upstreamClientOptions = this.authServer.upstreamClientOptionss[codeData?.upstream];
|
|
1062
|
+
upstreamLabel = codeData.upstream;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
906
1066
|
if (formData.grant_type == "authorization_code" &&
|
|
907
|
-
|
|
1067
|
+
upstreamClient && upstreamClientOptions) {
|
|
1068
|
+
// if it is the authorization code flow and we have an upstream client,
|
|
1069
|
+
// get the tokens that the our upstream redirect uri saved, keyed on
|
|
1070
|
+
// the authorization code
|
|
908
1071
|
const codeData = await this.authServer.getAuthorizationCodeData(formData.code);
|
|
909
1072
|
if (codeData == undefined) {
|
|
910
1073
|
return json({
|
|
@@ -916,12 +1079,42 @@ export class SvelteKitAuthorizationServer {
|
|
|
916
1079
|
return json({ error: "access_denied", error_description: "No access token was issued" }, { status: 401 });
|
|
917
1080
|
}
|
|
918
1081
|
await this.authServer.deleteAuthorizationCodeData(formData.code);
|
|
1082
|
+
// determine whether we are also creating a refresh token
|
|
1083
|
+
let createRefreshToken = false;
|
|
1084
|
+
if (this.authServer.issueRefreshToken) {
|
|
1085
|
+
createRefreshToken = true;
|
|
1086
|
+
}
|
|
1087
|
+
if (this.authServer.issueRefreshToken &&
|
|
1088
|
+
this.authServer.rollingRefreshToken) {
|
|
1089
|
+
createRefreshToken = true;
|
|
1090
|
+
}
|
|
1091
|
+
// get client
|
|
1092
|
+
const clientResponse = await this.authServer.getClientById(client_id);
|
|
1093
|
+
if (!clientResponse.client)
|
|
1094
|
+
return json({
|
|
1095
|
+
error: clientResponse.error,
|
|
1096
|
+
error_description: clientResponse.error_description
|
|
1097
|
+
}, { status: CrossauthError.fromOAuthError(clientResponse.error ?? "server_error").httpStatus });
|
|
1098
|
+
const client = clientResponse.client;
|
|
1099
|
+
let refreshToken = await this.authServer.createRefreshToken(client, { upstreamRefreshToken: codeData.refresh_token, upstreamLabel: upstreamLabel });
|
|
1100
|
+
// save tokens so that this is treated as the logged in user
|
|
1101
|
+
// doesn't work - no cookie
|
|
1102
|
+
/*await this.storeSessionData(event, {
|
|
1103
|
+
access_token: codeData.access_token,
|
|
1104
|
+
id_token: codeData.id_token,
|
|
1105
|
+
id_payload: codeData.id_payload,
|
|
1106
|
+
refresh_token: refreshToken ?? upstreamLabel + ":" + codeData.refresh_token,
|
|
1107
|
+
expires_in: codeData.expires_in,
|
|
1108
|
+
upstream: upstreamLabel
|
|
1109
|
+
|
|
1110
|
+
}, this.sessionDataName)*/
|
|
919
1111
|
return json({
|
|
920
1112
|
access_token: codeData.access_token,
|
|
921
1113
|
id_token: codeData.id_token,
|
|
922
1114
|
id_payload: codeData.id_payload,
|
|
923
|
-
refresh_token: codeData.refresh_token,
|
|
1115
|
+
refresh_token: refreshToken ?? upstreamLabel + ":" + codeData.refresh_token,
|
|
924
1116
|
expires_in: codeData.expires_in,
|
|
1117
|
+
upstream: upstreamLabel,
|
|
925
1118
|
});
|
|
926
1119
|
}
|
|
927
1120
|
// if refreshTokenType is not "json", check if there
|
|
@@ -1004,11 +1197,12 @@ export class SvelteKitAuthorizationServer {
|
|
|
1004
1197
|
}
|
|
1005
1198
|
},
|
|
1006
1199
|
};
|
|
1200
|
+
// The upstream auth server will redirect to hear with an authorization code
|
|
1007
1201
|
upstreamRedirectUriEndpoint = {
|
|
1008
|
-
get: async (event) => {
|
|
1202
|
+
get: async (event, upstream) => {
|
|
1009
1203
|
let oauthData = {};
|
|
1010
1204
|
try {
|
|
1011
|
-
if (!this.authServer.upstreamClientOptions) {
|
|
1205
|
+
if (!this.authServer.upstreamClientOptions && !this.authServer.upstreamClientOptionss) {
|
|
1012
1206
|
CrossauthLogger.logger.error(j({ msg: "Cannot call upstreamRedirectUriEndpoint if upstreamClient not defined" }));
|
|
1013
1207
|
return json({
|
|
1014
1208
|
ok: false,
|
|
@@ -1016,29 +1210,61 @@ export class SvelteKitAuthorizationServer {
|
|
|
1016
1210
|
error_description: "Cannot call upstreamRedirectUriEndpoint if upstreamClient not defined"
|
|
1017
1211
|
});
|
|
1018
1212
|
}
|
|
1019
|
-
CrossauthLogger.logger.debug(j({ msg: "upstreamRedirectUriEndpoint" }));
|
|
1020
|
-
|
|
1213
|
+
CrossauthLogger.logger.debug(j({ msg: "upstreamRedirectUriEndpoint", upstream }));
|
|
1214
|
+
// find which upstream client was used
|
|
1215
|
+
let upstreamClient = undefined;
|
|
1216
|
+
let upstreamClientOptions = undefined;
|
|
1217
|
+
if (this.authServer.upstreamClient && this.authServer.upstreamClientOptions) {
|
|
1218
|
+
upstreamClient = this.authServer.upstreamClient;
|
|
1219
|
+
upstreamClientOptions = this.authServer.upstreamClientOptions;
|
|
1220
|
+
}
|
|
1221
|
+
else if (this.authServer.upstreamClients && this.authServer.upstreamClientOptionss) {
|
|
1222
|
+
if (!upstream) {
|
|
1223
|
+
CrossauthLogger.logger.error(j({ msg: "Have multiple upstream clients but upstream redirect uri not passed the upstream identifier" }));
|
|
1224
|
+
return this.redirectError(oauthData.orig_redirect_uri, "server_error", "Have multiple upstream clients but upstream redirect uri not passed the upstream identifier");
|
|
1225
|
+
}
|
|
1226
|
+
upstreamClient = this.authServer.upstreamClients[upstream];
|
|
1227
|
+
upstreamClientOptions = this.authServer.upstreamClientOptionss[upstream];
|
|
1228
|
+
}
|
|
1229
|
+
if (!upstreamClient || !upstreamClientOptions) {
|
|
1021
1230
|
CrossauthLogger.logger.error(j({ msg: "upstreamRedirectUri endpoint called but no upstreamClient or merge function set" }));
|
|
1022
1231
|
return this.redirectError(oauthData.orig_redirect_uri, "server_error", "upstreamRedirectUri endpoint called but no upstreamClient or merge function set");
|
|
1023
1232
|
}
|
|
1233
|
+
// URL contains redirect URI parameters between us and the upstream auth server
|
|
1234
|
+
// Session data contains parameters the originating client sent to our /authorize endpoint
|
|
1024
1235
|
const code = event.url.searchParams.get("code") ?? "";
|
|
1025
1236
|
const state = event.url.searchParams.get("state") ?? undefined;
|
|
1026
1237
|
const error = event.url.searchParams.get("error") ?? undefined;
|
|
1027
1238
|
const error_description = event.url.searchParams.get("error") ?? undefined;
|
|
1028
|
-
const sessionDataName =
|
|
1239
|
+
const sessionDataName = upstreamClientOptions.sessionDataName ?? DEFAULT_UPSTREAM_SESSION_DATA_NAME;
|
|
1029
1240
|
oauthData = await this.svelteKitServer.sessionAdapter?.getSessionData(event, sessionDataName) ?? {};
|
|
1241
|
+
if (this.authServer.upstreamClients && (!("upstream_label" in oauthData) || !(oauthData.upstream_label in this.authServer.upstreamClients))) {
|
|
1242
|
+
return this.redirectError(oauthData.orig_redirect_uri, "server_error", "Invalid upstream client found in saessom");
|
|
1243
|
+
}
|
|
1244
|
+
if (this.authServer.upstreamClients) {
|
|
1245
|
+
upstreamClient = this.authServer.upstreamClients[oauthData.upstream_label];
|
|
1246
|
+
}
|
|
1030
1247
|
if (oauthData?.state != state) {
|
|
1031
1248
|
CrossauthLogger.logger.error(j({ msg: "State does not match" }));
|
|
1032
1249
|
throw new CrossauthError(ErrorCode.Unauthorized, "State does not match");
|
|
1033
1250
|
}
|
|
1034
|
-
|
|
1251
|
+
// make the post to the upstream server's token endpoint to receive the tokens
|
|
1252
|
+
const resp = await upstreamClient.redirectEndpoint({
|
|
1253
|
+
code,
|
|
1254
|
+
scope: oauthData?.scope,
|
|
1255
|
+
codeVerifier: oauthData?.codeVerifier,
|
|
1256
|
+
error,
|
|
1257
|
+
errorDescription: error_description
|
|
1258
|
+
});
|
|
1035
1259
|
if (resp.error) {
|
|
1036
1260
|
CrossauthLogger.logger.error(j({ msg: resp.error_description }));
|
|
1037
1261
|
return this.redirectError(oauthData.orig_redirect_uri, resp.error, resp.error_description ?? "unknown error");
|
|
1038
1262
|
}
|
|
1263
|
+
// some servers have the access token as a JWT. For others, it is a simple string.
|
|
1264
|
+
// ID token, if present, is always a JWT
|
|
1039
1265
|
let access_token = resp.access_token;
|
|
1040
|
-
if (resp.access_token &&
|
|
1041
|
-
const resp1 = await
|
|
1266
|
+
if (resp.access_token && upstreamClientOptions.accessTokenIsJwt) {
|
|
1267
|
+
const resp1 = await upstreamClient.getAccessPayload(resp.access_token, false);
|
|
1042
1268
|
if (resp1.error)
|
|
1043
1269
|
return json(resp1);
|
|
1044
1270
|
else if (resp1.payload)
|
|
@@ -1048,9 +1274,12 @@ export class SvelteKitAuthorizationServer {
|
|
|
1048
1274
|
return this.redirectError(oauthData.orig_redirect_uri, "server_error", "No error or access payload received when querying access token");
|
|
1049
1275
|
}
|
|
1050
1276
|
}
|
|
1051
|
-
|
|
1277
|
+
// call user-supplied merge function to combine received ID token with entry in user storage
|
|
1278
|
+
const mergeResponse = await upstreamClientOptions.tokenMergeFn(access_token ?? "", resp.id_payload, this.authServer.userStorage);
|
|
1052
1279
|
if (mergeResponse.authorized) {
|
|
1053
|
-
|
|
1280
|
+
// make ID token from the merged payload. If the access token is a JWT, do the same for it
|
|
1281
|
+
const ret = await this.authServer.createTokensFromPayload(oauthData.orig_client_id, typeof (mergeResponse.access_payload) == "string" ? undefined : mergeResponse.access_payload, mergeResponse.id_payload);
|
|
1282
|
+
// I think this is redundant...
|
|
1054
1283
|
oauthData = {
|
|
1055
1284
|
...oauthData,
|
|
1056
1285
|
access_token: ret.access_token,
|
|
@@ -1059,6 +1288,7 @@ export class SvelteKitAuthorizationServer {
|
|
|
1059
1288
|
expires_in: ret.expires_in,
|
|
1060
1289
|
refresh_token: resp.refresh_token,
|
|
1061
1290
|
};
|
|
1291
|
+
// get the tokens saved for the originating client when it's redirect URI called our token endpoint
|
|
1062
1292
|
let authzData = await this.authServer.getAuthorizationCodeData(oauthData.code);
|
|
1063
1293
|
authzData = {
|
|
1064
1294
|
...authzData,
|
|
@@ -1066,9 +1296,13 @@ export class SvelteKitAuthorizationServer {
|
|
|
1066
1296
|
id_token: ret.id_token,
|
|
1067
1297
|
id_payload: ret.id_token,
|
|
1068
1298
|
refresh_token: resp.refresh_token,
|
|
1069
|
-
expires_in: ret.expires_in
|
|
1299
|
+
expires_in: ret.expires_in,
|
|
1300
|
+
upstream
|
|
1070
1301
|
};
|
|
1302
|
+
// save our new tokens, keyed on the authorization code
|
|
1071
1303
|
await this.authServer.setAuthorizationCodeData(oauthData.code, authzData);
|
|
1304
|
+
// redirect to the originating client's redirect URI
|
|
1305
|
+
// our token endpoint will return the saved tokens
|
|
1072
1306
|
throw this.redirect(302, this.authServer.redirect_uri(oauthData.orig_redirect_uri, oauthData.code, oauthData.orig_state));
|
|
1073
1307
|
}
|
|
1074
1308
|
else {
|