Skip to content

Commit

Permalink
fix: #688 id_token is lost on silent refresh token renewal
Browse files Browse the repository at this point in the history
  • Loading branch information
pamapa committed Sep 16, 2022
1 parent a806ab8 commit a0c00a3
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 6 deletions.
4 changes: 4 additions & 0 deletions src/OidcClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { SignoutRequest } from "./SignoutRequest";
import { SignoutResponse } from "./SignoutResponse";
import { RefreshState } from "./RefreshState";
import { SigninResponse } from "./SigninResponse";
import type { UserProfile } from "./User";

describe("OidcClient", () => {
let subject: OidcClient;
Expand Down Expand Up @@ -347,6 +348,7 @@ describe("OidcClient", () => {
id_token: "id_token",
session_state: "session_state",
scope: "openid",
profile: {} as UserProfile,
});

// act
Expand All @@ -371,6 +373,7 @@ describe("OidcClient", () => {
id_token: "id_token",
session_state: "session_state",
scope: "openid",
profile: {} as UserProfile,
});

// act
Expand Down Expand Up @@ -410,6 +413,7 @@ describe("OidcClient", () => {
id_token: "id_token",
session_state: "session_state",
scope: "openid",
profile: {} as UserProfile,
});

// act
Expand Down
7 changes: 7 additions & 0 deletions src/RefreshState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (C) AuthTS Contributors
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

import type { UserProfile } from "./User";

/**
* Fake state store implementation necessary for validating refresh token requests.
*
Expand All @@ -14,18 +16,23 @@ export class RefreshState {
public readonly id_token?: string;
public readonly session_state: string | null;
public readonly scope?: string;
public readonly profile: UserProfile;

constructor(args: {
refresh_token: string;
id_token?: string;
session_state: string | null;
scope?: string;
profile: UserProfile;

state?: unknown;
}) {
this.refresh_token = args.refresh_token;
this.id_token = args.id_token;
this.session_state = args.session_state;
this.scope = args.scope;
this.profile = args.profile;

this.data = args.state;
}
}
17 changes: 12 additions & 5 deletions src/ResponseValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,20 @@ export class ResponseValidator {

// OpenID Connect Core 1.0 says that id_token is optional in refresh response:
// https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse
const hasIdToken = response.isOpenId && !!response.id_token;
if (hasIdToken) {
this._validateIdTokenAttributes(response, state.id_token);
logger.debug("ID Token validated");
if (response.isOpenId) {
if (response.id_token) {
this._validateIdTokenAttributes(response, state.id_token);
logger.debug("ID Token validated");
}
else {
// if there's no id_token on the response, copy over id_token from original request
response.id_token = state.id_token;
response.profile = state.profile;
}
}

await this._processClaims(response, false, hasIdToken);
const hasNewIdToken = response.isOpenId && !!response.id_token;
await this._processClaims(response, false, hasNewIdToken);
logger.debug("claims processed");
}

Expand Down
6 changes: 5 additions & 1 deletion src/UserManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,11 @@ describe("UserManager", () => {
expect(refreshedUser!.profile).toHaveProperty("nickname", "Nicholas");
expect(useRefreshTokenSpy).toBeCalledWith(
expect.objectContaining({
state: { refresh_token: user.refresh_token, session_state: null },
state: {
refresh_token: user.refresh_token,
session_state: null,
"profile": { "nickname": "Nick", "sub": "sub" },
},
}),
);
});
Expand Down

0 comments on commit a0c00a3

Please sign in to comment.