oidc-spa 8.1.9 → 8.1.11
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/backend.d.ts +27 -6
- package/backend.js +124 -139
- package/backend.js.map +1 -1
- package/core/Oidc.d.ts +28 -4
- package/core/createOidc.d.ts +12 -3
- package/core/createOidc.js +1 -1
- package/core/createOidc.js.map +1 -1
- package/core/earlyInit.d.ts +1 -0
- package/core/earlyInit.js +11 -4
- package/core/earlyInit.js.map +1 -1
- package/core/iframeMessageProtection.js +16 -18
- package/core/iframeMessageProtection.js.map +1 -1
- package/core/loginOrGoToAuthServer.js +8 -3
- package/core/loginOrGoToAuthServer.js.map +1 -1
- package/core/loginSilent.js +4 -0
- package/core/loginSilent.js.map +1 -1
- package/core/oidcClientTsUserToTokens.d.ts +1 -1
- package/core/oidcClientTsUserToTokens.js.map +1 -1
- package/core/requiredPostHydrationReplaceNavigationUrl.d.ts +6 -0
- package/core/requiredPostHydrationReplaceNavigationUrl.js +12 -0
- package/core/requiredPostHydrationReplaceNavigationUrl.js.map +1 -0
- package/entrypoint.d.ts +1 -0
- package/entrypoint.js +3 -1
- package/entrypoint.js.map +1 -1
- package/esm/angular.d.ts +14 -4
- package/esm/angular.js +155 -10
- package/esm/angular.js.map +1 -1
- package/esm/backend.d.ts +48 -0
- package/esm/backend.js +259 -0
- package/esm/backend.js.map +1 -0
- package/esm/core/Oidc.d.ts +28 -4
- package/esm/core/createOidc.d.ts +12 -3
- package/esm/core/createOidc.js +1 -1
- package/esm/core/createOidc.js.map +1 -1
- package/esm/core/earlyInit.d.ts +1 -0
- package/esm/core/earlyInit.js +11 -4
- package/esm/core/earlyInit.js.map +1 -1
- package/esm/core/iframeMessageProtection.js +16 -18
- package/esm/core/iframeMessageProtection.js.map +1 -1
- package/esm/core/loginOrGoToAuthServer.js +8 -3
- package/esm/core/loginOrGoToAuthServer.js.map +1 -1
- package/esm/core/loginSilent.js +4 -0
- package/esm/core/loginSilent.js.map +1 -1
- package/esm/core/oidcClientTsUserToTokens.d.ts +1 -1
- package/esm/core/oidcClientTsUserToTokens.js.map +1 -1
- package/esm/core/requiredPostHydrationReplaceNavigationUrl.d.ts +6 -0
- package/esm/core/requiredPostHydrationReplaceNavigationUrl.js +8 -0
- package/esm/core/requiredPostHydrationReplaceNavigationUrl.js.map +1 -0
- package/esm/entrypoint.d.ts +1 -0
- package/esm/entrypoint.js +1 -0
- package/esm/entrypoint.js.map +1 -1
- package/esm/mock/oidc.d.ts +1 -1
- package/esm/mock/oidc.js.map +1 -1
- package/esm/react/react.d.ts +1 -1
- package/esm/tanstack-start/react/accessTokenValidation_rfc9068.d.ts +12 -0
- package/esm/tanstack-start/react/accessTokenValidation_rfc9068.js +95 -0
- package/esm/tanstack-start/react/accessTokenValidation_rfc9068.js.map +1 -0
- package/esm/tanstack-start/react/apiBuilder.d.ts +27 -0
- package/esm/tanstack-start/react/apiBuilder.js +58 -0
- package/esm/tanstack-start/react/apiBuilder.js.map +1 -0
- package/esm/tanstack-start/react/createOidcSpaApi.d.ts +9 -0
- package/esm/tanstack-start/react/createOidcSpaApi.js +678 -0
- package/esm/tanstack-start/react/createOidcSpaApi.js.map +1 -0
- package/esm/tanstack-start/react/index.d.ts +3 -0
- package/esm/tanstack-start/react/index.js +4 -0
- package/esm/tanstack-start/react/index.js.map +1 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/UnifiedClientRetryForSsrLoadersError.d.ts +4 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/UnifiedClientRetryForSsrLoadersError.js +8 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/UnifiedClientRetryForSsrLoadersError.js.map +1 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/enableUnifiedClientRetryForSsrLoaders.d.ts +4 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/enableUnifiedClientRetryForSsrLoaders.js +76 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/enableUnifiedClientRetryForSsrLoaders.js.map +1 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/entrypoint.d.ts +1 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/entrypoint.js +11 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/entrypoint.js.map +1 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/index.d.ts +2 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/index.js +3 -0
- package/esm/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/index.js.map +1 -0
- package/esm/tanstack-start/react/types.d.ts +355 -0
- package/esm/tanstack-start/react/types.js +2 -0
- package/esm/tanstack-start/react/types.js.map +1 -0
- package/esm/tanstack-start/react/withHandlingOidcPostLoginNavigation.d.ts +2 -0
- package/esm/tanstack-start/react/withHandlingOidcPostLoginNavigation.js +25 -0
- package/esm/tanstack-start/react/withHandlingOidcPostLoginNavigation.js.map +1 -0
- package/esm/tools/GetterOrDirectValue.d.ts +1 -0
- package/esm/tools/GetterOrDirectValue.js +2 -0
- package/esm/tools/GetterOrDirectValue.js.map +1 -0
- package/esm/tools/ZodSchemaLike.d.ts +3 -0
- package/esm/tools/ZodSchemaLike.js +2 -0
- package/esm/tools/ZodSchemaLike.js.map +1 -0
- package/esm/tools/inferIsViteDev.d.ts +1 -0
- package/esm/tools/inferIsViteDev.js +6 -0
- package/esm/tools/inferIsViteDev.js.map +1 -0
- package/esm/tools/infer_import_meta_env_BASE_URL.d.ts +1 -0
- package/esm/tools/infer_import_meta_env_BASE_URL.js +15 -0
- package/esm/tools/infer_import_meta_env_BASE_URL.js.map +1 -0
- package/esm/tools/tsafe/uncapitalize.d.ts +2 -0
- package/esm/tools/tsafe/uncapitalize.js +5 -0
- package/esm/tools/tsafe/uncapitalize.js.map +1 -0
- package/esm/vendor/backend/evt.d.ts +2 -0
- package/esm/vendor/backend/evt.js +3286 -0
- package/esm/vendor/backend/jose.d.ts +1 -0
- package/esm/vendor/backend/jose.js +3546 -0
- package/esm/vendor/backend/tsafe.d.ts +5 -0
- package/esm/vendor/backend/tsafe.js +68 -0
- package/esm/vendor/backend/zod.d.ts +1 -0
- package/esm/vendor/backend/zod.js +4023 -0
- package/esm/vendor/frontend/worker-timers.js +261 -1
- package/mock/oidc.d.ts +1 -1
- package/mock/oidc.js.map +1 -1
- package/package.json +40 -4
- package/react/react.d.ts +1 -1
- package/src/angular.ts +224 -9
- package/src/backend.ts +201 -166
- package/src/core/Oidc.ts +41 -11
- package/src/core/createOidc.ts +12 -3
- package/src/core/earlyInit.ts +19 -4
- package/src/core/iframeMessageProtection.ts +14 -15
- package/src/core/loginOrGoToAuthServer.ts +11 -3
- package/src/core/loginSilent.ts +5 -0
- package/src/core/oidcClientTsUserToTokens.ts +2 -2
- package/src/core/requiredPostHydrationReplaceNavigationUrl.ts +11 -0
- package/src/entrypoint.ts +1 -0
- package/src/mock/oidc.ts +2 -2
- package/src/react/react.tsx +1 -1
- package/src/tanstack-start/react/accessTokenValidation_rfc9068.ts +135 -0
- package/src/tanstack-start/react/apiBuilder.ts +151 -0
- package/src/tanstack-start/react/createOidcSpaApi.tsx +1009 -0
- package/src/tanstack-start/react/index.ts +5 -0
- package/src/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/UnifiedClientRetryForSsrLoadersError.ts +8 -0
- package/src/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/enableUnifiedClientRetryForSsrLoaders.tsx +110 -0
- package/src/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/entrypoint.ts +13 -0
- package/src/tanstack-start/react/rfcUnifiedClientRetryForSsrLoaders/index.ts +2 -0
- package/src/tanstack-start/react/types.tsx +415 -0
- package/src/tanstack-start/react/withHandlingOidcPostLoginNavigation.tsx +35 -0
- package/src/tools/GetterOrDirectValue.ts +1 -0
- package/src/tools/ZodSchemaLike.ts +3 -0
- package/src/tools/getThisCodebaseRootDirPath_cjs.ts +19 -0
- package/src/tools/inferIsViteDev.ts +6 -0
- package/src/tools/infer_import_meta_env_BASE_URL.ts +19 -0
- package/src/tools/tsafe/uncapitalize.ts +4 -0
- package/src/vendor/backend/jose.ts +1 -0
- package/src/vendor/build-runtime/babel.ts +6 -0
- package/src/vendor/build-runtime/magic-string.ts +3 -0
- package/src/vite-plugin/detectProjectType.ts +20 -0
- package/src/vite-plugin/excludeModuleExportFromOptimizedDeps.ts +20 -0
- package/src/vite-plugin/handleClientEntrypoint.ts +260 -0
- package/src/vite-plugin/index.ts +1 -0
- package/src/vite-plugin/transformCreateFileRoute.ts +240 -0
- package/src/vite-plugin/vite-plugin.ts +54 -0
- package/tools/GetterOrDirectValue.d.ts +1 -0
- package/tools/GetterOrDirectValue.js +3 -0
- package/tools/GetterOrDirectValue.js.map +1 -0
- package/tools/ZodSchemaLike.d.ts +3 -0
- package/tools/ZodSchemaLike.js +3 -0
- package/tools/ZodSchemaLike.js.map +1 -0
- package/tools/getThisCodebaseRootDirPath_cjs.d.ts +2 -0
- package/tools/getThisCodebaseRootDirPath_cjs.js +53 -0
- package/tools/getThisCodebaseRootDirPath_cjs.js.map +1 -0
- package/tools/tsafe/uncapitalize.d.ts +2 -0
- package/tools/tsafe/uncapitalize.js +8 -0
- package/tools/tsafe/uncapitalize.js.map +1 -0
- package/vendor/backend/jose.d.ts +1 -0
- package/vendor/backend/jose.js +3 -0
- package/vendor/build-runtime/babel.d.ts +6 -0
- package/vendor/build-runtime/babel.js +3 -0
- package/vendor/build-runtime/magic-string.d.ts +2 -0
- package/vendor/build-runtime/magic-string.js +2 -0
- package/vendor/frontend/oidc-client-ts.js +0 -2
- package/vite-plugin/detectProjectType.d.ts +10 -0
- package/vite-plugin/detectProjectType.js +15 -0
- package/vite-plugin/detectProjectType.js.map +1 -0
- package/vite-plugin/excludeModuleExportFromOptimizedDeps.d.ts +4 -0
- package/vite-plugin/excludeModuleExportFromOptimizedDeps.js +50 -0
- package/vite-plugin/excludeModuleExportFromOptimizedDeps.js.map +1 -0
- package/vite-plugin/handleClientEntrypoint.d.ts +10 -0
- package/vite-plugin/handleClientEntrypoint.js +211 -0
- package/vite-plugin/handleClientEntrypoint.js.map +1 -0
- package/vite-plugin/index.d.ts +1 -0
- package/vite-plugin/index.js +6 -0
- package/vite-plugin/index.js.map +1 -0
- package/vite-plugin/transformCreateFileRoute.d.ts +10 -0
- package/vite-plugin/transformCreateFileRoute.js +173 -0
- package/vite-plugin/transformCreateFileRoute.js.map +1 -0
- package/vite-plugin/vite-plugin.d.ts +5 -0
- package/vite-plugin/vite-plugin.js +46 -0
- package/vite-plugin/vite-plugin.js.map +1 -0
- package/src/vendor/backend/jsonwebtoken.ts +0 -1
- package/src/vendor/backend/node-fetch.ts +0 -2
- package/src/vendor/backend/node-jose.ts +0 -1
- package/vendor/backend/jsonwebtoken.d.ts +0 -1
- package/vendor/backend/jsonwebtoken.js +0 -3
- package/vendor/backend/node-fetch.d.ts +0 -2
- package/vendor/backend/node-fetch.js +0 -2
- package/vendor/backend/node-jose.d.ts +0 -1
- package/vendor/backend/node-jose.js +0 -3
|
@@ -93,15 +93,16 @@ export async function initIframeMessageProtection(params: { stateUrlParamValue:
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
function startSessionStoragePublicKeyMaliciousWriteDetection() {
|
|
96
|
-
setSessionStoragePublicKey();
|
|
97
|
-
|
|
98
96
|
assert(capturedApis !== undefined);
|
|
99
97
|
|
|
100
|
-
const {
|
|
98
|
+
const { alert, setTimeout } = capturedApis;
|
|
99
|
+
|
|
100
|
+
sessionStorage.removeItem(sessionStorageKey);
|
|
101
101
|
|
|
102
102
|
const checkTimeoutCallback = () => {
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
const publicKey_inStorage = sessionStorage.getItem(sessionStorageKey);
|
|
104
|
+
|
|
105
|
+
if (publicKey_inStorage !== null && publicKey_inStorage !== publicKey) {
|
|
105
106
|
while (true) {
|
|
106
107
|
alert(
|
|
107
108
|
[
|
|
@@ -112,7 +113,6 @@ export async function initIframeMessageProtection(params: { stateUrlParamValue:
|
|
|
112
113
|
].join(" ")
|
|
113
114
|
);
|
|
114
115
|
}
|
|
115
|
-
*/
|
|
116
116
|
}
|
|
117
117
|
check();
|
|
118
118
|
};
|
|
@@ -161,20 +161,19 @@ export async function postEncryptedAuthResponseToParent(params: { authResponse:
|
|
|
161
161
|
|
|
162
162
|
parent.postMessage(getReadyMessage({ stateUrlParamValue: authResponse.state }), location.origin);
|
|
163
163
|
|
|
164
|
-
const readPublicKey = () =>
|
|
165
|
-
sessionStorage.getItem(getSessionStorageKey({ stateUrlParamValue: authResponse.state }));
|
|
166
|
-
|
|
167
164
|
await new Promise<void>(resolve => setTimeout(resolve, 2));
|
|
168
165
|
|
|
169
|
-
|
|
170
|
-
await new Promise<void>(resolve => setTimeout(resolve, 2));
|
|
171
|
-
}
|
|
166
|
+
let publicKey: string | null;
|
|
172
167
|
|
|
173
|
-
|
|
168
|
+
{
|
|
169
|
+
let sessionStorageKey = getSessionStorageKey({ stateUrlParamValue: authResponse.state });
|
|
174
170
|
|
|
175
|
-
|
|
171
|
+
while ((publicKey = sessionStorage.getItem(sessionStorageKey)) === null) {
|
|
172
|
+
await new Promise<void>(resolve => setTimeout(resolve, 2));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
176
175
|
|
|
177
|
-
|
|
176
|
+
await new Promise<void>(resolve => setTimeout(resolve, 7));
|
|
178
177
|
|
|
179
178
|
const { encryptedMessage: encryptedMessage_withoutPrefix } = await asymmetricEncrypt({
|
|
180
179
|
publicKey,
|
|
@@ -355,9 +355,17 @@ export function createLoginOrGoToAuthServer(params: {
|
|
|
355
355
|
return new Promise<never>(() => {});
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
358
|
+
if (error.message.includes("Crypto.subtle is available only in secure contexts")) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
[
|
|
361
|
+
`oidc-spa: ${error.message}.`,
|
|
362
|
+
"To fix this error see:",
|
|
363
|
+
"https://docs.oidc-spa.dev/v/v8/resources/fixing-crypto.subtle-is-available-only-in-secure-contexts-https"
|
|
364
|
+
].join(" ")
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
assert(false, "224238482");
|
|
361
369
|
}
|
|
362
370
|
);
|
|
363
371
|
}
|
package/src/core/loginSilent.ts
CHANGED
|
@@ -178,6 +178,11 @@ export async function loginSilent(params: {
|
|
|
178
178
|
|
|
179
179
|
window.removeEventListener("message", listener);
|
|
180
180
|
|
|
181
|
+
// NOTE: Acknowledge that we're also doing it later but
|
|
182
|
+
// since there's a aggressive write protection in place
|
|
183
|
+
// it's good to clear the key ASAP.
|
|
184
|
+
clearSessionStoragePublicKey();
|
|
185
|
+
|
|
181
186
|
dEncryptedAuthResponse.resolve(message);
|
|
182
187
|
};
|
|
183
188
|
|
|
@@ -9,7 +9,7 @@ import { INFINITY_TIME } from "../tools/INFINITY_TIME";
|
|
|
9
9
|
export function oidcClientTsUserToTokens<DecodedIdToken extends Record<string, unknown>>(params: {
|
|
10
10
|
oidcClientTsUser: OidcClientTsUser;
|
|
11
11
|
decodedIdTokenSchema?: {
|
|
12
|
-
parse: (decodedIdToken_original: Oidc.Tokens.
|
|
12
|
+
parse: (decodedIdToken_original: Oidc.Tokens.DecodedIdToken_OidcCoreSpec) => DecodedIdToken;
|
|
13
13
|
};
|
|
14
14
|
__unsafe_useIdTokenAsAccessToken: boolean;
|
|
15
15
|
decodedIdToken_previous: DecodedIdToken | undefined;
|
|
@@ -33,7 +33,7 @@ export function oidcClientTsUserToTokens<DecodedIdToken extends Record<string, u
|
|
|
33
33
|
|
|
34
34
|
assert(idToken !== undefined, "No id token provided by the oidc server");
|
|
35
35
|
|
|
36
|
-
const decodedIdToken_original = decodeJwt<Oidc.Tokens.
|
|
36
|
+
const decodedIdToken_original = decodeJwt<Oidc.Tokens.DecodedIdToken_OidcCoreSpec>(idToken);
|
|
37
37
|
|
|
38
38
|
if (isFirstInit) {
|
|
39
39
|
log?.(
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
let rootRelativeRedirectUrl: string | undefined = undefined;
|
|
2
|
+
|
|
3
|
+
export function getOidcRequiredPostHydrationReplaceNavigationUrl() {
|
|
4
|
+
return { rootRelativeRedirectUrl };
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function setOidcRequiredPostHydrationReplaceNavigationUrl(params: {
|
|
8
|
+
rootRelativeRedirectUrl: string;
|
|
9
|
+
}) {
|
|
10
|
+
rootRelativeRedirectUrl = params.rootRelativeRedirectUrl;
|
|
11
|
+
}
|
package/src/entrypoint.ts
CHANGED
package/src/mock/oidc.ts
CHANGED
|
@@ -32,7 +32,7 @@ const URL_SEARCH_PARAM_NAME = "isUserLoggedIn";
|
|
|
32
32
|
const locationHref_moduleEvalTime = location.href;
|
|
33
33
|
|
|
34
34
|
export async function createMockOidc<
|
|
35
|
-
DecodedIdToken extends Record<string, unknown> = Oidc.Tokens.
|
|
35
|
+
DecodedIdToken extends Record<string, unknown> = Oidc.Tokens.DecodedIdToken_OidcCoreSpec,
|
|
36
36
|
AutoLogin extends boolean = false
|
|
37
37
|
>(
|
|
38
38
|
params: ParamsOfCreateMockOidc<DecodedIdToken, AutoLogin>
|
|
@@ -157,7 +157,7 @@ export async function createMockOidc<
|
|
|
157
157
|
}),
|
|
158
158
|
decodedIdToken_original:
|
|
159
159
|
mockedTokens.decodedIdToken_original ??
|
|
160
|
-
createObjectThatThrowsIfAccessed<Oidc.Tokens.
|
|
160
|
+
createObjectThatThrowsIfAccessed<Oidc.Tokens.DecodedIdToken_OidcCoreSpec>({
|
|
161
161
|
debugMessage: [
|
|
162
162
|
"You haven't provided a mocked decodedIdToken_original",
|
|
163
163
|
"See https://docs.oidc-spa.dev/v/v8/mock"
|
package/src/react/react.tsx
CHANGED
|
@@ -454,7 +454,7 @@ export function createReactOidc_dependencyInjection<
|
|
|
454
454
|
|
|
455
455
|
/** @see: https://docs.oidc-spa.dev/v/v8/usage#react-api */
|
|
456
456
|
export function createReactOidc<
|
|
457
|
-
DecodedIdToken extends Record<string, unknown> = Oidc.Tokens.
|
|
457
|
+
DecodedIdToken extends Record<string, unknown> = Oidc.Tokens.DecodedIdToken_OidcCoreSpec,
|
|
458
458
|
AutoLogin extends boolean = false
|
|
459
459
|
>(params: ValueOrAsyncGetter<ParamsOfCreateOidc<DecodedIdToken, AutoLogin>>) {
|
|
460
460
|
return createReactOidc_dependencyInjection(params, createOidc);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { CreateValidateAndGetAccessTokenClaims, ParamsOfBootstrap } from "./types";
|
|
2
|
+
import type { DecodedAccessToken_RFC9068 as AccessTokenClaims_RFC9068 } from "../../backend";
|
|
3
|
+
import type { ZodSchemaLike } from "../../tools/ZodSchemaLike";
|
|
4
|
+
import { createObjectThatThrowsIfAccessed } from "../../tools/createObjectThatThrowsIfAccessed";
|
|
5
|
+
import { assert, type Equals, is } from "../../tools/tsafe/assert";
|
|
6
|
+
|
|
7
|
+
export function createCreateValidateAndGetAccessTokenClaims_rfc9068<
|
|
8
|
+
AccessTokenClaims extends Record<string, unknown>
|
|
9
|
+
>(params: {
|
|
10
|
+
accessTokenClaimsSchema?: ZodSchemaLike<AccessTokenClaims_RFC9068, AccessTokenClaims>;
|
|
11
|
+
accessTokenClaims_mock?: AccessTokenClaims;
|
|
12
|
+
expectedAudience?:
|
|
13
|
+
| string
|
|
14
|
+
| ((params: {
|
|
15
|
+
paramsOfBootstrap: ParamsOfBootstrap<boolean, Record<string, unknown>, AccessTokenClaims>;
|
|
16
|
+
}) => string);
|
|
17
|
+
}) {
|
|
18
|
+
const {
|
|
19
|
+
accessTokenClaimsSchema,
|
|
20
|
+
accessTokenClaims_mock,
|
|
21
|
+
expectedAudience: expectedAudienceOrGetter
|
|
22
|
+
} = params;
|
|
23
|
+
|
|
24
|
+
const createValidateAndGetAccessTokenClaims: CreateValidateAndGetAccessTokenClaims<
|
|
25
|
+
AccessTokenClaims
|
|
26
|
+
> = ({ paramsOfBootstrap }) => {
|
|
27
|
+
if (paramsOfBootstrap.implementation === "mock") {
|
|
28
|
+
return {
|
|
29
|
+
validateAndGetAccessTokenClaims: async () => {
|
|
30
|
+
return {
|
|
31
|
+
isValid: true,
|
|
32
|
+
accessTokenClaims: (() => {
|
|
33
|
+
if (paramsOfBootstrap.accessTokenClaims_mock !== undefined) {
|
|
34
|
+
assert(is<AccessTokenClaims>(paramsOfBootstrap.accessTokenClaims_mock));
|
|
35
|
+
return paramsOfBootstrap.accessTokenClaims_mock;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (accessTokenClaims_mock !== undefined) {
|
|
39
|
+
return accessTokenClaims_mock;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return createObjectThatThrowsIfAccessed<AccessTokenClaims>({
|
|
43
|
+
debugMessage: [
|
|
44
|
+
"oidc-spa: You didn't provide any mock for the accessTokenClaims",
|
|
45
|
+
"Either provide a default one by specifying accessTokenClaims_mock",
|
|
46
|
+
"as parameter of .withAccessTokenValidation() or",
|
|
47
|
+
"specify accessTokenClaims_mock when calling bootstrapOidc()"
|
|
48
|
+
].join(" ")
|
|
49
|
+
});
|
|
50
|
+
})()
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
assert<Equals<(typeof paramsOfBootstrap)["implementation"], "real">>;
|
|
56
|
+
|
|
57
|
+
const prVerifyAndDecodeAccessToken = (async () => {
|
|
58
|
+
const { createOidcBackend } = await import("../../backend");
|
|
59
|
+
|
|
60
|
+
const { verifyAndDecodeAccessToken } = await createOidcBackend({
|
|
61
|
+
issuerUri: paramsOfBootstrap.issuerUri,
|
|
62
|
+
decodedAccessTokenSchema: accessTokenClaimsSchema
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return verifyAndDecodeAccessToken;
|
|
66
|
+
})();
|
|
67
|
+
|
|
68
|
+
const expectedAudience = (() => {
|
|
69
|
+
if (expectedAudienceOrGetter === undefined) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
if (typeof expectedAudienceOrGetter === "function") {
|
|
73
|
+
return expectedAudienceOrGetter({ paramsOfBootstrap });
|
|
74
|
+
}
|
|
75
|
+
return expectedAudienceOrGetter;
|
|
76
|
+
})();
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
validateAndGetAccessTokenClaims: async ({ accessToken }) => {
|
|
80
|
+
const verifyAndDecodeAccessToken = await prVerifyAndDecodeAccessToken;
|
|
81
|
+
|
|
82
|
+
const {
|
|
83
|
+
isValid,
|
|
84
|
+
errorCase,
|
|
85
|
+
errorMessage,
|
|
86
|
+
decodedAccessToken,
|
|
87
|
+
decodedAccessToken_original
|
|
88
|
+
} = await verifyAndDecodeAccessToken({ accessToken });
|
|
89
|
+
|
|
90
|
+
if (!isValid) {
|
|
91
|
+
return {
|
|
92
|
+
isValid: false,
|
|
93
|
+
errorMessage: `${errorCase}: ${errorMessage}`,
|
|
94
|
+
wwwAuthenticateHeaderErrorDescription: (() => {
|
|
95
|
+
switch (errorCase) {
|
|
96
|
+
case "does not respect schema":
|
|
97
|
+
return "The access token is malformed or missing required claims";
|
|
98
|
+
case "expired":
|
|
99
|
+
return "The access token expired";
|
|
100
|
+
case "invalid signature":
|
|
101
|
+
return "The access token signature is invalid";
|
|
102
|
+
}
|
|
103
|
+
})()
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (expectedAudience !== undefined) {
|
|
108
|
+
const aud_array =
|
|
109
|
+
typeof decodedAccessToken_original.aud === "string"
|
|
110
|
+
? [decodedAccessToken_original.aud]
|
|
111
|
+
: decodedAccessToken_original.aud;
|
|
112
|
+
|
|
113
|
+
if (!aud_array.includes(expectedAudience)) {
|
|
114
|
+
return {
|
|
115
|
+
isValid: false,
|
|
116
|
+
errorMessage: [
|
|
117
|
+
"Access token is not for the expected audience.",
|
|
118
|
+
`Got aud claim: ${JSON.stringify(decodedAccessToken_original.aud)}`,
|
|
119
|
+
`Expected: ${expectedAudience}`
|
|
120
|
+
].join(" "),
|
|
121
|
+
wwwAuthenticateHeaderErrorDescription: "The access token audience is invalid"
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
isValid: true,
|
|
128
|
+
accessTokenClaims: decodedAccessToken
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return { createValidateAndGetAccessTokenClaims };
|
|
135
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { OidcSpaApi, CreateValidateAndGetAccessTokenClaims, ParamsOfBootstrap } from "./types";
|
|
2
|
+
import type { Oidc as Oidc_core } from "../../core";
|
|
3
|
+
import { assert, type Equals } from "../../tools/tsafe/assert";
|
|
4
|
+
import type { ZodSchemaLike } from "../../tools/ZodSchemaLike";
|
|
5
|
+
import type { DecodedAccessToken_RFC9068 as AccessTokenClaims_RFC9068 } from "../../backend";
|
|
6
|
+
import { createCreateValidateAndGetAccessTokenClaims_rfc9068 } from "./accessTokenValidation_rfc9068";
|
|
7
|
+
import { createOidcSpaApi } from "./createOidcSpaApi";
|
|
8
|
+
|
|
9
|
+
export type OidcSpaApiBuilder<
|
|
10
|
+
AutoLogin extends boolean = false,
|
|
11
|
+
DecodedIdToken extends Record<string, unknown> = Oidc_core.Tokens.DecodedIdToken_OidcCoreSpec,
|
|
12
|
+
AccessTokenClaims extends Record<string, unknown> | undefined = undefined,
|
|
13
|
+
ExcludedMethod extends
|
|
14
|
+
| "withAutoLogin"
|
|
15
|
+
| "withExpectedDecodedIdTokenShape"
|
|
16
|
+
| "withAccessTokenValidation"
|
|
17
|
+
| "finalize" = never
|
|
18
|
+
> = Omit<
|
|
19
|
+
{
|
|
20
|
+
withAutoLogin: () => OidcSpaApiBuilder<
|
|
21
|
+
true,
|
|
22
|
+
DecodedIdToken,
|
|
23
|
+
AccessTokenClaims,
|
|
24
|
+
ExcludedMethod | "withAutoLogin"
|
|
25
|
+
>;
|
|
26
|
+
withExpectedDecodedIdTokenShape: <DecodedIdToken extends Record<string, unknown>>(params: {
|
|
27
|
+
decodedIdTokenSchema: ZodSchemaLike<
|
|
28
|
+
Oidc_core.Tokens.DecodedIdToken_OidcCoreSpec,
|
|
29
|
+
DecodedIdToken
|
|
30
|
+
>;
|
|
31
|
+
decodedIdToken_mock?: NoInfer<DecodedIdToken>;
|
|
32
|
+
}) => OidcSpaApiBuilder<
|
|
33
|
+
AutoLogin,
|
|
34
|
+
DecodedIdToken,
|
|
35
|
+
AccessTokenClaims,
|
|
36
|
+
ExcludedMethod | "withExpectedDecodedIdTokenShape"
|
|
37
|
+
>;
|
|
38
|
+
withAccessTokenValidation: {
|
|
39
|
+
<AccessTokenClaims extends Record<string, unknown> = AccessTokenClaims_RFC9068>(params: {
|
|
40
|
+
type: "RFC 9068: JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens";
|
|
41
|
+
accessTokenClaimsSchema?: ZodSchemaLike<AccessTokenClaims_RFC9068, AccessTokenClaims>;
|
|
42
|
+
accessTokenClaims_mock?: NoInfer<AccessTokenClaims>;
|
|
43
|
+
|
|
44
|
+
expectedAudience?:
|
|
45
|
+
| string
|
|
46
|
+
| ((params: {
|
|
47
|
+
paramsOfBootstrap: ParamsOfBootstrap<
|
|
48
|
+
boolean,
|
|
49
|
+
Record<string, unknown>,
|
|
50
|
+
AccessTokenClaims
|
|
51
|
+
>;
|
|
52
|
+
}) => string);
|
|
53
|
+
}): OidcSpaApiBuilder<
|
|
54
|
+
AutoLogin,
|
|
55
|
+
DecodedIdToken,
|
|
56
|
+
AccessTokenClaims,
|
|
57
|
+
ExcludedMethod | "withAccessTokenValidation"
|
|
58
|
+
>;
|
|
59
|
+
<AccessTokenClaims extends Record<string, unknown>>(params: {
|
|
60
|
+
type: "custom";
|
|
61
|
+
createValidateAndGetAccessTokenClaims: CreateValidateAndGetAccessTokenClaims<AccessTokenClaims>;
|
|
62
|
+
}): OidcSpaApiBuilder<
|
|
63
|
+
AutoLogin,
|
|
64
|
+
DecodedIdToken,
|
|
65
|
+
AccessTokenClaims,
|
|
66
|
+
ExcludedMethod | "withAccessTokenValidation"
|
|
67
|
+
>;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
finalize: () => OidcSpaApi<AutoLogin, DecodedIdToken, AccessTokenClaims>;
|
|
71
|
+
},
|
|
72
|
+
ExcludedMethod
|
|
73
|
+
>;
|
|
74
|
+
|
|
75
|
+
function createOidcSpaApiBuilder<
|
|
76
|
+
AutoLogin extends boolean = false,
|
|
77
|
+
DecodedIdToken extends Record<string, unknown> = Oidc_core.Tokens.DecodedIdToken_OidcCoreSpec,
|
|
78
|
+
AccessTokenClaims extends Record<string, unknown> | undefined = undefined
|
|
79
|
+
>(params: {
|
|
80
|
+
autoLogin: AutoLogin;
|
|
81
|
+
decodedIdTokenSchema:
|
|
82
|
+
| ZodSchemaLike<Oidc_core.Tokens.DecodedIdToken_OidcCoreSpec, DecodedIdToken>
|
|
83
|
+
| undefined;
|
|
84
|
+
decodedIdToken_mock: DecodedIdToken | undefined;
|
|
85
|
+
createValidateAndGetAccessTokenClaims:
|
|
86
|
+
| CreateValidateAndGetAccessTokenClaims<AccessTokenClaims>
|
|
87
|
+
| undefined;
|
|
88
|
+
}): OidcSpaApiBuilder<AutoLogin, DecodedIdToken, AccessTokenClaims> {
|
|
89
|
+
return {
|
|
90
|
+
withAutoLogin: () =>
|
|
91
|
+
createOidcSpaApiBuilder({
|
|
92
|
+
autoLogin: true,
|
|
93
|
+
decodedIdTokenSchema: params.decodedIdTokenSchema,
|
|
94
|
+
decodedIdToken_mock: params.decodedIdToken_mock,
|
|
95
|
+
createValidateAndGetAccessTokenClaims: params.createValidateAndGetAccessTokenClaims
|
|
96
|
+
}),
|
|
97
|
+
withExpectedDecodedIdTokenShape: ({ decodedIdTokenSchema, decodedIdToken_mock }) =>
|
|
98
|
+
createOidcSpaApiBuilder({
|
|
99
|
+
autoLogin: params.autoLogin,
|
|
100
|
+
decodedIdTokenSchema,
|
|
101
|
+
decodedIdToken_mock: decodedIdToken_mock,
|
|
102
|
+
createValidateAndGetAccessTokenClaims: params.createValidateAndGetAccessTokenClaims
|
|
103
|
+
}),
|
|
104
|
+
withAccessTokenValidation: params_scope =>
|
|
105
|
+
createOidcSpaApiBuilder({
|
|
106
|
+
autoLogin: params.autoLogin,
|
|
107
|
+
decodedIdTokenSchema: params.decodedIdTokenSchema,
|
|
108
|
+
decodedIdToken_mock: params.decodedIdToken_mock,
|
|
109
|
+
createValidateAndGetAccessTokenClaims: ((): any => {
|
|
110
|
+
switch (params_scope.type) {
|
|
111
|
+
case "RFC 9068: JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens": {
|
|
112
|
+
const { accessTokenClaimsSchema, accessTokenClaims_mock, expectedAudience } =
|
|
113
|
+
params_scope;
|
|
114
|
+
|
|
115
|
+
const { createValidateAndGetAccessTokenClaims } =
|
|
116
|
+
createCreateValidateAndGetAccessTokenClaims_rfc9068<
|
|
117
|
+
Exclude<AccessTokenClaims, undefined>
|
|
118
|
+
>({
|
|
119
|
+
// @ts-expect-error
|
|
120
|
+
accessTokenClaims_mock,
|
|
121
|
+
// @ts-expect-error
|
|
122
|
+
accessTokenClaimsSchema,
|
|
123
|
+
expectedAudience
|
|
124
|
+
});
|
|
125
|
+
return createValidateAndGetAccessTokenClaims;
|
|
126
|
+
}
|
|
127
|
+
case "custom": {
|
|
128
|
+
const { createValidateAndGetAccessTokenClaims } = params_scope;
|
|
129
|
+
return createValidateAndGetAccessTokenClaims;
|
|
130
|
+
}
|
|
131
|
+
default:
|
|
132
|
+
assert<Equals<typeof params_scope, never>>(false);
|
|
133
|
+
}
|
|
134
|
+
})()
|
|
135
|
+
}),
|
|
136
|
+
finalize: () =>
|
|
137
|
+
createOidcSpaApi<AutoLogin, DecodedIdToken, AccessTokenClaims>({
|
|
138
|
+
autoLogin: params.autoLogin,
|
|
139
|
+
decodedIdTokenSchema: params.decodedIdTokenSchema,
|
|
140
|
+
decodedIdToken_mock: params.decodedIdToken_mock,
|
|
141
|
+
createValidateAndGetAccessTokenClaims: params.createValidateAndGetAccessTokenClaims
|
|
142
|
+
})
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const oidcSpaApiBuilder = createOidcSpaApiBuilder({
|
|
147
|
+
autoLogin: false,
|
|
148
|
+
createValidateAndGetAccessTokenClaims: undefined,
|
|
149
|
+
decodedIdToken_mock: undefined,
|
|
150
|
+
decodedIdTokenSchema: undefined
|
|
151
|
+
});
|