diff --git a/packages/browser-tests/cypress/integration/auth/auth.spec.js b/packages/browser-tests/cypress/integration/auth/auth.spec.js index eef68bbdc..d634985c9 100644 --- a/packages/browser-tests/cypress/integration/auth/auth.spec.js +++ b/packages/browser-tests/cypress/integration/auth/auth.spec.js @@ -32,12 +32,10 @@ describe("Auth - UI", () => { "acl.basic.auth.realm.enabled": false, "acl.oidc.enabled": false, "acl.oidc.client.id": null, - "acl.oidc.host": null, - "acl.oidc.port": null, - "acl.oidc.tls.enabled": null, "acl.oidc.authorization.endpoint": null, "acl.oidc.token.endpoint": null, "acl.oidc.pkce.required": null, + "acl.oidc.groups.encoded.in.token": false, }); cy.visit(baseUrl); }); @@ -59,12 +57,10 @@ describe("Auth - OIDC", () => { "acl.basic.auth.realm.enabled": false, "acl.oidc.enabled": true, "acl.oidc.client.id": "test", - "acl.oidc.host": "host", - "acl.oidc.port": 9999, - "acl.oidc.tls.enabled": true, - "acl.oidc.authorization.endpoint": "/auth", - "acl.oidc.token.endpoint": "/token", + "acl.oidc.authorization.endpoint": "https://host:9999/auth", + "acl.oidc.token.endpoint": "https://host:9999/token", "acl.oidc.pkce.required": true, + "acl.oidc.groups.encoded.in.token": false, }); cy.visit(baseUrl); }); @@ -86,12 +82,10 @@ describe("Auth - Basic", () => { "acl.basic.auth.realm.enabled": true, "acl.oidc.enabled": false, "acl.oidc.client.id": null, - "acl.oidc.host": null, - "acl.oidc.port": null, - "acl.oidc.tls.enabled": null, "acl.oidc.authorization.endpoint": null, "acl.oidc.token.endpoint": null, "acl.oidc.pkce.required": null, + "acl.oidc.groups.encoded.in.token": false, }); cy.visit(baseUrl); }); @@ -111,12 +105,10 @@ describe("Auth - Disabled", () => { "acl.basic.auth.realm.enabled": true, "acl.oidc.enabled": false, "acl.oidc.client.id": null, - "acl.oidc.host": null, - "acl.oidc.port": null, - "acl.oidc.tls.enabled": null, "acl.oidc.authorization.endpoint": null, "acl.oidc.token.endpoint": null, "acl.oidc.pkce.required": null, + "acl.oidc.groups.encoded.in.token": false, }); cy.visit(baseUrl); }); diff --git a/packages/browser-tests/questdb b/packages/browser-tests/questdb index 1620d78e5..ad57a7eff 160000 --- a/packages/browser-tests/questdb +++ b/packages/browser-tests/questdb @@ -1 +1 @@ -Subproject commit 1620d78e560d08db2ca8475262fd84879299fcee +Subproject commit ad57a7effc10ba1c9d59db5edd6e0ca7ffca3ebc diff --git a/packages/web-console/src/modules/OAuth2/types.ts b/packages/web-console/src/modules/OAuth2/types.ts index 6e675add8..de7f20a1a 100644 --- a/packages/web-console/src/modules/OAuth2/types.ts +++ b/packages/web-console/src/modules/OAuth2/types.ts @@ -1,7 +1,9 @@ export type AuthPayload = { access_token: string + id_token: string refresh_token: string token_type: string expires_in: number expires_at?: string + groups_encoded_in_token?: boolean } diff --git a/packages/web-console/src/modules/OAuth2/utils.ts b/packages/web-console/src/modules/OAuth2/utils.ts index 8a7836a4b..099cc23ca 100644 --- a/packages/web-console/src/modules/OAuth2/utils.ts +++ b/packages/web-console/src/modules/OAuth2/utils.ts @@ -9,27 +9,33 @@ type TokenPayload = Partial<{ refresh_token: string }> -const getBaseURL = (config: Settings) => { - return `${config["acl.oidc.tls.enabled"] ? "https" : "http"}://${ - config["acl.oidc.host"] - }:${config["acl.oidc.port"]}` +const getBaseURL = (settings: Settings) => { + // if there is no host in settings, no need to construct base URL at all + if (!settings["acl.oidc.host"]) { + return ""; + } + + // if there is host in settings, we are in legacy mode, and we should construct the base URL + return `${settings["acl.oidc.tls.enabled"] ? "https" : "http"}://${ + settings["acl.oidc.host"] + }:${settings["acl.oidc.port"]}` } export const getAuthorisationURL = ({ - config, + settings, code_challenge = null, login, redirect_uri, }: { - config: Settings + settings: Settings code_challenge: string | null login?: boolean redirect_uri: string }) => { const params = { - client_id: config["acl.oidc.client.id"] || "", + client_id: settings["acl.oidc.client.id"] || "", response_type: "code", - scope: config["acl.oidc.scope"] || "openid", + scope: settings["acl.oidc.scope"] || "openid", redirect_uri, } @@ -43,8 +49,8 @@ export const getAuthorisationURL = ({ } return ( - getBaseURL(config) + - config["acl.oidc.authorization.endpoint"] + + getBaseURL(settings) + + settings["acl.oidc.authorization.endpoint"] + "?" + urlParams ) @@ -70,5 +76,5 @@ export const getAuthToken = async ( ) } -export const hasUIAuth = (config: Settings) => - config["acl.enabled"] && !config["acl.basic.auth.realm.enabled"] +export const hasUIAuth = (settings: Settings) => + settings["acl.enabled"] && !settings["acl.basic.auth.realm.enabled"] diff --git a/packages/web-console/src/providers/AuthProvider.tsx b/packages/web-console/src/providers/AuthProvider.tsx index b6783cb16..1b1eae9e3 100644 --- a/packages/web-console/src/providers/AuthProvider.tsx +++ b/packages/web-console/src/providers/AuthProvider.tsx @@ -77,14 +77,13 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { ) const [state, dispatch] = useReducer(reducer, initialState) - const setAuthToken = (tokenResponse: AuthPayload) => { - if (tokenResponse.access_token) { + const setAuthToken = (tokenResponse: AuthPayload, settings: Settings) => { + if (tokenResponse.access_token && tokenResponse.id_token) { + tokenResponse.groups_encoded_in_token = settings["acl.oidc.groups.encoded.in.token"] + tokenResponse.expires_at = getTokenExpirationDate(tokenResponse.expires_in).toString() // convert from the sec offset setValue( StoreKey.AUTH_PAYLOAD, - JSON.stringify({ - ...tokenResponse, - expires_at: getTokenExpirationDate(tokenResponse.expires_in), // convert from the sec offset - }), + JSON.stringify(tokenResponse), ) // if the token payload does not contain the rolling refresh token, we'll keep the old one if (tokenResponse.refresh_token) { @@ -119,7 +118,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { client_id: settings["acl.oidc.client.id"], }) const tokenResponse = await response.json() - setAuthToken(tokenResponse) + setAuthToken(tokenResponse, settings) return tokenResponse } @@ -175,15 +174,15 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { // User is authenticated already if (authPayload !== "") { - const token = JSON.parse(authPayload) + const tokenResponse = JSON.parse(authPayload) // Check if the token expired or is about to in 30 seconds if ( - new Date(token.expires_at).getTime() - Date.now() < 30000 && + new Date(tokenResponse.expires_at).getTime() - Date.now() < 30000 && getValue(StoreKey.AUTH_REFRESH_TOKEN) !== "" ) { await refreshAuthToken(settings) } else { - setSessionData(token) + setSessionData(tokenResponse) } } else { // User has just been redirected back from the OAuth2 provider and has the code @@ -198,7 +197,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { redirect_uri: settings["acl.oidc.redirect.uri"] || window.location.origin + window.location.pathname, }) const tokenResponse = await response.json() - setAuthToken(tokenResponse) + setAuthToken(tokenResponse, settings) } catch (e) { throw e } @@ -253,7 +252,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { const code_verifier = generateCodeVerifier(settings) const code_challenge = generateCodeChallenge(code_verifier) window.location.href = getAuthorisationURL({ - config: settings, + settings, code_challenge, login, redirect_uri: settings["acl.oidc.redirect.uri"] || window.location.href, diff --git a/packages/web-console/src/providers/QuestProvider/index.tsx b/packages/web-console/src/providers/QuestProvider/index.tsx index ee1d1a592..8bd7f0d63 100644 --- a/packages/web-console/src/providers/QuestProvider/index.tsx +++ b/packages/web-console/src/providers/QuestProvider/index.tsx @@ -83,7 +83,7 @@ export const QuestProvider = ({ children }: PropsWithChildren) => { const setupClient = async (sessionData: Partial) => { questClient.setCommonHeaders({ - Authorization: `Bearer ${sessionData.access_token}`, + Authorization: `Bearer ${sessionData.groups_encoded_in_token ? sessionData.id_token : sessionData.access_token}`, }) questClient.refreshTokenMethod = () => { @@ -101,11 +101,11 @@ export const QuestProvider = ({ children }: PropsWithChildren) => { }, [sessionData]) useEffect(() => { - const token = getValue(StoreKey.REST_TOKEN) + const restToken = getValue(StoreKey.REST_TOKEN) // User has provided the basic auth credentials - if (token) { + if (restToken) { questClient.setCommonHeaders({ - Authorization: `Bearer ${token}`, + Authorization: `Bearer ${restToken}`, }) void finishAuthCheck() } else { diff --git a/packages/web-console/src/providers/SettingsProvider/types.ts b/packages/web-console/src/providers/SettingsProvider/types.ts index 4c78c2652..81919aed4 100644 --- a/packages/web-console/src/providers/SettingsProvider/types.ts +++ b/packages/web-console/src/providers/SettingsProvider/types.ts @@ -4,6 +4,7 @@ export type Settings = Partial<{ "release.version": string "acl.enabled": boolean "acl.basic.auth.realm.enabled": boolean + "acl.oidc.groups.encoded.in.token": boolean "acl.oidc.enabled": boolean "acl.oidc.client.id": string "acl.oidc.redirect.uri": string diff --git a/packages/web-console/src/store/Telemetry/epics.ts b/packages/web-console/src/store/Telemetry/epics.ts index f8fdf83bf..082a0be24 100644 --- a/packages/web-console/src/store/Telemetry/epics.ts +++ b/packages/web-console/src/store/Telemetry/epics.ts @@ -59,9 +59,9 @@ export const getConfig: Epic = ( ? getValue(StoreKey.AUTH_PAYLOAD) : "{}" const token = JSON.parse(authPayload) as AuthPayload - if (token.access_token) { + if (token.access_token && token.id_token) { quest.setCommonHeaders({ - Authorization: `Bearer ${token.access_token}`, + Authorization: `Bearer ${token.groups_encoded_in_token ? token.id_token : token.access_token}`, }) } else { const restToken = getValue(StoreKey.REST_TOKEN) diff --git a/packages/web-console/src/utils/questdb.ts b/packages/web-console/src/utils/questdb.ts index 7373ada41..9c16ad98a 100644 --- a/packages/web-console/src/utils/questdb.ts +++ b/packages/web-console/src/utils/questdb.ts @@ -344,10 +344,10 @@ export class Client { if (Client.numOfPendingQueries === 0) { clearInterval(interval) const newToken = await this.refreshTokenMethod() - if (newToken.access_token) { + if (newToken.access_token && newToken.id_token) { this.setCommonHeaders({ ...this.commonHeaders, - Authorization: `Bearer ${newToken.access_token}`, + Authorization: `Bearer ${newToken.groups_encoded_in_token ? newToken.id_token : newToken.access_token}`, }) } Client.refreshTokenPending = false