Update Refresh Token, if provided by the server in the refresh grant

The OAuth2 specifications specify that during the refresh grant, the
server may provide an alternate refresh Token along with the access
Token when performing a token refresh.
The oauth2 provider in the `auth-backend` ignores the newer refresh
token during the refresh grant.
This commit will update the refresh token cookie at the end of the
call to the /auth/oauth2/refresh endpoint if the server provides a new
refresh token
grant a
This commit is contained in:
Govindarajan Nagarajan
2020-08-26 13:29:53 +02:00
parent 4b05b9a96a
commit 1665dbbb46
4 changed files with 24 additions and 6 deletions
@@ -258,6 +258,13 @@ export class OAuthProvider implements AuthProviderRouteHandlers {
await this.populateIdentity(response.backstageIdentity);
if (
response.providerInfo.refreshToken &&
response.providerInfo.refreshToken !== refreshToken
) {
this.setRefreshTokenCookie(res, response.providerInfo.refreshToken);
}
res.send(response);
} catch (error) {
res.status(401).send(`${error.message}`);
@@ -45,7 +45,6 @@ export const makeProfileInfo = (
if ((!email || !picture) && idToken) {
try {
const decoded: Record<string, string> = jwtDecoder(idToken);
if (!email && decoded.email) {
email = decoded.email;
}
@@ -133,7 +132,7 @@ export const executeRefreshTokenStrategy = async (
(
err: Error | null,
accessToken: string,
_refreshToken: string,
newRefreshToken: string,
params: any,
) => {
if (err) {
@@ -149,6 +148,7 @@ export const executeRefreshTokenStrategy = async (
resolve({
accessToken,
refreshToken: newRefreshToken,
params,
});
},
@@ -55,6 +55,7 @@ export class OAuth2AuthProvider implements OAuthProviderHandlers {
done: PassportDoneCallback<OAuthResponse, PrivateInfo>,
) => {
const profile = makeProfileInfo(rawProfile, params.id_token);
done(
undefined,
{
@@ -101,21 +102,24 @@ export class OAuth2AuthProvider implements OAuthProviderHandlers {
}
async refresh(refreshToken: string, scope: string): Promise<OAuthResponse> {
const { accessToken, params } = await executeRefreshTokenStrategy(
const refreshTokenResponse = await executeRefreshTokenStrategy(
this._strategy,
refreshToken,
scope,
);
const { accessToken, params } = refreshTokenResponse;
const updatedRefreshToken = refreshTokenResponse.refreshToken;
const profile = await executeFetchUserProfileStrategy(
this._strategy,
accessToken,
params.id_token,
refreshTokenResponse.accessToken,
refreshTokenResponse.params.id_token,
);
return this.populateIdentity({
providerInfo: {
accessToken,
refreshToken: updatedRefreshToken,
idToken: params.id_token,
expiresInSeconds: params.expires_in,
scope: params.scope,
@@ -134,7 +138,6 @@ export class OAuth2AuthProvider implements OAuthProviderHandlers {
if (!profile.email) {
throw new Error('Profile does not contain a profile');
}
const id = profile.email.split('@')[0];
return { ...response, backstageIdentity: { id } };
@@ -259,6 +259,10 @@ export type OAuthProviderInfo = {
* Scopes granted for the access token.
*/
scope: string;
/**
* A refresh token issued for the signed in user
*/
refreshToken?: string;
};
export type OAuthPrivateInfo = {
@@ -326,6 +330,10 @@ export type RefreshTokenResponse = {
* An access token issued for the signed in user.
*/
accessToken: string;
/**
* Optionally, the server can issue a new Refresh Token for the user
*/
refreshToken?: string;
params: any;
};