Skip to content

Commit 40fb449

Browse files
- Add support for OIDC session management error status
1 parent a1cc83c commit 40fb449

6 files changed

+85
-34
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ node_modules/
2222
.vscode/
2323
temp/
2424
.history/
25+
26+
.idea
27+
*.iml

docs/oidc-client-ts.api.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class AccessTokenEvents {
2626

2727
// @internal (undocumented)
2828
export class CheckSessionIFrame {
29-
constructor(_callback: () => Promise<void>, _client_id: string, url: string, _intervalInSeconds: number, _stopOnError: boolean);
29+
constructor(_callback: () => Promise<void>, _client_id: string, url: string, _intervalInSeconds: number, _stopOnError: boolean, propagateUserSessionError: boolean, userSessionErrorCallback: () => void);
3030
// (undocumented)
3131
load(): Promise<void>;
3232
// (undocumented)
@@ -988,6 +988,8 @@ export interface UserManagerSettings extends OidcClientSettings {
988988
popupWindowFeatures?: PopupWindowFeatures;
989989
popupWindowTarget?: string;
990990
// (undocumented)
991+
propagateUserSessionError?: boolean;
992+
// (undocumented)
991993
query_status_response_type?: string;
992994
redirectMethod?: "replace" | "assign";
993995
redirectTarget?: "top" | "self";
@@ -1029,6 +1031,8 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore {
10291031
// (undocumented)
10301032
readonly popupWindowTarget: string;
10311033
// (undocumented)
1034+
readonly propagateUserSessionError: boolean;
1035+
// (undocumented)
10321036
readonly query_status_response_type: string;
10331037
// (undocumented)
10341038
readonly redirectMethod: "replace" | "assign";

src/CheckSessionIFrame.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export class CheckSessionIFrame {
1919
url: string,
2020
private _intervalInSeconds: number,
2121
private _stopOnError: boolean,
22+
private propagateUserSessionError: boolean,
23+
private userSessionErrorCallback: () => void,
2224
) {
2325
const parsedUrl = new URL(url);
2426
this._frame_origin = parsedUrl.origin;
@@ -55,6 +57,10 @@ export class CheckSessionIFrame {
5557
if (this._stopOnError) {
5658
this.stop();
5759
}
60+
61+
if (this.propagateUserSessionError) {
62+
this.userSessionErrorCallback();
63+
}
5864
}
5965
else if (e.data === "changed") {
6066
this._logger.debug("changed message from check session op iframe");

src/SessionMonitor.ts

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ export class SessionMonitor {
3737
// doesn't trigger load event.
3838
if (user) {
3939
void this._start(user);
40-
}
41-
else if (this._userManager.settings.monitorAnonymousSession) {
40+
} else if (this._userManager.settings.monitorAnonymousSession) {
4241
const session = await this._userManager.querySessionStatus();
4342
if (session) {
4443
const tmpUser = {
@@ -69,8 +68,7 @@ export class SessionMonitor {
6968
this._sub = user.profile.sub;
7069
this._sid = user.profile.sid;
7170
logger.debug("session_state", session_state, ", sub", this._sub);
72-
}
73-
else {
71+
} else {
7472
this._sub = undefined;
7573
this._sid = undefined;
7674
logger.debug("session_state", session_state, ", anonymous user");
@@ -90,21 +88,27 @@ export class SessionMonitor {
9088
const intervalInSeconds = this._userManager.settings.checkSessionIntervalInSeconds;
9189
const stopOnError = this._userManager.settings.stopCheckSessionOnError;
9290

93-
const checkSessionIFrame = new CheckSessionIFrame(this._callback, client_id, url, intervalInSeconds, stopOnError);
91+
const checkSessionIFrame = new CheckSessionIFrame(this._callback, client_id, url, intervalInSeconds, stopOnError, this.doUserSessionErrorPropagation(), this.raiseUserSessionError.bind(this));
9492
await checkSessionIFrame.load();
9593
this._checkSessionIFrame = checkSessionIFrame;
9694
checkSessionIFrame.start(session_state);
97-
}
98-
else {
95+
} else {
9996
logger.warn("no check session iframe found in the metadata");
10097
}
101-
}
102-
catch (err) {
98+
} catch (err) {
10399
// catch to suppress errors since we're in non-promise callback
104100
logger.error("Error from getCheckSessionIframe:", err instanceof Error ? err.message : err);
105101
}
106102
};
107103

104+
private doUserSessionErrorPropagation(): boolean {
105+
return this._userManager.settings.propagateUserSessionError;
106+
}
107+
108+
private raiseUserSessionError(): void {
109+
this._userManager.events._raiseUserSessionError.bind(this);
110+
}
111+
108112
protected _stop = (): void => {
109113
const logger = this._logger.create("_stop");
110114
this._sub = undefined;
@@ -133,8 +137,7 @@ export class SessionMonitor {
133137
};
134138
void this._start(tmpUser);
135139
}
136-
}
137-
catch (err) {
140+
} catch (err) {
138141
// catch to suppress errors since we're in a callback
139142
logger.error("error from querySessionStatus", err instanceof Error ? err.message : err);
140143
}
@@ -155,32 +158,27 @@ export class SessionMonitor {
155158

156159
if (session.sid === this._sid) {
157160
logger.debug("same sub still logged in at OP, restarting check session iframe; session_state", session.session_state);
158-
}
159-
else {
161+
} else {
160162
logger.debug("same sub still logged in at OP, session state has changed, restarting check session iframe; session_state", session.session_state);
161163
this._userManager.events._raiseUserSessionChanged();
162164
}
163-
}
164-
else {
165+
} else {
165166
logger.debug("different subject signed into OP", session.sub);
166167
}
167-
}
168-
else {
168+
} else {
169169
logger.debug("subject no longer signed into OP");
170170
}
171171

172172
if (raiseEvent) {
173173
if (this._sub) {
174174
this._userManager.events._raiseUserSignedOut();
175-
}
176-
else {
175+
} else {
177176
this._userManager.events._raiseUserSignedIn();
178177
}
179178
} else {
180179
logger.debug("no change in session detected, no event to raise");
181180
}
182-
}
183-
catch (err) {
181+
} catch (err) {
184182
if (this._sub) {
185183
logger.debug("Error calling queryCurrentSigninSession; raising signed out event", err);
186184
this._userManager.events._raiseUserSignedOut();

src/UserManagerEvents.test.ts

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,32 +44,68 @@ describe("UserManagerEvents", () => {
4444
expect(cb).toBeCalledTimes(0);
4545
});
4646

47-
it("should pass error to callback", () => {
47+
it("should allow callback", () => {
4848
// arrange
49-
let e: Error | null = null;
50-
const cb = function (arg_e: Error) {
51-
e = arg_e;
52-
};
53-
const expected = new Error("boom");
49+
const cb = jest.fn();
5450

5551
// act
56-
subject.addSilentRenewError(cb);
57-
subject._raiseSilentRenewError(expected);
52+
subject.addUserSessionError(cb);
53+
subject._raiseUserSessionError();
5854

5955
// assert
60-
expect(e).toEqual(expected);
56+
expect(cb).toBeCalled();
6157
});
6258

63-
it("should pass error to callback", () => {
59+
it("should allow unregistering callback", () => {
60+
// arrange
61+
const cb = jest.fn();
62+
63+
// act
64+
subject.addUserSessionError(cb);
65+
subject.removeUserSessionError(cb);
66+
subject._raiseUserSessionError();
67+
68+
// assert
69+
expect(cb).toBeCalledTimes(0);
70+
});
71+
72+
it("should allow callback", () => {
6473
// arrange
65-
const e: Error | null = null;
6674
const cb = jest.fn();
67-
const expected = new Error("boom");
6875

6976
// act
7077
subject.addUserSessionError(cb);
7178
subject._raiseUserSessionError();
7279

80+
// assert
81+
expect(cb).toBeCalled();
82+
});
83+
84+
it("should allow unregistering callback", () => {
85+
// arrange
86+
const cb = jest.fn();
87+
88+
// act
89+
subject.addUserSessionError(cb);
90+
subject.removeUserSessionError(cb);
91+
subject._raiseUserSessionError();
92+
93+
// assert
94+
expect(cb).toBeCalledTimes(0);
95+
});
96+
97+
it("should pass error to callback", () => {
98+
// arrange
99+
let e: Error | null = null;
100+
const cb = function (arg_e: Error) {
101+
e = arg_e;
102+
};
103+
const expected = new Error("boom");
104+
105+
// act
106+
subject.addSilentRenewError(cb);
107+
subject._raiseSilentRenewError(expected);
108+
73109
// assert
74110
expect(e).toEqual(expected);
75111
});

src/UserManagerSettings.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export interface UserManagerSettings extends OidcClientSettings {
6262
checkSessionIntervalInSeconds?: number;
6363
query_status_response_type?: string;
6464
stopCheckSessionOnError?: boolean;
65+
propagateUserSessionError?: boolean;
6566

6667
/**
6768
* The `token_type_hint`s to pass to the authority server by default (default: ["access_token", "refresh_token"])
@@ -109,6 +110,7 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore {
109110
public readonly checkSessionIntervalInSeconds: number;
110111
public readonly query_status_response_type: string;
111112
public readonly stopCheckSessionOnError: boolean;
113+
public readonly propagateUserSessionError: boolean;
112114

113115
public readonly revokeTokenTypes: ("access_token" | "refresh_token")[];
114116
public readonly revokeTokensOnSignout: boolean;
@@ -139,6 +141,7 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore {
139141
checkSessionIntervalInSeconds = DefaultCheckSessionIntervalInSeconds,
140142
query_status_response_type = "code",
141143
stopCheckSessionOnError = true,
144+
propagateUserSessionError = false,
142145

143146
revokeTokenTypes = ["access_token", "refresh_token"],
144147
revokeTokensOnSignout = false,
@@ -170,6 +173,7 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore {
170173
this.checkSessionIntervalInSeconds = checkSessionIntervalInSeconds;
171174
this.stopCheckSessionOnError = stopCheckSessionOnError;
172175
this.query_status_response_type = query_status_response_type;
176+
this.propagateUserSessionError = propagateUserSessionError;
173177

174178
this.revokeTokenTypes = revokeTokenTypes;
175179
this.revokeTokensOnSignout = revokeTokensOnSignout;

0 commit comments

Comments
 (0)