oidc-spa 7.2.0 → 7.2.2
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.js.map +1 -1
- package/core/AuthResponse.js.map +1 -1
- package/core/Oidc.js.map +1 -1
- package/core/OidcInitializationError.js.map +1 -1
- package/core/OidcMetadata.js.map +1 -1
- package/core/StateData.js.map +1 -1
- package/core/configId.js.map +1 -1
- package/core/createOidc.js +1 -1
- package/core/createOidc.js.map +1 -1
- package/core/diagnostic.js.map +1 -1
- package/core/evtIsUserActive.js.map +1 -1
- package/core/handleOidcCallback.js.map +1 -1
- package/core/iframeMessageProtection.js.map +1 -1
- package/core/index.js.map +1 -1
- package/core/initialLocationHref.js.map +1 -1
- package/core/isNewBrowserSession.js.map +1 -1
- package/core/loginOrGoToAuthServer.js.map +1 -1
- package/core/loginPropagationToOtherTabs.js.map +1 -1
- package/core/loginSilent.js.map +1 -1
- package/core/logoutPropagationToOtherTabs.js.map +1 -1
- package/core/oidcClientTsUserToTokens.js.map +1 -1
- package/core/ongoingLoginOrRefreshProcesses.js.map +1 -1
- package/core/persistedAuthState.js.map +1 -1
- package/entrypoint.js.map +1 -1
- package/esm/core/AuthResponse.js +2 -2
- package/esm/core/AuthResponse.js.map +1 -1
- package/esm/core/Oidc.d.ts +1 -1
- package/esm/core/Oidc.js.map +1 -1
- package/esm/core/OidcInitializationError.js.map +1 -1
- package/esm/core/OidcMetadata.js +2 -2
- package/esm/core/OidcMetadata.js.map +1 -1
- package/esm/core/StateData.js +3 -3
- package/esm/core/StateData.js.map +1 -1
- package/esm/core/configId.js.map +1 -1
- package/esm/core/createOidc.d.ts +2 -2
- package/esm/core/createOidc.js +33 -33
- package/esm/core/createOidc.js.map +1 -1
- package/esm/core/diagnostic.d.ts +1 -1
- package/esm/core/diagnostic.js +4 -4
- package/esm/core/diagnostic.js.map +1 -1
- package/esm/core/evtIsUserActive.d.ts +1 -1
- package/esm/core/evtIsUserActive.js +5 -5
- package/esm/core/evtIsUserActive.js.map +1 -1
- package/esm/core/handleOidcCallback.d.ts +2 -2
- package/esm/core/handleOidcCallback.js +5 -5
- package/esm/core/handleOidcCallback.js.map +1 -1
- package/esm/core/iframeMessageProtection.d.ts +1 -1
- package/esm/core/iframeMessageProtection.js +3 -3
- package/esm/core/iframeMessageProtection.js.map +1 -1
- package/esm/core/index.d.ts +4 -4
- package/esm/core/index.js +4 -4
- package/esm/core/index.js.map +1 -1
- package/esm/core/initialLocationHref.js.map +1 -1
- package/esm/core/isNewBrowserSession.d.ts +1 -1
- package/esm/core/isNewBrowserSession.js.map +1 -1
- package/esm/core/loginOrGoToAuthServer.d.ts +2 -2
- package/esm/core/loginOrGoToAuthServer.js +6 -6
- package/esm/core/loginOrGoToAuthServer.js.map +1 -1
- package/esm/core/loginPropagationToOtherTabs.js +3 -3
- package/esm/core/loginPropagationToOtherTabs.js.map +1 -1
- package/esm/core/loginSilent.d.ts +2 -2
- package/esm/core/loginSilent.js +8 -8
- package/esm/core/loginSilent.js.map +1 -1
- package/esm/core/logoutPropagationToOtherTabs.js +3 -3
- package/esm/core/logoutPropagationToOtherTabs.js.map +1 -1
- package/esm/core/oidcClientTsUserToTokens.d.ts +2 -2
- package/esm/core/oidcClientTsUserToTokens.js +4 -4
- package/esm/core/oidcClientTsUserToTokens.js.map +1 -1
- package/esm/core/ongoingLoginOrRefreshProcesses.js +3 -3
- package/esm/core/ongoingLoginOrRefreshProcesses.js.map +1 -1
- package/esm/core/persistedAuthState.js +2 -2
- package/esm/core/persistedAuthState.js.map +1 -1
- package/esm/entrypoint.js +3 -3
- package/esm/entrypoint.js.map +1 -1
- package/esm/index.d.ts +1 -1
- package/esm/index.js +2 -2
- package/esm/index.js.map +1 -1
- package/esm/keycloak/index.d.ts +3 -3
- package/esm/keycloak/index.js +3 -3
- package/esm/keycloak/index.js.map +1 -1
- package/esm/keycloak/isKeycloak.js.map +1 -1
- package/esm/keycloak/keycloak-js/Keycloak.d.ts +1 -1
- package/esm/keycloak/keycloak-js/Keycloak.js +9 -9
- package/esm/keycloak/keycloak-js/Keycloak.js.map +1 -1
- package/esm/keycloak/keycloak-js/index.d.ts +2 -2
- package/esm/keycloak/keycloak-js/index.js +2 -2
- package/esm/keycloak/keycloak-js/index.js.map +1 -1
- package/esm/keycloak/keycloak-js/types.js.map +1 -1
- package/esm/keycloak/keycloakIssuerUriParsed.js +3 -3
- package/esm/keycloak/keycloakIssuerUriParsed.js.map +1 -1
- package/esm/keycloak/keycloakUtils.d.ts +1 -1
- package/esm/keycloak/keycloakUtils.js +3 -3
- package/esm/keycloak/keycloakUtils.js.map +1 -1
- package/esm/keycloak-js.d.ts +1 -1
- package/esm/keycloak-js.js +2 -2
- package/esm/keycloak-js.js.map +1 -1
- package/esm/mock/index.d.ts +1 -1
- package/esm/mock/index.js +2 -2
- package/esm/mock/index.js.map +1 -1
- package/esm/mock/oidc.d.ts +1 -1
- package/esm/mock/oidc.js +6 -6
- package/esm/mock/oidc.js.map +1 -1
- package/esm/mock/react.d.ts +8 -8
- package/esm/mock/react.js +3 -3
- package/esm/mock/react.js.map +1 -1
- package/esm/react/index.d.ts +1 -1
- package/esm/react/index.js +2 -2
- package/esm/react/index.js.map +1 -1
- package/esm/react/react.d.ts +2 -2
- package/esm/react/react.js +6 -6
- package/esm/react/react.js.map +1 -1
- package/esm/tools/Deferred.js.map +1 -1
- package/esm/tools/EphemeralSessionStorage.js +2 -2
- package/esm/tools/EphemeralSessionStorage.js.map +1 -1
- package/esm/tools/Evt.js +3 -3
- package/esm/tools/Evt.js.map +1 -1
- package/esm/tools/StatefulEvt.js.map +1 -1
- package/esm/tools/ValueOrAsyncGetter.js.map +1 -1
- package/esm/tools/asymmetricEncryption.js.map +1 -1
- package/esm/tools/base64.js.map +1 -1
- package/esm/tools/createObjectThatThrowsIfAccessed.js.map +1 -1
- package/esm/tools/decodeJwt.js.map +1 -1
- package/esm/tools/generateUrlSafeRandom.js.map +1 -1
- package/esm/tools/getDownlinkAndRtt.js +2 -2
- package/esm/tools/getDownlinkAndRtt.js.map +1 -1
- package/esm/tools/getIsOnline.js +2 -2
- package/esm/tools/getIsOnline.js.map +1 -1
- package/esm/tools/getIsValidRemoteJson.js.map +1 -1
- package/esm/tools/getPrUserInteraction.js +2 -2
- package/esm/tools/getPrUserInteraction.js.map +1 -1
- package/esm/tools/getUserEnvironmentInfo.js.map +1 -1
- package/esm/tools/haveSharedParentDomain.js.map +1 -1
- package/esm/tools/isDev.js.map +1 -1
- package/esm/tools/parseKeycloakIssuerUri.js +2 -2
- package/esm/tools/parseKeycloakIssuerUri.js.map +1 -1
- package/esm/tools/readExpirationTimeInJwt.js +3 -3
- package/esm/tools/readExpirationTimeInJwt.js.map +1 -1
- package/esm/tools/startCountdown.js +2 -2
- package/esm/tools/startCountdown.js.map +1 -1
- package/esm/tools/subscribeToUserInteraction.js +2 -2
- package/esm/tools/subscribeToUserInteraction.js.map +1 -1
- package/esm/tools/toFullyQualifiedUrl.js.map +1 -1
- package/esm/tools/toHumanReadableDuration.js.map +1 -1
- package/esm/tools/urlSearchParams.js.map +1 -1
- package/esm/tools/workerTimers.js +2 -2
- package/esm/tools/workerTimers.js.map +1 -1
- package/index.js.map +1 -1
- package/keycloak/index.js.map +1 -1
- package/keycloak/isKeycloak.js.map +1 -1
- package/keycloak/keycloak-js/Keycloak.js.map +1 -1
- package/keycloak/keycloak-js/index.js.map +1 -1
- package/keycloak/keycloak-js/types.js.map +1 -1
- package/keycloak/keycloakIssuerUriParsed.js.map +1 -1
- package/keycloak/keycloakUtils.js.map +1 -1
- package/keycloak-js.js.map +1 -1
- package/mock/index.js.map +1 -1
- package/mock/oidc.js.map +1 -1
- package/mock/react.js.map +1 -1
- package/package.json +1 -1
- package/react/index.js.map +1 -1
- package/react/react.js.map +1 -1
- package/src/backend.ts +391 -0
- package/src/core/AuthResponse.ts +26 -0
- package/src/core/Oidc.ts +140 -0
- package/src/core/OidcInitializationError.ts +19 -0
- package/src/core/OidcMetadata.ts +271 -0
- package/src/core/StateData.ts +118 -0
- package/src/core/configId.ts +3 -0
- package/src/core/createOidc.ts +1576 -0
- package/src/core/diagnostic.ts +267 -0
- package/src/core/evtIsUserActive.ts +108 -0
- package/src/core/handleOidcCallback.ts +321 -0
- package/src/core/iframeMessageProtection.ts +100 -0
- package/src/core/index.ts +4 -0
- package/src/core/initialLocationHref.ts +5 -0
- package/src/core/isNewBrowserSession.ts +37 -0
- package/src/core/loginOrGoToAuthServer.ts +324 -0
- package/src/core/loginPropagationToOtherTabs.ts +51 -0
- package/src/core/loginSilent.ts +242 -0
- package/src/core/logoutPropagationToOtherTabs.ts +53 -0
- package/src/core/oidcClientTsUserToTokens.ts +229 -0
- package/src/core/ongoingLoginOrRefreshProcesses.ts +47 -0
- package/src/core/persistedAuthState.ts +122 -0
- package/src/entrypoint.ts +69 -0
- package/src/index.ts +1 -0
- package/src/keycloak/index.ts +8 -0
- package/src/keycloak/isKeycloak.ts +23 -0
- package/src/keycloak/keycloak-js/Keycloak.ts +1097 -0
- package/src/keycloak/keycloak-js/index.ts +2 -0
- package/src/keycloak/keycloak-js/types.ts +442 -0
- package/src/keycloak/keycloakIssuerUriParsed.ts +29 -0
- package/src/keycloak/keycloakUtils.ts +90 -0
- package/src/keycloak-js.ts +1 -0
- package/src/mock/index.ts +1 -0
- package/src/mock/oidc.ts +211 -0
- package/src/mock/react.tsx +11 -0
- package/src/react/index.ts +1 -0
- package/src/react/react.tsx +476 -0
- package/src/tools/Deferred.ts +33 -0
- package/src/tools/EphemeralSessionStorage.ts +223 -0
- package/src/tools/Evt.ts +56 -0
- package/src/tools/StatefulEvt.ts +38 -0
- package/src/tools/ValueOrAsyncGetter.ts +1 -0
- package/src/tools/asymmetricEncryption.ts +184 -0
- package/src/tools/base64.ts +7 -0
- package/src/tools/createObjectThatThrowsIfAccessed.ts +40 -0
- package/src/tools/decodeJwt.ts +95 -0
- package/src/tools/generateUrlSafeRandom.ts +26 -0
- package/src/tools/getDownlinkAndRtt.ts +22 -0
- package/src/tools/getIsOnline.ts +20 -0
- package/src/tools/getIsValidRemoteJson.ts +18 -0
- package/src/tools/getPrUserInteraction.ts +27 -0
- package/src/tools/getUserEnvironmentInfo.ts +42 -0
- package/src/tools/haveSharedParentDomain.ts +13 -0
- package/src/tools/isDev.ts +30 -0
- package/src/tools/parseKeycloakIssuerUri.ts +49 -0
- package/src/tools/readExpirationTimeInJwt.ts +16 -0
- package/src/tools/startCountdown.ts +36 -0
- package/src/tools/subscribeToUserInteraction.ts +33 -0
- package/src/tools/toFullyQualifiedUrl.ts +58 -0
- package/src/tools/toHumanReadableDuration.ts +21 -0
- package/src/tools/urlSearchParams.ts +130 -0
- package/src/tools/workerTimers.ts +57 -0
- package/src/vendor/backend/evt.ts +2 -0
- package/src/vendor/backend/jsonwebtoken.ts +1 -0
- package/src/vendor/backend/node-fetch.ts +2 -0
- package/src/vendor/backend/node-jose.ts +1 -0
- package/src/vendor/backend/tsafe.ts +5 -0
- package/src/vendor/backend/zod.ts +1 -0
- package/src/vendor/frontend/oidc-client-ts.ts +1 -0
- package/src/vendor/frontend/tsafe.ts +6 -0
- package/src/vendor/frontend/worker-timers.ts +2 -0
- package/tools/Deferred.js.map +1 -1
- package/tools/EphemeralSessionStorage.js.map +1 -1
- package/tools/Evt.js.map +1 -1
- package/tools/StatefulEvt.js.map +1 -1
- package/tools/ValueOrAsyncGetter.js.map +1 -1
- package/tools/asymmetricEncryption.js.map +1 -1
- package/tools/base64.js.map +1 -1
- package/tools/createObjectThatThrowsIfAccessed.js.map +1 -1
- package/tools/decodeJwt.js.map +1 -1
- package/tools/generateUrlSafeRandom.js.map +1 -1
- package/tools/getDownlinkAndRtt.js.map +1 -1
- package/tools/getIsOnline.js.map +1 -1
- package/tools/getIsValidRemoteJson.js.map +1 -1
- package/tools/getPrUserInteraction.js.map +1 -1
- package/tools/getUserEnvironmentInfo.js.map +1 -1
- package/tools/haveSharedParentDomain.js.map +1 -1
- package/tools/isDev.js.map +1 -1
- package/tools/parseKeycloakIssuerUri.js.map +1 -1
- package/tools/readExpirationTimeInJwt.js.map +1 -1
- package/tools/startCountdown.js.map +1 -1
- package/tools/subscribeToUserInteraction.js.map +1 -1
- package/tools/toFullyQualifiedUrl.js.map +1 -1
- package/tools/toHumanReadableDuration.js.map +1 -1
- package/tools/urlSearchParams.js.map +1 -1
- package/tools/workerTimers.js.map +1 -1
|
@@ -0,0 +1,1097 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
KeycloakServerConfig,
|
|
3
|
+
KeycloakInitOptions,
|
|
4
|
+
KeycloakError,
|
|
5
|
+
KeycloakLogoutOptions,
|
|
6
|
+
KeycloakRoles,
|
|
7
|
+
KeycloakTokenParsed,
|
|
8
|
+
KeycloakResourceAccess,
|
|
9
|
+
KeycloakProfile,
|
|
10
|
+
KeycloakUserInfo,
|
|
11
|
+
KeycloakLoginOptions,
|
|
12
|
+
KeycloakRegisterOptions,
|
|
13
|
+
KeycloakAccountOptions
|
|
14
|
+
} from "./types";
|
|
15
|
+
import { assert, is, isAmong } from "../../vendor/frontend/tsafe";
|
|
16
|
+
import { createOidc, type Oidc, OidcInitializationError } from "../../core";
|
|
17
|
+
import { Deferred } from "../../tools/Deferred";
|
|
18
|
+
import { decodeJwt } from "../../tools/decodeJwt";
|
|
19
|
+
import { type KeycloakUtils, createKeycloakUtils } from "../keycloakUtils";
|
|
20
|
+
import { workerTimers } from "../../vendor/frontend/worker-timers";
|
|
21
|
+
import { type StatefulEvt, createStatefulEvt } from "../../tools/StatefulEvt";
|
|
22
|
+
import { readExpirationTimeInJwt } from "../../tools/readExpirationTimeInJwt";
|
|
23
|
+
|
|
24
|
+
type ConstructorParams = KeycloakServerConfig & {
|
|
25
|
+
homeUrl: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type InternalState = {
|
|
29
|
+
constructorParams: ConstructorParams;
|
|
30
|
+
keycloakUtils: KeycloakUtils;
|
|
31
|
+
issuerUri: string;
|
|
32
|
+
dInitialized: Deferred<void>;
|
|
33
|
+
initOptions: KeycloakInitOptions | undefined;
|
|
34
|
+
oidc: Oidc<Record<string, unknown>> | undefined;
|
|
35
|
+
tokens: Oidc.Tokens<Record<string, unknown>> | undefined;
|
|
36
|
+
profile: KeycloakProfile | undefined;
|
|
37
|
+
userInfo: KeycloakUserInfo | undefined;
|
|
38
|
+
$onTokenExpired: StatefulEvt<(() => void) | undefined>;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const internalStateByInstance = new WeakMap<Keycloak, InternalState>();
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* This module provides a drop-in replacement for `keycloak-js`,
|
|
45
|
+
* designed for teams migrating to `oidc-spa` with minimal changes.
|
|
46
|
+
*
|
|
47
|
+
* ⚠️ While the import path is `oidc-spa/keycloak-js`, this is *not* a re-export or patch —
|
|
48
|
+
* it is a full alternative implementation aligned with the `keycloak-js` API.
|
|
49
|
+
*/
|
|
50
|
+
export class Keycloak {
|
|
51
|
+
/**
|
|
52
|
+
* Creates a new Keycloak client instance.
|
|
53
|
+
* @param config A configuration object or path to a JSON config file.
|
|
54
|
+
*
|
|
55
|
+
* NOTE oidc-spa: Currently not supporting GenericOidcConfig (providing explicitly authorization_endpoint ect)
|
|
56
|
+
* But we could if with the __metadata parameter of oidc-spa.
|
|
57
|
+
* I'm not seeing the usecase when ran against keycloak right now so not doing it.
|
|
58
|
+
*/
|
|
59
|
+
constructor(params: ConstructorParams) {
|
|
60
|
+
const issuerUri = `${params.url.replace(/\/$/, "")}/realms/${params.realm}`;
|
|
61
|
+
|
|
62
|
+
internalStateByInstance.set(this, {
|
|
63
|
+
constructorParams: params,
|
|
64
|
+
dInitialized: new Deferred(),
|
|
65
|
+
initOptions: undefined,
|
|
66
|
+
oidc: undefined,
|
|
67
|
+
tokens: undefined,
|
|
68
|
+
keycloakUtils: createKeycloakUtils({ issuerUri }),
|
|
69
|
+
issuerUri,
|
|
70
|
+
profile: undefined,
|
|
71
|
+
userInfo: undefined,
|
|
72
|
+
$onTokenExpired: createStatefulEvt(() => undefined)
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Called to initialize the adapter.
|
|
78
|
+
* @param initOptions Initialization options.
|
|
79
|
+
* @returns A promise to set functions to be invoked on success or error.
|
|
80
|
+
*/
|
|
81
|
+
async init(initOptions: KeycloakInitOptions = {}): Promise<boolean> {
|
|
82
|
+
const { onLoad = "check-sso", redirectUri, enableLogging, scope, locale } = initOptions;
|
|
83
|
+
|
|
84
|
+
const internalState = internalStateByInstance.get(this);
|
|
85
|
+
|
|
86
|
+
assert(internalState !== undefined);
|
|
87
|
+
|
|
88
|
+
if (internalState.initOptions !== undefined) {
|
|
89
|
+
if (JSON.stringify(internalState.initOptions) !== JSON.stringify(initOptions)) {
|
|
90
|
+
throw new Error("Can't call init() multiple time with different params");
|
|
91
|
+
}
|
|
92
|
+
await internalState.dInitialized.pr;
|
|
93
|
+
const { oidc } = internalState;
|
|
94
|
+
assert(oidc !== undefined);
|
|
95
|
+
return oidc.isUserLoggedIn;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
internalState.initOptions = initOptions;
|
|
99
|
+
|
|
100
|
+
const { constructorParams, issuerUri } = internalState;
|
|
101
|
+
|
|
102
|
+
const autoLogin = onLoad === "login-required";
|
|
103
|
+
|
|
104
|
+
let hasCreateResolved = false;
|
|
105
|
+
|
|
106
|
+
const oidcOrError = await createOidc({
|
|
107
|
+
homeUrl: constructorParams.homeUrl,
|
|
108
|
+
issuerUri,
|
|
109
|
+
clientId: internalState.constructorParams.clientId,
|
|
110
|
+
autoLogin,
|
|
111
|
+
postLoginRedirectUrl: redirectUri,
|
|
112
|
+
debugLogs: enableLogging,
|
|
113
|
+
scopes: scope?.split(" "),
|
|
114
|
+
extraQueryParams:
|
|
115
|
+
!autoLogin || locale === undefined
|
|
116
|
+
? undefined
|
|
117
|
+
: () => {
|
|
118
|
+
if (hasCreateResolved) {
|
|
119
|
+
return {};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
ui_locales: locale
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
// NOTE: This can only happen when autoLogin is true, otherwise the error
|
|
128
|
+
// is in oidc.initializationError
|
|
129
|
+
.catch((error: OidcInitializationError) => error);
|
|
130
|
+
|
|
131
|
+
hasCreateResolved = true;
|
|
132
|
+
|
|
133
|
+
if (oidcOrError instanceof OidcInitializationError) {
|
|
134
|
+
this.onAuthError?.({
|
|
135
|
+
error: oidcOrError.name,
|
|
136
|
+
error_description: oidcOrError.message
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
await new Promise<never>(() => {});
|
|
140
|
+
assert(false);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const oidc = oidcOrError;
|
|
144
|
+
|
|
145
|
+
internalState.oidc = oidc;
|
|
146
|
+
|
|
147
|
+
if (oidc.isUserLoggedIn) {
|
|
148
|
+
{
|
|
149
|
+
const tokens = await oidc.getTokens();
|
|
150
|
+
|
|
151
|
+
const onNewToken = (tokens_new: Oidc.Tokens<Record<string, unknown>>) => {
|
|
152
|
+
internalState.tokens = tokens_new;
|
|
153
|
+
this.onAuthRefreshSuccess?.();
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
onNewToken(tokens);
|
|
157
|
+
|
|
158
|
+
oidc.subscribeToTokensChange(onNewToken);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
{
|
|
162
|
+
const { $onTokenExpired } = internalState;
|
|
163
|
+
|
|
164
|
+
let clear: (() => void) | undefined = undefined;
|
|
165
|
+
|
|
166
|
+
$onTokenExpired.subscribe(onTokenExpired => {
|
|
167
|
+
clear?.();
|
|
168
|
+
|
|
169
|
+
if (onTokenExpired === undefined) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let timer: ReturnType<typeof workerTimers.setTimeout> | undefined = undefined;
|
|
174
|
+
|
|
175
|
+
const onNewToken = () => {
|
|
176
|
+
if (timer !== undefined) {
|
|
177
|
+
workerTimers.clearTimeout(timer);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const { tokens } = internalState;
|
|
181
|
+
assert(tokens !== undefined);
|
|
182
|
+
|
|
183
|
+
timer = workerTimers.setTimeout(() => {
|
|
184
|
+
onTokenExpired.call(this);
|
|
185
|
+
}, Math.max(tokens.accessTokenExpirationTime - Date.now() - 3_000, 0));
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
onNewToken();
|
|
189
|
+
|
|
190
|
+
const { unsubscribe } = oidc.subscribeToTokensChange(onNewToken);
|
|
191
|
+
|
|
192
|
+
clear = () => {
|
|
193
|
+
if (timer !== undefined) {
|
|
194
|
+
workerTimers.clearTimeout(timer);
|
|
195
|
+
}
|
|
196
|
+
unsubscribe();
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
onActionUpdate_call: {
|
|
202
|
+
if (this.onActionUpdate === undefined) {
|
|
203
|
+
break onActionUpdate_call;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const { backFromAuthServer } = oidc;
|
|
207
|
+
|
|
208
|
+
if (backFromAuthServer === undefined) {
|
|
209
|
+
break onActionUpdate_call;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const status = backFromAuthServer.result.kc_action_status;
|
|
213
|
+
|
|
214
|
+
if (!isAmong(["success", "cancelled", "error"], status)) {
|
|
215
|
+
break onActionUpdate_call;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const action = backFromAuthServer.extraQueryParams.kc_action;
|
|
219
|
+
|
|
220
|
+
if (action === undefined) {
|
|
221
|
+
break onActionUpdate_call;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this.onActionUpdate(status, action);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!oidc.isUserLoggedIn && oidc.initializationError !== undefined) {
|
|
229
|
+
this.onAuthError?.({
|
|
230
|
+
error: oidc.initializationError.name,
|
|
231
|
+
error_description: oidc.initializationError.message
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
internalState.dInitialized.resolve();
|
|
236
|
+
|
|
237
|
+
this.onReady?.(oidc.isUserLoggedIn);
|
|
238
|
+
if (oidc.isUserLoggedIn) {
|
|
239
|
+
this.onAuthSuccess?.();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return oidc.isUserLoggedIn;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Is true if the user is authenticated, false otherwise.
|
|
247
|
+
*/
|
|
248
|
+
get authenticated(): boolean {
|
|
249
|
+
if (!this.didInitialize) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const internalState = internalStateByInstance.get(this);
|
|
254
|
+
|
|
255
|
+
assert(internalState !== undefined);
|
|
256
|
+
|
|
257
|
+
const { oidc } = internalState;
|
|
258
|
+
|
|
259
|
+
assert(oidc !== undefined);
|
|
260
|
+
|
|
261
|
+
return oidc.isUserLoggedIn;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The user id.
|
|
266
|
+
*/
|
|
267
|
+
get subject(): string | undefined {
|
|
268
|
+
if (!this.didInitialize) {
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const internalState = internalStateByInstance.get(this);
|
|
273
|
+
|
|
274
|
+
assert(internalState !== undefined);
|
|
275
|
+
|
|
276
|
+
const { oidc, tokens } = internalState;
|
|
277
|
+
|
|
278
|
+
assert(oidc !== undefined);
|
|
279
|
+
|
|
280
|
+
if (!oidc.isUserLoggedIn) {
|
|
281
|
+
console.warn(
|
|
282
|
+
"Trying to read keycloak.subject when keycloak.authenticated is false is a logical error in your application"
|
|
283
|
+
);
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
assert(tokens !== undefined);
|
|
288
|
+
|
|
289
|
+
return tokens.decodedIdToken_original.sub;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Response mode passed in init (default value is `'fragment'`).
|
|
294
|
+
*
|
|
295
|
+
* NOTE oidc-spa: Can only be fragment.
|
|
296
|
+
*/
|
|
297
|
+
responseMode = "fragment";
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Response type sent to Keycloak with login requests. This is determined
|
|
301
|
+
* based on the flow value used during initialization, but can be overridden
|
|
302
|
+
* by setting this value.
|
|
303
|
+
*
|
|
304
|
+
* NOTE oidc-spa: Can only be 'code'
|
|
305
|
+
*/
|
|
306
|
+
responseType = "code";
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Flow passed in init.
|
|
310
|
+
*
|
|
311
|
+
* NOTE oidc-spa: Can only be 'standard'
|
|
312
|
+
*/
|
|
313
|
+
flow = "standard";
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* The realm roles associated with the token.
|
|
317
|
+
*/
|
|
318
|
+
get realmAccess(): KeycloakRoles | undefined {
|
|
319
|
+
if (!this.didInitialize) {
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const internalState = internalStateByInstance.get(this);
|
|
324
|
+
|
|
325
|
+
assert(internalState !== undefined);
|
|
326
|
+
|
|
327
|
+
const { oidc, tokens } = internalState;
|
|
328
|
+
|
|
329
|
+
assert(oidc !== undefined);
|
|
330
|
+
|
|
331
|
+
if (!oidc.isUserLoggedIn) {
|
|
332
|
+
console.warn(
|
|
333
|
+
"Trying to read keycloak.realAccess when keycloak.realmAccess is false is a logical error in your application"
|
|
334
|
+
);
|
|
335
|
+
return undefined;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
assert(tokens !== undefined);
|
|
339
|
+
assert(is<KeycloakTokenParsed>(tokens.decodedIdToken_original));
|
|
340
|
+
|
|
341
|
+
return tokens.decodedIdToken_original.realm_access;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* The resource roles associated with the token.
|
|
346
|
+
*/
|
|
347
|
+
get resourceAccess(): KeycloakResourceAccess | undefined {
|
|
348
|
+
if (!this.didInitialize) {
|
|
349
|
+
return undefined;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const internalState = internalStateByInstance.get(this);
|
|
353
|
+
|
|
354
|
+
assert(internalState !== undefined);
|
|
355
|
+
|
|
356
|
+
const { oidc, tokens } = internalState;
|
|
357
|
+
|
|
358
|
+
assert(oidc !== undefined);
|
|
359
|
+
|
|
360
|
+
if (!oidc.isUserLoggedIn) {
|
|
361
|
+
console.warn(
|
|
362
|
+
"Trying to read keycloak.resourceAccess when keycloak.authenticated is false is a logical error in your application"
|
|
363
|
+
);
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
assert(tokens !== undefined);
|
|
368
|
+
assert(is<KeycloakTokenParsed>(tokens.decodedIdToken_original));
|
|
369
|
+
|
|
370
|
+
return tokens.decodedIdToken_original.resource_access;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* The base64 encoded token that can be sent in the Authorization header in
|
|
375
|
+
* requests to services.
|
|
376
|
+
*/
|
|
377
|
+
get token(): string | undefined {
|
|
378
|
+
const internalState = internalStateByInstance.get(this);
|
|
379
|
+
|
|
380
|
+
assert(internalState !== undefined);
|
|
381
|
+
|
|
382
|
+
if (!this.didInitialize) {
|
|
383
|
+
return internalState.initOptions?.token;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const { oidc, tokens } = internalState;
|
|
387
|
+
|
|
388
|
+
assert(oidc !== undefined);
|
|
389
|
+
|
|
390
|
+
if (!oidc.isUserLoggedIn) {
|
|
391
|
+
console.warn(
|
|
392
|
+
"Trying to read keycloak.token when keycloak.token is false is a logical error in your application"
|
|
393
|
+
);
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
assert(tokens !== undefined);
|
|
398
|
+
|
|
399
|
+
return tokens.accessToken;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* The parsed token as a JavaScript object.
|
|
404
|
+
*/
|
|
405
|
+
get tokenParsed(): KeycloakTokenParsed | undefined {
|
|
406
|
+
const internalState = internalStateByInstance.get(this);
|
|
407
|
+
|
|
408
|
+
assert(internalState !== undefined);
|
|
409
|
+
|
|
410
|
+
if (!this.didInitialize) {
|
|
411
|
+
const { token } = internalState.initOptions ?? {};
|
|
412
|
+
|
|
413
|
+
if (token === undefined) {
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return decodeJwt(token) as KeycloakTokenParsed;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const { oidc, tokens } = internalState;
|
|
421
|
+
|
|
422
|
+
assert(oidc !== undefined);
|
|
423
|
+
|
|
424
|
+
if (!oidc.isUserLoggedIn) {
|
|
425
|
+
console.warn(
|
|
426
|
+
"Trying to read keycloak.token when keycloak.tokenParsed is false is a logical error in your application"
|
|
427
|
+
);
|
|
428
|
+
return undefined;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
assert(tokens !== undefined);
|
|
432
|
+
|
|
433
|
+
return decodeJwt(tokens.accessToken);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* The base64 encoded refresh token that can be used to retrieve a new token.
|
|
438
|
+
*/
|
|
439
|
+
get refreshToken(): string | undefined {
|
|
440
|
+
const internalState = internalStateByInstance.get(this);
|
|
441
|
+
|
|
442
|
+
assert(internalState !== undefined);
|
|
443
|
+
|
|
444
|
+
if (!this.didInitialize) {
|
|
445
|
+
return internalState.initOptions?.refreshToken;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const { oidc, tokens } = internalState;
|
|
449
|
+
|
|
450
|
+
assert(oidc !== undefined);
|
|
451
|
+
|
|
452
|
+
if (!oidc.isUserLoggedIn) {
|
|
453
|
+
console.warn(
|
|
454
|
+
"Trying to read keycloak.token when keycloak.refreshToken is false is a logical error in your application"
|
|
455
|
+
);
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
assert(tokens !== undefined);
|
|
460
|
+
|
|
461
|
+
return tokens.refreshToken;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* The parsed refresh token as a JavaScript object.
|
|
466
|
+
*/
|
|
467
|
+
get refreshTokenParsed(): KeycloakTokenParsed | undefined {
|
|
468
|
+
const internalState = internalStateByInstance.get(this);
|
|
469
|
+
|
|
470
|
+
assert(internalState !== undefined);
|
|
471
|
+
|
|
472
|
+
if (!this.didInitialize) {
|
|
473
|
+
const { refreshToken } = internalState.initOptions ?? {};
|
|
474
|
+
|
|
475
|
+
if (refreshToken === undefined) {
|
|
476
|
+
return undefined;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return decodeJwt(refreshToken) as KeycloakTokenParsed;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const { oidc, tokens } = internalState;
|
|
483
|
+
|
|
484
|
+
assert(oidc !== undefined);
|
|
485
|
+
|
|
486
|
+
if (!oidc.isUserLoggedIn) {
|
|
487
|
+
console.warn(
|
|
488
|
+
"Trying to read keycloak.token when keycloak.refreshTokenParsed is false is a logical error in your application"
|
|
489
|
+
);
|
|
490
|
+
return undefined;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
assert(tokens !== undefined);
|
|
494
|
+
|
|
495
|
+
if (tokens.refreshToken === undefined) {
|
|
496
|
+
return undefined;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return decodeJwt(tokens.refreshToken) as KeycloakTokenParsed;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* The base64 encoded ID token.
|
|
504
|
+
*/
|
|
505
|
+
get idToken(): string | undefined {
|
|
506
|
+
const internalState = internalStateByInstance.get(this);
|
|
507
|
+
|
|
508
|
+
assert(internalState !== undefined);
|
|
509
|
+
|
|
510
|
+
if (!this.didInitialize) {
|
|
511
|
+
return internalState.initOptions?.idToken;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const { oidc, tokens } = internalState;
|
|
515
|
+
|
|
516
|
+
assert(oidc !== undefined);
|
|
517
|
+
|
|
518
|
+
if (!oidc.isUserLoggedIn) {
|
|
519
|
+
console.warn(
|
|
520
|
+
"Trying to read keycloak.token when keycloak.token is false is a logical error in your application"
|
|
521
|
+
);
|
|
522
|
+
return undefined;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
assert(tokens !== undefined);
|
|
526
|
+
|
|
527
|
+
return tokens.idToken;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* The parsed id token as a JavaScript object.
|
|
532
|
+
*/
|
|
533
|
+
get idTokenParsed(): KeycloakTokenParsed | undefined {
|
|
534
|
+
const internalState = internalStateByInstance.get(this);
|
|
535
|
+
|
|
536
|
+
assert(internalState !== undefined);
|
|
537
|
+
|
|
538
|
+
if (!this.didInitialize) {
|
|
539
|
+
const { idToken } = internalState.initOptions ?? {};
|
|
540
|
+
|
|
541
|
+
if (idToken === undefined) {
|
|
542
|
+
return undefined;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return decodeJwt(idToken) as KeycloakTokenParsed;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const { oidc, tokens } = internalState;
|
|
549
|
+
|
|
550
|
+
assert(oidc !== undefined);
|
|
551
|
+
|
|
552
|
+
if (!oidc.isUserLoggedIn) {
|
|
553
|
+
console.warn(
|
|
554
|
+
"Trying to read keycloak.token when keycloak.refreshTokenParsed is false is a logical error in your application"
|
|
555
|
+
);
|
|
556
|
+
return undefined;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
assert(tokens !== undefined);
|
|
560
|
+
assert(is<KeycloakTokenParsed>(tokens.decodedIdToken_original));
|
|
561
|
+
|
|
562
|
+
return tokens.decodedIdToken_original;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* The estimated time difference between the browser time and the Keycloak
|
|
567
|
+
* server in seconds. This value is just an estimation, but is accurate
|
|
568
|
+
* enough when determining if a token is expired or not.
|
|
569
|
+
*
|
|
570
|
+
* NOTE oidc-spa: Not supported.
|
|
571
|
+
*/
|
|
572
|
+
timeSkew = null;
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Whether the instance has been initialized by calling `.init()`.
|
|
576
|
+
*/
|
|
577
|
+
get didInitialize(): boolean {
|
|
578
|
+
const internalState = internalStateByInstance.get(this);
|
|
579
|
+
assert(internalState !== undefined);
|
|
580
|
+
return internalState.oidc !== undefined;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* @private Undocumented.
|
|
585
|
+
*/
|
|
586
|
+
get loginRequired(): boolean {
|
|
587
|
+
const internalState = internalStateByInstance.get(this);
|
|
588
|
+
assert(internalState !== undefined);
|
|
589
|
+
|
|
590
|
+
const { initOptions } = internalState;
|
|
591
|
+
|
|
592
|
+
if (initOptions === undefined) {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return initOptions.onLoad === "login-required";
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* @private Undocumented.
|
|
601
|
+
*/
|
|
602
|
+
get authServerUrl(): string {
|
|
603
|
+
const internalState = internalStateByInstance.get(this);
|
|
604
|
+
assert(internalState !== undefined);
|
|
605
|
+
const {
|
|
606
|
+
keycloakUtils: { issuerUriParsed }
|
|
607
|
+
} = internalState;
|
|
608
|
+
|
|
609
|
+
return `${issuerUriParsed.origin}${issuerUriParsed.kcHttpRelativePath}`;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* @private Undocumented.
|
|
614
|
+
*/
|
|
615
|
+
get realm(): string {
|
|
616
|
+
const internalState = internalStateByInstance.get(this);
|
|
617
|
+
assert(internalState !== undefined);
|
|
618
|
+
const {
|
|
619
|
+
keycloakUtils: { issuerUriParsed }
|
|
620
|
+
} = internalState;
|
|
621
|
+
|
|
622
|
+
return issuerUriParsed.realm;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* @private Undocumented.
|
|
627
|
+
*/
|
|
628
|
+
get clientId(): string {
|
|
629
|
+
const internalState = internalStateByInstance.get(this);
|
|
630
|
+
assert(internalState !== undefined);
|
|
631
|
+
const { constructorParams } = internalState;
|
|
632
|
+
return constructorParams.clientId;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* @private Undocumented.
|
|
637
|
+
*/
|
|
638
|
+
get redirectUri(): string | undefined {
|
|
639
|
+
const internalState = internalStateByInstance.get(this);
|
|
640
|
+
assert(internalState !== undefined);
|
|
641
|
+
const { initOptions } = internalState;
|
|
642
|
+
if (initOptions === undefined) {
|
|
643
|
+
return undefined;
|
|
644
|
+
}
|
|
645
|
+
return initOptions.redirectUri;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* @private Undocumented.
|
|
650
|
+
*/
|
|
651
|
+
get sessionId(): string | undefined {
|
|
652
|
+
if (!this.didInitialize) {
|
|
653
|
+
return undefined;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const internalState = internalStateByInstance.get(this);
|
|
657
|
+
assert(internalState !== undefined);
|
|
658
|
+
const { oidc, tokens } = internalState;
|
|
659
|
+
|
|
660
|
+
assert(oidc !== undefined);
|
|
661
|
+
|
|
662
|
+
if (!oidc.isUserLoggedIn) {
|
|
663
|
+
console.warn(
|
|
664
|
+
"Trying to read keycloak.sessionId when keycloak.authenticated is false is a logical error in your application"
|
|
665
|
+
);
|
|
666
|
+
return undefined;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
assert(tokens !== undefined);
|
|
670
|
+
|
|
671
|
+
const { sid } = tokens.decodedIdToken_original;
|
|
672
|
+
|
|
673
|
+
assert(typeof sid === "string");
|
|
674
|
+
|
|
675
|
+
return sid;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* @private Undocumented.
|
|
680
|
+
*/
|
|
681
|
+
get profile(): KeycloakProfile | undefined {
|
|
682
|
+
const internalState = internalStateByInstance.get(this);
|
|
683
|
+
assert(internalState !== undefined);
|
|
684
|
+
const { profile } = internalState;
|
|
685
|
+
return profile;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* @private Undocumented.
|
|
690
|
+
*/
|
|
691
|
+
get userInfo(): KeycloakUserInfo | undefined {
|
|
692
|
+
const internalState = internalStateByInstance.get(this);
|
|
693
|
+
assert(internalState !== undefined);
|
|
694
|
+
const { userInfo } = internalState;
|
|
695
|
+
return userInfo;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Called when the adapter is initialized.
|
|
700
|
+
*/
|
|
701
|
+
onReady?(authenticated: boolean): void;
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Called when a user is successfully authenticated.
|
|
705
|
+
*/
|
|
706
|
+
onAuthSuccess?(): void;
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Called if there was an error during authentication.
|
|
710
|
+
*/
|
|
711
|
+
onAuthError?(errorData: KeycloakError): void;
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Called when the token is refreshed.
|
|
715
|
+
*/
|
|
716
|
+
onAuthRefreshSuccess?(): void;
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Called if there was an error while trying to refresh the token.
|
|
720
|
+
*
|
|
721
|
+
* NOTE oidc-spa: In oidc-spa an auth refresh error always triggers a page refresh.
|
|
722
|
+
*/
|
|
723
|
+
//onAuthRefreshError?(): void;
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Called if the user is logged out (will only be called if the session
|
|
727
|
+
* status iframe is enabled, or in Cordova mode).
|
|
728
|
+
*
|
|
729
|
+
* NOTE oidc-spa: In oidc-spa a logout always triggers a page refresh.
|
|
730
|
+
*/
|
|
731
|
+
//onAuthLogout?(): void;
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Called when the access token is expired. If a refresh token is available
|
|
735
|
+
* the token can be refreshed with Keycloak#updateToken, or in cases where
|
|
736
|
+
* it's not (ie. with implicit flow) you can redirect to login screen to
|
|
737
|
+
* obtain a new access token.
|
|
738
|
+
*/
|
|
739
|
+
set onTokenExpired(value: (() => void) | undefined) {
|
|
740
|
+
const internalState = internalStateByInstance.get(this);
|
|
741
|
+
assert(internalState !== undefined);
|
|
742
|
+
const { $onTokenExpired } = internalState;
|
|
743
|
+
$onTokenExpired.current = value;
|
|
744
|
+
}
|
|
745
|
+
get onTokenExpired() {
|
|
746
|
+
const internalState = internalStateByInstance.get(this);
|
|
747
|
+
assert(internalState !== undefined);
|
|
748
|
+
const { $onTokenExpired } = internalState;
|
|
749
|
+
return $onTokenExpired.current;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Called when a AIA has been requested by the application.
|
|
754
|
+
* @param status the outcome of the required action
|
|
755
|
+
* @param action the alias name of the required action, e.g. UPDATE_PASSWORD, CONFIGURE_TOTP etc.
|
|
756
|
+
*/
|
|
757
|
+
onActionUpdate?(status: "success" | "cancelled" | "error", action?: string): void;
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Redirects to login form.
|
|
761
|
+
* @param options Login options.
|
|
762
|
+
*/
|
|
763
|
+
async login(
|
|
764
|
+
options?: KeycloakLoginOptions & { doesCurrentHrefRequiresAuth?: boolean }
|
|
765
|
+
): Promise<never> {
|
|
766
|
+
const {
|
|
767
|
+
redirectUri,
|
|
768
|
+
action,
|
|
769
|
+
loginHint,
|
|
770
|
+
acr,
|
|
771
|
+
acrValues,
|
|
772
|
+
idpHint,
|
|
773
|
+
locale,
|
|
774
|
+
doesCurrentHrefRequiresAuth
|
|
775
|
+
} = options ?? {};
|
|
776
|
+
|
|
777
|
+
const internalState = internalStateByInstance.get(this);
|
|
778
|
+
assert(internalState !== undefined);
|
|
779
|
+
|
|
780
|
+
if (!this.didInitialize) {
|
|
781
|
+
await internalState.dInitialized.pr;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const { oidc, keycloakUtils } = internalState;
|
|
785
|
+
|
|
786
|
+
assert(oidc !== undefined);
|
|
787
|
+
|
|
788
|
+
const extraQueryParams_commons: Record<string, string | undefined> = {
|
|
789
|
+
claims:
|
|
790
|
+
acr === undefined
|
|
791
|
+
? undefined
|
|
792
|
+
: JSON.stringify({
|
|
793
|
+
id_token: {
|
|
794
|
+
acr
|
|
795
|
+
}
|
|
796
|
+
}),
|
|
797
|
+
acr_values: acrValues,
|
|
798
|
+
ui_locales: locale
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
if (oidc.isUserLoggedIn) {
|
|
802
|
+
assert(action !== "register");
|
|
803
|
+
assert(loginHint === undefined);
|
|
804
|
+
assert(idpHint === undefined);
|
|
805
|
+
assert(doesCurrentHrefRequiresAuth === undefined);
|
|
806
|
+
|
|
807
|
+
await oidc.goToAuthServer({
|
|
808
|
+
redirectUrl: redirectUri,
|
|
809
|
+
extraQueryParams: {
|
|
810
|
+
...extraQueryParams_commons,
|
|
811
|
+
kc_action: action,
|
|
812
|
+
ui_locales: locale
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
assert(false);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
assert(action === undefined || action === "register");
|
|
819
|
+
|
|
820
|
+
await oidc.login({
|
|
821
|
+
redirectUrl: redirectUri,
|
|
822
|
+
doesCurrentHrefRequiresAuth: doesCurrentHrefRequiresAuth ?? false,
|
|
823
|
+
extraQueryParams: {
|
|
824
|
+
...extraQueryParams_commons,
|
|
825
|
+
login_hint: loginHint,
|
|
826
|
+
kc_idp_hint: idpHint
|
|
827
|
+
},
|
|
828
|
+
transformUrlBeforeRedirect:
|
|
829
|
+
action !== "register" ? undefined : keycloakUtils.transformUrlBeforeRedirectForRegister
|
|
830
|
+
});
|
|
831
|
+
assert(false);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Redirects to logout.
|
|
836
|
+
* @param options Logout options.
|
|
837
|
+
*/
|
|
838
|
+
async logout(options?: KeycloakLogoutOptions): Promise<never> {
|
|
839
|
+
const internalState = internalStateByInstance.get(this);
|
|
840
|
+
|
|
841
|
+
assert(internalState !== undefined);
|
|
842
|
+
|
|
843
|
+
if (!this.didInitialize) {
|
|
844
|
+
await internalState.dInitialized.pr;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const { oidc, initOptions } = internalState;
|
|
848
|
+
|
|
849
|
+
assert(oidc !== undefined);
|
|
850
|
+
assert(initOptions !== undefined);
|
|
851
|
+
|
|
852
|
+
assert(oidc.isUserLoggedIn, "The user is not currently logged in");
|
|
853
|
+
|
|
854
|
+
const redirectUri = options?.redirectUri ?? initOptions.redirectUri;
|
|
855
|
+
|
|
856
|
+
await oidc.logout({
|
|
857
|
+
...(redirectUri === undefined
|
|
858
|
+
? { redirectTo: "current page" }
|
|
859
|
+
: { redirectTo: "specific url", url: redirectUri })
|
|
860
|
+
});
|
|
861
|
+
assert(false);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Redirects to registration form.
|
|
866
|
+
* @param options The options used for the registration.
|
|
867
|
+
*/
|
|
868
|
+
async register(options?: KeycloakRegisterOptions): Promise<never> {
|
|
869
|
+
return this.login({
|
|
870
|
+
...options,
|
|
871
|
+
action: "register"
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Redirects to the Account Management Console.
|
|
877
|
+
*/
|
|
878
|
+
async accountManagement(options?: {
|
|
879
|
+
/**
|
|
880
|
+
* Specifies the uri to redirect to when redirecting back to the application.
|
|
881
|
+
*/
|
|
882
|
+
redirectUri?: string;
|
|
883
|
+
locale?: string;
|
|
884
|
+
}): Promise<never> {
|
|
885
|
+
const { redirectUri, locale } = options ?? {};
|
|
886
|
+
|
|
887
|
+
window.location.href = this.createAccountUrl({
|
|
888
|
+
redirectUri,
|
|
889
|
+
locale
|
|
890
|
+
});
|
|
891
|
+
return new Promise<never>(() => {});
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Returns the URL to login form.
|
|
896
|
+
* @param options Supports same options as Keycloak#login.
|
|
897
|
+
*
|
|
898
|
+
* NOTE oidc-spa: Not supported, please use login() method.
|
|
899
|
+
*/
|
|
900
|
+
//createLoginUrl(options?: KeycloakLoginOptions): Promise<string>;
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Returns the URL to logout the user.
|
|
904
|
+
* @param options Logout options.
|
|
905
|
+
*
|
|
906
|
+
* NOTE oidc-spa: Not supported, please use logout() method.
|
|
907
|
+
*/
|
|
908
|
+
//createLogoutUrl(options?: KeycloakLogoutOptions): string;
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Returns the URL to registration page.
|
|
912
|
+
* @param options The options used for creating the registration URL.
|
|
913
|
+
*
|
|
914
|
+
* NOTE oidc-spa: Not supported please user login({ action: "register" })
|
|
915
|
+
*/
|
|
916
|
+
//createRegisterUrl(options?: KeycloakRegisterOptions): Promise<string>;
|
|
917
|
+
|
|
918
|
+
/**
|
|
919
|
+
* Returns the URL to the Account Management Console.
|
|
920
|
+
* @param options The options used for creating the account URL.
|
|
921
|
+
*/
|
|
922
|
+
createAccountUrl(options?: KeycloakAccountOptions & { locale?: string }): string {
|
|
923
|
+
const { locale, redirectUri } = options ?? {};
|
|
924
|
+
|
|
925
|
+
const internalState = internalStateByInstance.get(this);
|
|
926
|
+
|
|
927
|
+
assert(internalState !== undefined);
|
|
928
|
+
|
|
929
|
+
const { keycloakUtils } = internalState;
|
|
930
|
+
|
|
931
|
+
return keycloakUtils.getAccountUrl({
|
|
932
|
+
clientId: this.clientId,
|
|
933
|
+
backToAppFromAccountUrl: redirectUri ?? location.href,
|
|
934
|
+
locale
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* Returns true if the token has less than `minValidity` seconds left before
|
|
940
|
+
* it expires.
|
|
941
|
+
* @param minValidity If not specified, `0` is used.
|
|
942
|
+
*/
|
|
943
|
+
isTokenExpired(minValidity: number = 0): boolean {
|
|
944
|
+
const internalState = internalStateByInstance.get(this);
|
|
945
|
+
assert(internalState !== undefined);
|
|
946
|
+
|
|
947
|
+
let accessTokenExpirationTime: number;
|
|
948
|
+
|
|
949
|
+
if (!this.didInitialize) {
|
|
950
|
+
const fakeAccessToken = this.token;
|
|
951
|
+
if (fakeAccessToken === undefined) {
|
|
952
|
+
throw new Error("isTokenExpired was called too early");
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
const time = readExpirationTimeInJwt(fakeAccessToken);
|
|
956
|
+
|
|
957
|
+
assert(time !== undefined, "The initial token is not a JWT");
|
|
958
|
+
|
|
959
|
+
accessTokenExpirationTime = time;
|
|
960
|
+
} else {
|
|
961
|
+
const { tokens } = internalState;
|
|
962
|
+
assert(tokens !== undefined);
|
|
963
|
+
|
|
964
|
+
accessTokenExpirationTime = tokens.accessTokenExpirationTime;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
if (accessTokenExpirationTime > Date.now() + minValidity * 1_000) {
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
return true;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* If the token expires within `minValidity` seconds, the token is refreshed.
|
|
976
|
+
* If the session status iframe is enabled, the session status is also
|
|
977
|
+
* checked.
|
|
978
|
+
* @param minValidity If not specified, `5` is used.
|
|
979
|
+
* @returns A promise to set functions that can be invoked if the token is
|
|
980
|
+
* still valid, or if the token is no longer valid.
|
|
981
|
+
* @example
|
|
982
|
+
* ```js
|
|
983
|
+
* keycloak.updateToken(5).then(function(refreshed) {
|
|
984
|
+
* if (refreshed) {
|
|
985
|
+
* alert('Token was successfully refreshed');
|
|
986
|
+
* } else {
|
|
987
|
+
* alert('Token is still valid');
|
|
988
|
+
* }
|
|
989
|
+
* }).catch(function() {
|
|
990
|
+
* alert('Failed to refresh the token, or the session has expired');
|
|
991
|
+
* });
|
|
992
|
+
*/
|
|
993
|
+
async updateToken(minValidity: number = 5): Promise<boolean> {
|
|
994
|
+
const internalState = internalStateByInstance.get(this);
|
|
995
|
+
|
|
996
|
+
assert(internalState !== undefined);
|
|
997
|
+
|
|
998
|
+
if (!this.didInitialize) {
|
|
999
|
+
await internalState.dInitialized.pr;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
const { oidc } = internalState;
|
|
1003
|
+
|
|
1004
|
+
assert(oidc !== undefined);
|
|
1005
|
+
|
|
1006
|
+
assert(oidc.isUserLoggedIn, "updateToken called too early");
|
|
1007
|
+
|
|
1008
|
+
if (!this.isTokenExpired(minValidity)) {
|
|
1009
|
+
return false;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
await oidc.renewTokens();
|
|
1013
|
+
|
|
1014
|
+
return true;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* Clears authentication state, including tokens. This can be useful if
|
|
1019
|
+
* the application has detected the session was expired, for example if
|
|
1020
|
+
* updating token fails. Invoking this results in Keycloak#onAuthLogout
|
|
1021
|
+
* callback listener being invoked.
|
|
1022
|
+
*
|
|
1023
|
+
* NOTE oidc-spa: In this implementation we never end up in the kind of
|
|
1024
|
+
* state where calling this makes sense.
|
|
1025
|
+
* oidc-spa take more control and exposes less complexity to the user of the
|
|
1026
|
+
* adapter.
|
|
1027
|
+
*/
|
|
1028
|
+
//clearToken(): void;
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* Returns true if the token has the given realm role.
|
|
1032
|
+
* @param role A realm role name.
|
|
1033
|
+
*/
|
|
1034
|
+
hasRealmRole(role: string): boolean {
|
|
1035
|
+
const access = this.realmAccess;
|
|
1036
|
+
return access !== undefined && access.roles.indexOf(role) >= 0;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Returns true if the token has the given role for the resource.
|
|
1041
|
+
* @param role A role name.
|
|
1042
|
+
* @param resource If not specified, `clientId` is used.
|
|
1043
|
+
*/
|
|
1044
|
+
hasResourceRole(role: string, resource?: string): boolean {
|
|
1045
|
+
if (this.resourceAccess === undefined) {
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
const access = this.resourceAccess[resource || this.clientId];
|
|
1050
|
+
return access !== undefined && access.roles.indexOf(role) >= 0;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/**
|
|
1054
|
+
* Loads the user's profile.
|
|
1055
|
+
* @returns A promise to set functions to be invoked on success or error.
|
|
1056
|
+
*/
|
|
1057
|
+
async loadUserProfile(): Promise<KeycloakProfile> {
|
|
1058
|
+
const internalState = internalStateByInstance.get(this);
|
|
1059
|
+
assert(internalState !== undefined);
|
|
1060
|
+
|
|
1061
|
+
if (!this.didInitialize) {
|
|
1062
|
+
await internalState.dInitialized.pr;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
const { oidc, keycloakUtils } = internalState;
|
|
1066
|
+
|
|
1067
|
+
assert(oidc !== undefined);
|
|
1068
|
+
|
|
1069
|
+
assert(oidc.isUserLoggedIn, "Can't load userProfile if user not authenticated");
|
|
1070
|
+
|
|
1071
|
+
const { accessToken } = await oidc.getTokens();
|
|
1072
|
+
|
|
1073
|
+
return (internalState.profile = await keycloakUtils.fetchUserProfile({ accessToken }));
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
/**
|
|
1077
|
+
* @private Undocumented.
|
|
1078
|
+
*/
|
|
1079
|
+
async loadUserInfo(): Promise<KeycloakUserInfo> {
|
|
1080
|
+
const internalState = internalStateByInstance.get(this);
|
|
1081
|
+
assert(internalState !== undefined);
|
|
1082
|
+
|
|
1083
|
+
if (!this.didInitialize) {
|
|
1084
|
+
await internalState.dInitialized.pr;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
const { oidc, keycloakUtils } = internalState;
|
|
1088
|
+
|
|
1089
|
+
assert(oidc !== undefined);
|
|
1090
|
+
|
|
1091
|
+
assert(oidc.isUserLoggedIn, "Can't load userInfo if user not authenticated");
|
|
1092
|
+
|
|
1093
|
+
const { accessToken } = await oidc.getTokens();
|
|
1094
|
+
|
|
1095
|
+
return (internalState.userInfo = await keycloakUtils.fetchUserInfo({ accessToken }));
|
|
1096
|
+
}
|
|
1097
|
+
}
|