@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.
@@ -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(event, name) {
254
- const val = event.url.searchParams.get(name);
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(event) {
273
- let error = this.requireGetParam(event, "response_type");
277
+ getAuthorizeQuery(url) {
278
+ let error = this.requireGetParam(url, "response_type");
274
279
  if (error)
275
280
  return { error };
276
- error = this.requireGetParam(event, "client_id");
281
+ error = this.requireGetParam(url, "client_id");
277
282
  if (error)
278
283
  return { error };
279
- error = this.requireGetParam(event, "redirect_uri");
284
+ error = this.requireGetParam(url, "redirect_uri");
280
285
  if (error)
281
286
  return { error };
282
- error = this.requireGetParam(event, "state");
287
+ error = this.requireGetParam(url, "state");
283
288
  if (error)
284
289
  return { error };
285
- const response_type = event.url.searchParams.get("response_type") ?? "";
286
- const client_id = event.url.searchParams.get("client_id") ?? "";
287
- const redirect_uri = event.url.searchParams.get("redirect_uri") ?? "";
288
- const scope = event.url.searchParams.get("scope") ?? undefined;
289
- const state = event.url.searchParams.get("state") ?? "";
290
- const code_challenge = event.url.searchParams.get("code_challenge") ?? undefined;
291
- const code_challenge_method = event.url.searchParams.get("code_challenge_method") ?? undefined;
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
- const resp = await this.authServer.upstreamClient.startAuthorizationCodeFlow(state, query.scope, query.code_challenge, query.code_challenge != undefined);
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 (!this.authServer.upstreamClientOptions.options.redirect_uri) {
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 = this.authServer.upstreamClientOptions.sessionDataName ?? DEFAULT_UPSTREAM_SESSION_DATA_NAME;
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 valid",
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
- this.authServer.upstreamClient && this.authServer.upstreamClientOptions) {
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
- if (!this.authServer.upstreamClient || !this.authServer.upstreamClientOptions.tokenMergeFn) {
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 = this.authServer.upstreamClientOptions.sessionDataName ?? DEFAULT_UPSTREAM_SESSION_DATA_NAME;
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
- const resp = await this.authServer.upstreamClient.redirectEndpoint(code, oauthData?.scope, oauthData?.codeVerifier, error, error_description);
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 && this.authServer.upstreamClientOptions.accessTokenIsJwt) {
1041
- const resp1 = await this.authServer.upstreamClient.getAccessPayload(resp.access_token, false);
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
- const mergeResponse = await this.authServer.upstreamClientOptions.tokenMergeFn(access_token ?? "", resp.id_payload, this.authServer.userStorage);
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
- const ret = await this.authServer.createTokensFromPayload(oauthData.orig_client_id, mergeResponse.access_payload, mergeResponse.id_payload);
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 {