diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index 9f1f006f2627..4f9ea169da3d 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -200,7 +200,7 @@ jobs: uses: actions/cache@v4 with: path: web/dist - key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**') }} + key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b - name: prepare web ui if: steps.cache-web.outputs.cache-hit != 'true' working-directory: web @@ -208,6 +208,7 @@ jobs: npm ci make -C .. gen-client-ts npm run build + npm run build:sfe - name: run e2e run: | uv run coverage run manage.py test ${{ matrix.job.glob }} diff --git a/Dockerfile b/Dockerfile index 6000e0b0904a..f32cae33876d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,7 +40,8 @@ COPY ./web /work/web/ COPY ./website /work/website/ COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api -RUN npm run build +RUN npm run build && \ + npm run build:sfe # Stage 3: Build go proxy FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS go-builder diff --git a/tests/e2e/test_flows_login_sfe.py b/tests/e2e/test_flows_login_sfe.py new file mode 100644 index 000000000000..2200a57aa388 --- /dev/null +++ b/tests/e2e/test_flows_login_sfe.py @@ -0,0 +1,51 @@ +"""test default login (using SFE interface) flow""" + +from time import sleep + +from selenium.webdriver.common.by import By +from selenium.webdriver.common.keys import Keys + +from authentik.blueprints.tests import apply_blueprint +from tests.e2e.utils import SeleniumTestCase, retry + + +class TestFlowsLoginSFE(SeleniumTestCase): + """test default login flow""" + + def login(self): + """Do entire login flow adjusted for SFE""" + flow_executor = self.driver.find_element(By.ID, "flow-sfe-container") + identification_stage = flow_executor.find_element(By.ID, "ident-form") + + identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").click() + identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys( + self.user.username + ) + identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys( + Keys.ENTER + ) + + password_stage = flow_executor.find_element(By.ID, "password-form") + password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys( + self.user.username + ) + password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(Keys.ENTER) + sleep(1) + + @retry() + @apply_blueprint( + "default/flow-default-authentication-flow.yaml", + "default/flow-default-invalidation-flow.yaml", + ) + def test_login(self): + """test default login flow""" + self.driver.get( + self.url( + "authentik_core:if-flow", + flow_slug="default-authentication-flow", + query={"sfe": True}, + ) + ) + self.login() + self.wait_for_url(self.if_user_url("/library")) + self.assert_user(self.user) diff --git a/tests/e2e/utils.py b/tests/e2e/utils.py index 88d9ec867d99..ce5c3480c8b3 100644 --- a/tests/e2e/utils.py +++ b/tests/e2e/utils.py @@ -241,7 +241,7 @@ def get_shadow_root( return element def login(self): - """Do entire login flow and check user afterwards""" + """Do entire login flow""" flow_executor = self.get_shadow_root("ak-flow-executor") identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor) diff --git a/web/packages/sfe/src/index.ts b/web/packages/sfe/src/index.ts index a4cc79b62f1f..6d6372fe1188 100644 --- a/web/packages/sfe/src/index.ts +++ b/web/packages/sfe/src/index.ts @@ -210,6 +210,9 @@ class PasswordStage extends Stage {

${this.challenge?.flowInfo?.title}

+
+ +
0 ? IS_INVALID : ""}" name="password" placeholder="Password"> ${this.renderInputError("password")}