Skip to content
Merged
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
76 changes: 59 additions & 17 deletions packages/browser-tests/cypress/integration/enterprise/oidc.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe("OIDC authentication", () => {
cy.logout();
});

it("should go to Login page if token expired, and there is no refresh token", () => {
it("should request a new token on page reload, even if there is no refresh token", () => {
interceptAuthorizationCodeRequest(`${baseUrl}?code=abcdefgh`);
cy.getByDataHook("button-sso-login").click();
cy.wait("@authorizationCode");
Expand All @@ -98,11 +98,6 @@ describe("OIDC authentication", () => {
cy.getEditor().should("be.visible");

cy.reload();
cy.getByDataHook("button-sso-continue").should("be.visible");
cy.getByDataHook("button-sso-login").should("be.visible");
cy.getByDataHook("button-sso-login").contains("Choose a different account");

cy.getByDataHook("button-sso-continue").click()
cy.getEditor().should("be.visible");
});

Expand Down Expand Up @@ -208,17 +203,19 @@ describe("OIDC authentication - with state", () => {

// load login page
interceptSettings({
"release.type": "EE",
"release.version": "1.2.3",
"acl.enabled": true,
"acl.basic.auth.realm.enabled": false,
"acl.oidc.enabled": true,
"acl.oidc.client.id": "client1",
"acl.oidc.authorization.endpoint": oidcAuthorizationCodeUrl,
"acl.oidc.token.endpoint": oidcTokenUrl,
"acl.oidc.pkce.required": true,
"acl.oidc.state.required": true,
"acl.oidc.groups.encoded.in.token": false,
"config": {
"release.type": "EE",
"release.version": "1.2.3",
"acl.enabled": true,
"acl.basic.auth.realm.enabled": false,
"acl.oidc.enabled": true,
"acl.oidc.client.id": "client1",
"acl.oidc.authorization.endpoint": oidcAuthorizationCodeUrl,
"acl.oidc.token.endpoint": oidcTokenUrl,
"acl.oidc.pkce.required": true,
"acl.oidc.state.required": true,
"acl.oidc.groups.encoded.in.token": false,
}
});
cy.visit(baseUrl);

Expand Down Expand Up @@ -256,6 +253,51 @@ describe("OIDC authentication - with state", () => {
cy.getEditor().should("not.exist");
});

it("should login via OIDC, then admin, then OIDC again without re-authenticating if the OAuth2 provider session is still alive", () => {
interceptAuthorizationCodeRequest(`${baseUrl}?code=abcdefgh`);
cy.getByDataHook("button-sso-login").click();
cy.wait("@authorizationCode");

interceptTokenRequest({
"access_token": "gslpJtzmmi6RwaPSx0dYGD4tEkom",
"refresh_token": "FUuAAqMp6LSTKmkUd5uZuodhiE4Kr6M7Eyv",
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6I",
"token_type": "Bearer",
"expires_in": 300
});
cy.wait("@tokens");
cy.getEditor().should("be.visible");

cy.executeSQL("select current_user();");
cy.getGridRow(0).should("contain", "user1");

cy.logout();
cy.getByDataHook("auth-login").should("be.visible");
cy.getByDataHook("button-sso-continue").should("be.visible");
cy.getByDataHook("button-sso-login").should("be.visible");
cy.getByDataHook("button-sso-login").contains("Choose a different account");
cy.getEditor().should("not.exist");

cy.loginWithUserAndPassword();
cy.getEditorContent().should("be.visible");

cy.executeSQL("select current_user();");
cy.getGridRow(0).should("contain", "admin");

cy.logout();
cy.getByDataHook("auth-login").should("be.visible");
cy.getByDataHook("button-sso-continue").should("be.visible");
cy.getByDataHook("button-sso-login").should("be.visible");
cy.getByDataHook("button-sso-login").contains("Choose a different account");
cy.getEditorContent().should("not.exist");

cy.getByDataHook("button-sso-continue").click();
cy.getEditorContent().should("be.visible");

cy.executeSQL("select current_user();");
cy.getGridRow(0).should("contain", "user1");
});

it("should force SSO re-authentication with state error", () => {
interceptAuthorizationCodeRequest(`${baseUrl}?code=abcdefgh`, true);
cy.getByDataHook("button-sso-login").click();
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-tests/questdb
Submodule questdb updated 170 files
15 changes: 7 additions & 8 deletions packages/web-console/src/components/TopBar/toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ import { useSelector } from "react-redux"
import { IconWithTooltip } from "../IconWithTooltip"
import { hasUIAuth, setSSOUserNameWithClientID } from "../../modules/OAuth2/utils"
import { useLocalStorage } from "../../providers/LocalStorageProvider"
import { getValue } from "../../utils/localStorage"
import { StoreKey } from "../../utils/localStorage/types"
import { InstanceSettingsPopper } from "./InstanceSettingsPopper"
import { Preferences, InstanceType } from "../../utils"
import { PopperHover, Placement } from "../"
import { useTheme } from "styled-components"
import { TelemetryTable } from "../../consts";
import { TelemetryConfigShape } from "../../store/Telemetry/types";
import { sendServerInfoTelemetry } from "../../utils/telemetry";
import { TelemetryTable } from "../../consts"
import { TelemetryConfigShape } from "../../store/Telemetry/types"
import { sendServerInfoTelemetry } from "../../utils/telemetry"
import { ssoAuthState } from "../../modules/OAuth2/ssoAuthState"

const EnvIconWrapper = styled.div<{ $background?: string }>`
display: flex;
Expand Down Expand Up @@ -370,9 +369,9 @@ export const Toolbar = () => {
const currentUser = response.data[0].current_user
setCurrentUser(currentUser)

// an SSO user is logged in, update the SSO username
const authPayload = getValue(StoreKey.AUTH_PAYLOAD)
if (authPayload && currentUser && settings["acl.oidc.client.id"]) {
const ssoAuthenticated = ssoAuthState.isSSOAuthenticated()
if (ssoAuthenticated && currentUser && settings["acl.oidc.client.id"]) {
// it is an SSO user, we should update the SSO username
setSSOUserNameWithClientID(settings["acl.oidc.client.id"], currentUser)
}
return currentUser
Expand Down
5 changes: 2 additions & 3 deletions packages/web-console/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,13 @@ import {
TransitionDuration,
ToastContainer,
} from "./components"
import { actions, rootEpic, rootReducer } from "./store"
import { rootEpic, rootReducer } from "./store"
import { StoreAction, StoreShape } from "./types"

import Layout from "./scenes/Layout"
import { theme } from "./theme"
import { LocalStorageProvider } from "./providers/LocalStorageProvider"
import { PosthogProviderWrapper } from "./providers/PosthogProviderWrapper"
import { AuthProvider, QuestProvider, SettingsProvider } from "./providers"
import { AuthProvider, QuestProvider, SettingsProvider, PosthogProviderWrapper } from "./providers"

const epicMiddleware = createEpicMiddleware<
StoreAction,
Expand Down
23 changes: 23 additions & 0 deletions packages/web-console/src/modules/OAuth2/ssoAuthState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { AuthPayload } from "./types";

class SsoAuthState {
private authPayload: AuthPayload | null = null;

setAuthPayload(authPayload: AuthPayload) {
this.authPayload = authPayload
}

getAuthPayload(): AuthPayload | null {
return this.authPayload;
}

isSSOAuthenticated(): boolean {
return !!this.authPayload;
}

clearAuthPayload() {
this.authPayload = null;
}
}

export const ssoAuthState = new SsoAuthState();
2 changes: 1 addition & 1 deletion packages/web-console/src/modules/OAuth2/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ export type AuthPayload = {
refresh_token: string
token_type: string
expires_in: number
expires_at?: string
expires_at: string
groups_encoded_in_token?: boolean
}
8 changes: 4 additions & 4 deletions packages/web-console/src/modules/OAuth2/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ export const getAuthorisationURL = ({
settings,
code_challenge = null,
state = null,
loginWithDifferentAccount,
prompt,
redirect_uri,
}: {
settings: Settings
code_challenge: string | null
state: string | null
loginWithDifferentAccount?: boolean
prompt?: "login" | "none"
redirect_uri: string
}) => {
const params = {
Expand All @@ -50,8 +50,8 @@ export const getAuthorisationURL = ({
if (state) {
urlParams.append("state", state)
}
if (loginWithDifferentAccount) {
urlParams.append("prompt", "login")
if (prompt) {
urlParams.append("prompt", prompt)
}

return (
Expand Down
Loading