Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/oidc-client-ts.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -900,12 +900,13 @@ export class User {
expires_at?: number;
userState?: unknown;
url_state?: string;
});
}, extraTokenResponseKeys?: string[]);
access_token: string;
get expired(): boolean | undefined;
expires_at?: number;
get expires_in(): number | undefined;
set expires_in(value: number | undefined);
extraTokenResponseProperties?: Record<string, unknown>;
// (undocumented)
static fromStorageString(storageString: string): User;
id_token?: string;
Expand Down Expand Up @@ -1050,6 +1051,7 @@ export interface UserManagerSettings extends OidcClientSettings {
accessTokenExpiringNotificationTimeInSeconds?: number;
automaticSilentRenew?: boolean;
checkSessionIntervalInSeconds?: number;
extraTokenResponseKeys?: string[];
iframeNotifyParentOrigin?: string;
iframeScriptOrigin?: string;
includeIdTokenInSilentRenew?: boolean;
Expand Down Expand Up @@ -1086,6 +1088,8 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore {
// (undocumented)
readonly checkSessionIntervalInSeconds: number;
// (undocumented)
readonly extraTokenResponseKeys: string[];
// (undocumented)
readonly iframeNotifyParentOrigin: string | undefined;
// (undocumented)
readonly iframeScriptOrigin: string | undefined;
Expand Down
17 changes: 17 additions & 0 deletions src/User.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,21 @@ describe("User", () => {
expect(subject.scopes).toEqual(["foo", "bar", "baz"]);
});
});

describe("extraTokenResponseProperties", () => {
it("should not provide extraTokenResponseProperties if extraTokenResponseKeys is not provided", () => {
const subject = new User({} as never);
expect(subject.extraTokenResponseProperties).not.toBeDefined();
});
it("should provide extraTokenResponseProperties if extraTokenResponseKeys is provided", () => {
const patient = "12345";
const subject = new User({ access_token: "notAToken", id_token: "not", patient, token_type: "Bearer" } as never, ["patient"]);
const actualTokenProps = subject.extraTokenResponseProperties ?? {};
const keys = Object.keys(actualTokenProps);
const values = Object.values(actualTokenProps);
expect(subject.extraTokenResponseProperties).toBeDefined();
expect(keys).toContain("patient");
expect(values).toContain(patient);
});
});
});
14 changes: 13 additions & 1 deletion src/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ export class User {
/** The expires at returned from the OIDC provider. */
public expires_at?: number;

/** The additional Token response properties the user has requested to keep */
public extraTokenResponseProperties?: Record<string, unknown>;

/** custom state data set during the initial signin request */
public readonly state: unknown;
public readonly url_state?: string;
Expand All @@ -64,7 +67,7 @@ export class User {
expires_at?: number;
userState?: unknown;
url_state?: string;
}) {
}, extraTokenResponseKeys?: string[]) {
this.id_token = args.id_token;
this.session_state = args.session_state ?? null;
this.access_token = args.access_token;
Expand All @@ -76,6 +79,15 @@ export class User {
this.expires_at = args.expires_at;
this.state = args.userState;
this.url_state = args.url_state;
if (extraTokenResponseKeys?.length) {
this.extraTokenResponseProperties = {};
const argsCast = args as Record<string, unknown>;
for (const key of extraTokenResponseKeys) {
if (argsCast[key]) {
this.extraTokenResponseProperties[key] = argsCast[key];
}
}
}
}

/** Computed number of seconds the access token has remaining. */
Expand Down
2 changes: 1 addition & 1 deletion src/UserManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ export class UserManager {

protected async _buildUser(signinResponse: SigninResponse, verifySub?: string) {
const logger = this._logger.create("_buildUser");
const user = new User(signinResponse);
const user = new User(signinResponse, this.settings.extraTokenResponseKeys);
if (verifySub) {
if (verifySub !== user.profile.sub) {
logger.debug("current user does not match user returned from signin. sub from signin:", user.profile.sub);
Expand Down
20 changes: 20 additions & 0 deletions src/UserManagerSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,24 @@ describe("UserManagerSettings", () => {
expect(subject.stopCheckSessionOnError).toEqual(true);
});
});

describe("extraTokenResponseKeys", () => {
it("should return value from the initial settings", () => {
const subject = new UserManagerSettingsStore({
authority: "authority",
client_id: "client",
redirect_uri: "redirect",
extraTokenResponseKeys: ["testProp"],
});
expect(subject.extraTokenResponseKeys).toEqual(["testProp"]);
});
it("should return the default value", () => {
const subject = new UserManagerSettingsStore({
authority: "authority",
client_id: "client",
redirect_uri: "redirect",
});
expect(subject.extraTokenResponseKeys).toEqual([]);
});
});
});
12 changes: 11 additions & 1 deletion src/UserManagerSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ export interface UserManagerSettings extends OidcClientSettings {
/** The number of seconds before an access token is to expire to raise the accessTokenExpiring event (default: 60) */
accessTokenExpiringNotificationTimeInSeconds?: number;

/**
* By default the User object only preserves the standard properties
* (access_token, session_state, id_token, refresh_token, token_type, scope, profile, and expiration).
* Any additional properties returned by the OIDC/OAuth2 token response will be ignored unlesss explicitly listed here.
*/
extraTokenResponseKeys?: string[];

/**
* Storage object used to persist User for currently authenticated user (default: window.sessionStorage, InMemoryWebStorage iff no window).
* E.g. `userStore: new WebStorageStateStore({ store: window.localStorage })`
Expand Down Expand Up @@ -120,6 +127,8 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore {

public readonly accessTokenExpiringNotificationTimeInSeconds: number;

public readonly extraTokenResponseKeys: string[];

public readonly userStore: WebStorageStateStore;

public constructor(args: UserManagerSettings) {
Expand Down Expand Up @@ -151,7 +160,7 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore {
includeIdTokenInSilentSignout = false,

accessTokenExpiringNotificationTimeInSeconds = DefaultAccessTokenExpiringNotificationTimeInSeconds,

extraTokenResponseKeys = [],
userStore,
} = args;

Expand Down Expand Up @@ -184,6 +193,7 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore {
this.includeIdTokenInSilentSignout = includeIdTokenInSilentSignout;

this.accessTokenExpiringNotificationTimeInSeconds = accessTokenExpiringNotificationTimeInSeconds;
this.extraTokenResponseKeys = extraTokenResponseKeys;

if (userStore) {
this.userStore = userStore;
Expand Down