Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix refresh token flow in OIDC accounting for cookie split #1569

Closed
Closed
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
3 changes: 3 additions & 0 deletions server/auth/types/authentication_type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class DummyAuthType extends AuthenticationType {
isValidCookie() {
return Promise.resolve(true);
}
refreshAccessToken() {
return Promise.resolve('');
}
requestIncludesAuthInfo() {
return false;
}
Expand Down
21 changes: 17 additions & 4 deletions server/auth/types/authentication_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ export abstract class AuthenticationType implements IAuthenticationType {
cookie = undefined;
}

let newAuthHeaderValue = '';
if (cookie) {
newAuthHeaderValue = await this.refreshAccessToken(cookie, request);
}

if (!cookie || !(await this.isValidCookie(cookie, request))) {
// clear cookie
this.sessionStorageFactory.asScoped(request).clear();
Expand All @@ -160,10 +165,14 @@ export abstract class AuthenticationType implements IAuthenticationType {
}
// cookie is valid
// build auth header
const authHeadersFromCookie = this.buildAuthHeaderFromCookie(cookie!, request);
Object.assign(authHeaders, authHeadersFromCookie);
const additonalAuthHeader = await this.getAdditionalAuthHeader(request);
Object.assign(authHeaders, additonalAuthHeader);
if (!!newAuthHeaderValue) {
Object.assign(authHeaders, { authorization: newAuthHeaderValue });
} else {
const authHeadersFromCookie = this.buildAuthHeaderFromCookie(cookie!, request);
Object.assign(authHeaders, authHeadersFromCookie);
const additonalAuthHeader = await this.getAdditionalAuthHeader(request);
Object.assign(authHeaders, additonalAuthHeader);
}
}

// resolve tenant if necessary
Expand Down Expand Up @@ -277,6 +286,10 @@ export abstract class AuthenticationType implements IAuthenticationType {
cookie: SecuritySessionCookie,
request: OpenSearchDashboardsRequest
): Promise<boolean>;
public abstract refreshAccessToken(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provide a default implementation that returns empty string?

cookie: SecuritySessionCookie,
request: OpenSearchDashboardsRequest
): Promise<string>;
protected abstract handleUnauthedRequest(
request: OpenSearchDashboardsRequest,
response: LifecycleResponseFactory,
Expand Down
7 changes: 7 additions & 0 deletions server/auth/types/basic/basic_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ export class BasicAuthentication extends AuthenticationType {
);
}

async refreshAccessToken(
cookie: SecuritySessionCookie,
request: OpenSearchDashboardsRequest
): Promise<string> {
return '';
}

handleUnauthedRequest(
request: OpenSearchDashboardsRequest,
response: LifecycleResponseFactory,
Expand Down
7 changes: 7 additions & 0 deletions server/auth/types/jwt/jwt_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ export class JwtAuthentication extends AuthenticationType {
);
}

async refreshAccessToken(
cookie: SecuritySessionCookie,
request: OpenSearchDashboardsRequest
): Promise<string> {
return '';
}

handleUnauthedRequest(
request: OpenSearchDashboardsRequest,
response: LifecycleResponseFactory,
Expand Down
12 changes: 12 additions & 0 deletions server/auth/types/multiple/multi_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ export class MultipleAuthentication extends AuthenticationType {
}
}

async refreshAccessToken(
cookie: SecuritySessionCookie,
request: OpenSearchDashboardsRequest
): Promise<string> {
const reqAuthType = cookie?.authType?.toLowerCase();
if (reqAuthType && this.authHandlers.has(reqAuthType)) {
return this.authHandlers.get(reqAuthType)!.refreshAccessToken(cookie, request);
} else {
return '';
}
}

handleUnauthedRequest(
request: OpenSearchDashboardsRequest,
response: LifecycleResponseFactory,
Expand Down
29 changes: 25 additions & 4 deletions server/auth/types/openid/openid_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,27 @@ export class OpenIdAuthentication extends AuthenticationType {
return true;
}

return false;
}

async refreshAccessToken(
cookie: SecuritySessionCookie,
request: OpenSearchDashboardsRequest
): Promise<string> {
if (
cookie.authType !== this.type ||
!cookie.username ||
!cookie.expiryTime ||
(!cookie.credentials?.authHeaderValue && !this.getExtraAuthStorageValue(request, cookie)) ||
!cookie.credentials?.expires_at
) {
return '';
}

if (cookie.credentials?.expires_at > Date.now()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the check on 243, drop the coalescing operator? I'm not sure what this check would resolve to if it wasn't set (for whatever crazy reason). Could you also add unit tests?

Suggested change
if (cookie.credentials?.expires_at > Date.now()) {
if (cookie.credentials.expires_at > Date.now()) {

return '';
}

// need to renew id token
if (cookie.credentials.refresh_token) {
try {
Expand Down Expand Up @@ -248,17 +269,17 @@ export class OpenIdAuthentication extends AuthenticationType {
this.getExtraAuthStorageOptions()
);

return true;
return `Bearer ${refreshTokenResponse.idToken}`;
} else {
return false;
return '';
}
} catch (error: any) {
this.logger.error(error);
return false;
return '';
}
} else {
// no refresh token, and current token is expired
return false;
return '';
}
}

Expand Down
7 changes: 7 additions & 0 deletions server/auth/types/proxy/proxy_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ export class ProxyAuthentication extends AuthenticationType {
);
}

async refreshAccessToken(
cookie: SecuritySessionCookie,
request: OpenSearchDashboardsRequest
): Promise<string> {
return '';
}

handleUnauthedRequest(
request: OpenSearchDashboardsRequest,
response: LifecycleResponseFactory,
Expand Down
7 changes: 7 additions & 0 deletions server/auth/types/saml/saml_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ export class SamlAuthentication extends AuthenticationType {
);
}

async refreshAccessToken(
cookie: SecuritySessionCookie,
request: OpenSearchDashboardsRequest
): Promise<string> {
return '';
}

handleUnauthedRequest(
request: OpenSearchDashboardsRequest,
response: LifecycleResponseFactory,
Expand Down