Compare commits
3 Commits
sdko/captc
...
issue-2199
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f959d14e00 | ||
|
|
52538a5961 | ||
|
|
47349e6354 |
2
.github/actions/setup/action.yml
vendored
@@ -64,7 +64,7 @@ runs:
|
||||
rustflags: ""
|
||||
- name: Setup rust dependencies
|
||||
if: ${{ contains(inputs.dependencies, 'rust') }}
|
||||
uses: taiki-e/install-action@c070f87102a1c75b3183910f391c1cb887fe13c8 # v2
|
||||
uses: taiki-e/install-action@ec28e287910af896fd98e04056d31fa68607e7ad # v2
|
||||
with:
|
||||
tool: cargo-deny cargo-machete cargo-llvm-cov nextest
|
||||
- name: Setup node (root, web)
|
||||
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
with:
|
||||
working-directory: web
|
||||
dependencies: "monorepo"
|
||||
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
||||
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
- name: Generate API Clients
|
||||
|
||||
254
.github/workflows/ci-web.yml
vendored
@@ -12,6 +12,20 @@ on:
|
||||
- main
|
||||
- version-*
|
||||
|
||||
env:
|
||||
POSTGRES_DB: authentik
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
AUTHENTIK_BLUEPRINTS_DIR: "./blueprints"
|
||||
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST: "true"
|
||||
# Drives the system/bootstrap.yaml blueprint at startup: creates akadmin with
|
||||
# these credentials and flips the Setup flag (Setup.set(True)) so the SPA's
|
||||
# post-login redirect to "/" doesn't bounce through /setup, which would 500
|
||||
# because the OOBE policy refuses to run once akadmin already has a usable
|
||||
# password. See authentik/core/setup/signals.py and blueprints/default/flow-oobe.yaml.
|
||||
AUTHENTIK_BOOTSTRAP_EMAIL: "test-admin@goauthentik.io"
|
||||
AUTHENTIK_BOOTSTRAP_PASSWORD: "test-runner"
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -43,6 +57,246 @@ jobs:
|
||||
NODE_ENV: "production"
|
||||
working-directory: web/
|
||||
run: corepack npm run build
|
||||
e2e:
|
||||
name: e2e (playwright)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: read
|
||||
# Required so the "Comment Playwright result on PR" step can update its
|
||||
# marker comment via the gh CLI / REST API.
|
||||
pull-requests: write
|
||||
# Required so the optional "Upload HTML report to S3" step can mint OIDC
|
||||
# credentials with aws-actions/configure-aws-credentials. Harmless when
|
||||
# the upload is gated off.
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
dependencies: system,python,node,go,rust,runtime
|
||||
- name: Build web UI
|
||||
run: corepack npm run --prefix web build
|
||||
- name: Build authentik server (Go)
|
||||
run: | # shell
|
||||
go build -o ./bin/authentik-server ./cmd/server
|
||||
sudo install -m 0755 ./bin/authentik-server /usr/local/bin/authentik-server
|
||||
# The Rust binary is named `authentik` (not `authentik-worker`) to match
|
||||
# `lifecycle/ak`, which probes `command -v authentik` and falls back to
|
||||
# `cargo run --` if the prebuilt binary isn't on PATH. It serves both
|
||||
# `ak worker` and `ak allinone`.
|
||||
- name: Build authentik Rust binary (worker / allinone)
|
||||
run: | # shell
|
||||
cargo build --release --bin authentik
|
||||
sudo install -m 0755 ./target/release/authentik /usr/local/bin/authentik
|
||||
- name: Apply migrations
|
||||
run: uv run python -m lifecycle.migrate
|
||||
- name: Resolve Playwright version
|
||||
id: playwright-version
|
||||
working-directory: web
|
||||
run: | # shell
|
||||
version=$(node -p "require('@playwright/test/package.json').version")
|
||||
if [ -z "$version" ]; then
|
||||
echo "Failed to resolve @playwright/test version" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
||||
- name: Cache Playwright browsers
|
||||
id: playwright-cache
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v4
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }}
|
||||
- name: Install Playwright browsers
|
||||
working-directory: web
|
||||
run: | # shell
|
||||
if [ "${{ steps.playwright-cache.outputs.cache-hit }}" = "true" ]; then
|
||||
corepack npm exec -- playwright install-deps chromium
|
||||
else
|
||||
corepack npm exec -- playwright install --with-deps chromium
|
||||
fi
|
||||
- name: Start authentik server and worker
|
||||
run: | # shell
|
||||
set -euo pipefail
|
||||
mkdir -p /tmp/ak-logs
|
||||
|
||||
# The Go server (authentik-server) spawns gunicorn as a child via PATH lookup
|
||||
# and inherits the env that `uv run` set up for it. Verify gunicorn resolves
|
||||
# under the same launcher so we fail fast here instead of waiting for an
|
||||
# empty 200 from the proxy fallback later.
|
||||
uv run --frozen sh -c 'command -v gunicorn' \
|
||||
|| { echo "gunicorn not resolvable from uv run"; exit 1; }
|
||||
|
||||
uv run ak server > /tmp/ak-logs/server.log 2>&1 &
|
||||
echo $! > /tmp/ak-logs/server.pid
|
||||
|
||||
# The Rust worker also opens an HTTP/metrics server on listen.http /
|
||||
# listen.metrics (default :9000 / :9300). On a single CI host that races the
|
||||
# Go server's binds and silently steals :9000, leaving Playwright talking to
|
||||
# a healthcheck-only axum router that returns 200/empty for /if/* paths.
|
||||
# Pin the worker to disjoint ports so the Go server keeps the public 9000.
|
||||
AUTHENTIK_LISTEN__HTTP="[::]:9001" \
|
||||
AUTHENTIK_LISTEN__METRICS="[::]:9301" \
|
||||
uv run ak worker > /tmp/ak-logs/worker.log 2>&1 &
|
||||
echo $! > /tmp/ak-logs/worker.pid
|
||||
- name: Wait for authentik to be ready
|
||||
run: | # shell
|
||||
set -euo pipefail
|
||||
# Readiness probes must verify the Go server is actually serving the request,
|
||||
# not just that *something* on :9000 returned 200. The Go proxy stamps
|
||||
# `X-authentik-version` on its static responses and the rendered flow page
|
||||
# contains the <ak-flow-executor> custom element — both are absent from the
|
||||
# worker's axum healthcheck router, so checking either rules out the
|
||||
# port-collision failure mode.
|
||||
timeout 240 bash -c '
|
||||
until curl -fsS -o /dev/null http://localhost:9000/-/health/ready/; do
|
||||
sleep 2
|
||||
done'
|
||||
timeout 300 bash -c '
|
||||
until curl -fsS http://localhost:9000/if/flow/default-authentication-flow/ \
|
||||
| grep -q "ak-flow-executor"; do
|
||||
sleep 3
|
||||
done'
|
||||
- name: Run Playwright tests
|
||||
working-directory: web
|
||||
env:
|
||||
AK_TEST_RUNNER_PAGE_URL: http://localhost:9000
|
||||
run: corepack npm run test:e2e
|
||||
# Reporting / upload steps below intentionally use `!cancelled()` rather
|
||||
# than `failure()`: a cancelled run (e.g. superseded by a newer push) is
|
||||
# not a real result and shouldn't produce reviewer-facing artifacts or
|
||||
# comments. `if-no-files-found: ignore` keeps the "passed" case quiet.
|
||||
- name: Upload Playwright HTML report
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: playwright-report
|
||||
path: web/playwright-report/
|
||||
retention-days: 14
|
||||
if-no-files-found: ignore
|
||||
- name: Upload Playwright traces and videos
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: playwright-traces
|
||||
path: web/test-results/
|
||||
retention-days: 14
|
||||
if-no-files-found: ignore
|
||||
- name: Upload authentik server and worker logs
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: authentik-logs
|
||||
path: /tmp/ak-logs/
|
||||
retention-days: 14
|
||||
if-no-files-found: ignore
|
||||
- name: Parse Playwright results
|
||||
id: playwright-results
|
||||
if: ${{ !cancelled() }}
|
||||
run: | # shell
|
||||
set -euo pipefail
|
||||
report=web/playwright-report/results.json
|
||||
if [ ! -f "$report" ]; then
|
||||
{
|
||||
echo "available=false"
|
||||
echo "passed=0"
|
||||
echo "failed=0"
|
||||
echo "flaky=0"
|
||||
echo "skipped=0"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
{
|
||||
echo "available=true"
|
||||
echo "passed=$(jq -r '.stats.expected // 0' "$report")"
|
||||
echo "failed=$(jq -r '.stats.unexpected // 0' "$report")"
|
||||
echo "flaky=$(jq -r '.stats.flaky // 0' "$report")"
|
||||
echo "skipped=$(jq -r '.stats.skipped // 0' "$report")"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
# The S3 publishing pair below is intentionally gated off until the
|
||||
# `authentik-playwright-artifacts` bucket is provisioned by infra. Flip
|
||||
# the repo variable `PLAYWRIGHT_S3_ENABLED=true` to turn it on; the URL
|
||||
# baked into the PR comment below already points at the eventual key.
|
||||
- name: Configure AWS credentials for HTML report upload
|
||||
if: ${{ !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && vars.PLAYWRIGHT_S3_ENABLED == 'true' }}
|
||||
uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0
|
||||
with:
|
||||
role-to-assume: "arn:aws:iam::016170277896:role/github_goauthentik_authentik"
|
||||
aws-region: eu-central-1
|
||||
- name: Upload HTML report to S3
|
||||
if: ${{ !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && vars.PLAYWRIGHT_S3_ENABLED == 'true' }}
|
||||
env:
|
||||
S3_BUCKET: authentik-playwright-artifacts
|
||||
S3_KEY_PREFIX: pr-${{ github.event.pull_request.number }}/run-${{ github.run_id }}/attempt-${{ github.run_attempt }}
|
||||
run: | # shell
|
||||
set -euo pipefail
|
||||
if [ ! -d web/playwright-report ]; then
|
||||
echo "No playwright-report/ produced; skipping S3 upload"
|
||||
exit 0
|
||||
fi
|
||||
# Reports are stamped with run_id + attempt in S3_KEY_PREFIX, so a given
|
||||
# key is immutable once written — safe to cache aggressively. 30 days
|
||||
# matches the retention-days on the GHA artifact upload above.
|
||||
aws s3 cp \
|
||||
--recursive \
|
||||
--acl=public-read \
|
||||
--cache-control "public, max-age=2592000, immutable" \
|
||||
web/playwright-report/ \
|
||||
"s3://${S3_BUCKET}/${S3_KEY_PREFIX}/"
|
||||
# Same-repo guard: fork PRs run with a read-only GITHUB_TOKEN (even after
|
||||
# maintainer approval), so the comment + edit calls would 403. Skip cleanly
|
||||
# rather than failing the job on every fork PR run.
|
||||
- name: Comment Playwright result on PR
|
||||
if: ${{ !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
AVAILABLE: ${{ steps.playwright-results.outputs.available }}
|
||||
PASSED: ${{ steps.playwright-results.outputs.passed }}
|
||||
FAILED: ${{ steps.playwright-results.outputs.failed }}
|
||||
FLAKY: ${{ steps.playwright-results.outputs.flaky }}
|
||||
SKIPPED: ${{ steps.playwright-results.outputs.skipped }}
|
||||
S3_ENABLED: ${{ vars.PLAYWRIGHT_S3_ENABLED }}
|
||||
REPORT_URL: https://authentik-playwright-artifacts.s3.amazonaws.com/pr-${{ github.event.pull_request.number }}/run-${{ github.run_id }}/attempt-${{ github.run_attempt }}/index.html
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/attempts/${{ github.run_attempt }}
|
||||
run: | # shell
|
||||
set -euo pipefail
|
||||
marker='<!-- playwright-result -->'
|
||||
|
||||
if [ "$AVAILABLE" = "true" ]; then
|
||||
if [ "$FAILED" -gt 0 ]; then
|
||||
status='❌ Failed'
|
||||
elif [ "$FLAKY" -gt 0 ]; then
|
||||
status='⚠️ Passed with flakes'
|
||||
else
|
||||
status='✅ Passed'
|
||||
fi
|
||||
stats=$(printf '| Result | Count |\n|---|---|\n| ✅ Passed | %s |\n| ❌ Failed | %s |\n| ⚠️ Flaky | %s |\n| ⏭️ Skipped | %s |\n' "$PASSED" "$FAILED" "$FLAKY" "$SKIPPED")
|
||||
else
|
||||
status='⚠️ No results produced'
|
||||
stats='The job did not produce `playwright-report/results.json`. The suite likely crashed before the JSON reporter wrote its output — see the workflow run for setup-step failures.'
|
||||
fi
|
||||
|
||||
if [ "$S3_ENABLED" = "true" ]; then
|
||||
report_line=$(printf '[HTML report](%s) · [Workflow run](%s)' "$REPORT_URL" "$RUN_URL")
|
||||
else
|
||||
report_line=$(printf '[Workflow run](%s) · _HTML report hosting is gated off until the `authentik-playwright-artifacts` S3 bucket is provisioned (`vars.PLAYWRIGHT_S3_ENABLED`). Until then, download the `playwright-report` artifact from the run page._' "$RUN_URL")
|
||||
fi
|
||||
|
||||
body=$(printf '%s\n## Playwright e2e — %s\n\n%s\n\n%s\n' "$marker" "$status" "$stats" "$report_line")
|
||||
|
||||
existing=$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \
|
||||
--paginate \
|
||||
--jq "[.[] | select(.body != null and (.body | startswith(\"$marker\")))] | .[0].id // empty")
|
||||
|
||||
if [ -n "$existing" ]; then
|
||||
gh api -X PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${existing}" -f body="$body" > /dev/null
|
||||
echo "Updated existing comment ${existing}"
|
||||
else
|
||||
gh api -X POST "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" -f body="$body" > /dev/null
|
||||
echo "Created new playwright-result comment"
|
||||
fi
|
||||
ci-web-mark:
|
||||
if: always()
|
||||
needs:
|
||||
|
||||
8
Cargo.lock
generated
@@ -288,9 +288,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-rs"
|
||||
version = "1.17.0"
|
||||
version = "1.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00"
|
||||
checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f"
|
||||
dependencies = [
|
||||
"aws-lc-fips-sys",
|
||||
"aws-lc-sys",
|
||||
@@ -300,9 +300,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-sys"
|
||||
version = "0.41.0"
|
||||
version = "0.40.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4"
|
||||
checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cmake",
|
||||
|
||||
@@ -22,7 +22,7 @@ publish = false
|
||||
arc-swap = "= 1.9.1"
|
||||
argh = "= 0.1.19"
|
||||
axum-server = { version = "= 0.8.0", features = ["tls-rustls-no-provider"] }
|
||||
aws-lc-rs = { version = "= 1.17.0", features = ["fips"] }
|
||||
aws-lc-rs = { version = "= 1.16.3", features = ["fips"] }
|
||||
axum = { version = "= 0.8.9", features = ["http2", "macros", "ws"] }
|
||||
clap = { version = "= 4.6.1", features = ["derive", "env"] }
|
||||
client-ip = { version = "0.2.1", features = ["forwarded-header"] }
|
||||
|
||||
@@ -128,7 +128,6 @@ class SessionEndChallenge(WithUserInfoChallenge):
|
||||
application_launch_url = CharField(required=False)
|
||||
|
||||
invalidation_flow_url = CharField(required=False)
|
||||
overview_url = CharField(required=False)
|
||||
brand_name = CharField(required=True)
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ from sentry_sdk import start_span
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
|
||||
from authentik.common.oauth.constants import PLAN_CONTEXT_POST_LOGOUT_REDIRECT_URI
|
||||
from authentik.core.models import Application, User, UserTypes
|
||||
from authentik.core.models import Application, User
|
||||
from authentik.flows.challenge import (
|
||||
AccessDeniedChallenge,
|
||||
Challenge,
|
||||
@@ -331,10 +331,6 @@ class SessionEndStage(ChallengeStageView):
|
||||
"component": "ak-stage-session-end",
|
||||
"brand_name": self.request.brand.branding_title,
|
||||
}
|
||||
if self.get_pending_user().type == UserTypes.INTERNAL:
|
||||
data["overview_url"] = self.request.build_absolute_uri(
|
||||
reverse("authentik_core:root-redirect")
|
||||
)
|
||||
if application:
|
||||
data["application_name"] = application.name
|
||||
data["application_launch_url"] = application.get_launch_url(self.get_pending_user())
|
||||
|
||||
@@ -17,7 +17,6 @@ class CaptchaStageSerializer(StageSerializer):
|
||||
"private_key",
|
||||
"js_url",
|
||||
"api_url",
|
||||
"request_content_type",
|
||||
"interactive",
|
||||
"score_min_threshold",
|
||||
"score_max_threshold",
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# Generated by Django 5.2.14 on 2026-05-14 23:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_stages_captcha", "0004_captchastage_interactive"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="captchastage",
|
||||
name="request_content_type",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
("application/x-www-form-urlencoded", "Form encoded"),
|
||||
("application/json", "JSON"),
|
||||
],
|
||||
default="application/x-www-form-urlencoded",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -8,13 +8,6 @@ from rest_framework.serializers import BaseSerializer
|
||||
from authentik.flows.models import Stage
|
||||
|
||||
|
||||
class CaptchaRequestContentType(models.TextChoices):
|
||||
"""Supported request content types for CAPTCHA verification."""
|
||||
|
||||
FORM = "application/x-www-form-urlencoded", _("Form encoded")
|
||||
JSON = "application/json", _("JSON")
|
||||
|
||||
|
||||
class CaptchaStage(Stage):
|
||||
"""Verify the user is human using Google's reCaptcha/other compatible CAPTCHA solutions."""
|
||||
|
||||
@@ -37,10 +30,6 @@ class CaptchaStage(Stage):
|
||||
|
||||
js_url = models.TextField(default="https://www.recaptcha.net/recaptcha/api.js")
|
||||
api_url = models.TextField(default="https://www.recaptcha.net/recaptcha/api/siteverify")
|
||||
request_content_type = models.TextField(
|
||||
choices=CaptchaRequestContentType.choices,
|
||||
default=CaptchaRequestContentType.FORM,
|
||||
)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[BaseSerializer]:
|
||||
|
||||
@@ -15,7 +15,7 @@ from authentik.flows.challenge import (
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.lib.utils.http import get_http_session
|
||||
from authentik.root.middleware import ClientIPMiddleware
|
||||
from authentik.stages.captcha.models import CaptchaRequestContentType, CaptchaStage
|
||||
from authentik.stages.captcha.models import CaptchaStage
|
||||
|
||||
LOGGER = get_logger()
|
||||
PLAN_CONTEXT_CAPTCHA = "captcha"
|
||||
@@ -35,23 +35,17 @@ class CaptchaChallenge(WithUserInfoChallenge):
|
||||
|
||||
def verify_captcha_token(stage: CaptchaStage, token: str, remote_ip: str, key: str | None = None):
|
||||
"""Validate captcha token"""
|
||||
payload = {
|
||||
"secret": key or stage.private_key,
|
||||
"response": token,
|
||||
"remoteip": remote_ip,
|
||||
}
|
||||
body_kwargs = (
|
||||
{"json": payload}
|
||||
if stage.request_content_type == CaptchaRequestContentType.JSON
|
||||
else {"data": payload}
|
||||
)
|
||||
try:
|
||||
response = get_http_session().post(
|
||||
stage.api_url,
|
||||
headers={
|
||||
"Content-Type": stage.request_content_type,
|
||||
"Content-type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
data={
|
||||
"secret": key or stage.private_key,
|
||||
"response": token,
|
||||
"remoteip": remote_ip,
|
||||
},
|
||||
**body_kwargs,
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
@@ -10,7 +10,7 @@ from authentik.flows.planner import FlowPlan
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.stages.captcha.models import CaptchaRequestContentType, CaptchaStage
|
||||
from authentik.stages.captcha.models import CaptchaStage
|
||||
from authentik.stages.captcha.stage import (
|
||||
PLAN_CONTEXT_CAPTCHA_PRIVATE_KEY,
|
||||
PLAN_CONTEXT_CAPTCHA_SITE_KEY,
|
||||
@@ -56,39 +56,6 @@ class TestCaptchaStage(FlowTestCase):
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||
self.assertEqual(
|
||||
mock.request_history[0].headers["Content-Type"],
|
||||
CaptchaRequestContentType.FORM,
|
||||
)
|
||||
self.assertIn("response=PASSED", mock.request_history[0].text)
|
||||
|
||||
@Mocker()
|
||||
def test_valid_json_content_type(self, mock: Mocker):
|
||||
"""Test valid captcha with JSON verification request"""
|
||||
self.stage.request_content_type = CaptchaRequestContentType.JSON
|
||||
self.stage.save()
|
||||
mock.post(
|
||||
"https://www.recaptcha.net/recaptcha/api/siteverify",
|
||||
json={
|
||||
"success": True,
|
||||
"score": 0.5,
|
||||
},
|
||||
)
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
{"token": "PASSED"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||
self.assertEqual(
|
||||
mock.request_history[0].headers["Content-Type"],
|
||||
CaptchaRequestContentType.JSON,
|
||||
)
|
||||
self.assertEqual(mock.request_history[0].json()["response"], "PASSED")
|
||||
|
||||
@Mocker()
|
||||
def test_valid_override(self, mock: Mocker):
|
||||
|
||||
@@ -4,7 +4,6 @@ from email.mime.image import MIMEImage
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.core.mail.message import sanitize_address
|
||||
from django.template.exceptions import TemplateDoesNotExist
|
||||
@@ -15,10 +14,9 @@ from django.utils import translation
|
||||
@lru_cache
|
||||
def logo_data() -> MIMEImage:
|
||||
"""Get logo as MIME Image for emails"""
|
||||
path = Path("web/dist/assets/icons/icon_left_brand.png")
|
||||
# When running tests, assets might not exist, so fallback to a different icon
|
||||
if settings.TEST:
|
||||
path = Path("web/authentik/sources/saml.png")
|
||||
path = Path("web/icons/icon_left_brand.png")
|
||||
if not path.exists():
|
||||
path = Path("web/dist/assets/icons/icon_left_brand.png")
|
||||
with open(path, "rb") as _logo_file:
|
||||
logo = MIMEImage(_logo_file.read())
|
||||
logo.add_header("Content-ID", "<logo>")
|
||||
|
||||
@@ -15160,14 +15160,6 @@
|
||||
"minLength": 1,
|
||||
"title": "Api url"
|
||||
},
|
||||
"request_content_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"application/x-www-form-urlencoded",
|
||||
"application/json"
|
||||
],
|
||||
"title": "Request content type"
|
||||
},
|
||||
"interactive": {
|
||||
"type": "boolean",
|
||||
"title": "Interactive"
|
||||
|
||||
6
go.mod
@@ -18,7 +18,7 @@ require (
|
||||
github.com/gorilla/securecookie v1.1.2
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/grafana/pyroscope-go v1.3.0
|
||||
github.com/grafana/pyroscope-go v1.2.8
|
||||
github.com/jackc/pgx/v5 v5.9.2
|
||||
github.com/jellydator/ttlcache/v3 v3.4.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
@@ -69,14 +69,14 @@ require (
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
|
||||
github.com/go-openapi/validate v0.25.2 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.10 // indirect
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/klauspost/compress v1.18.6 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oklog/ulid/v2 v2.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
||||
12
go.sum
@@ -105,10 +105,10 @@ github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2e
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/pyroscope-go v1.3.0 h1:t3Jehad8vvqN4oRAB0LdmfQ5ZSUXQw3asoft+K4GAT8=
|
||||
github.com/grafana/pyroscope-go v1.3.0/go.mod h1:XA7I3usNx+UdjOZfQnl1WV8y924vsJo9KIVrKB+9jx4=
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.10 h1:dvhndEbyavTb59vFCd6PsrAG5qi69/qZZtegh/TJKSY=
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.10/go.mod h1:XnWRGg2XO5uxZdiz1rfeJH6w1eZ+YICCBVXNWOfH86g=
|
||||
github.com/grafana/pyroscope-go v1.2.8 h1:UvCwIhlx9DeV7F6TW/z8q1Mi4PIm3vuUJ2ZlCEvmA4M=
|
||||
github.com/grafana/pyroscope-go v1.2.8/go.mod h1:SSi59eQ1/zmKoY/BKwa5rSFsJaq+242Bcrr4wPix1g8=
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og=
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
@@ -139,8 +139,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
|
||||
10
lifecycle/aws/package-lock.json
generated
@@ -9,12 +9,12 @@
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1121.0",
|
||||
"aws-cdk": "^2.1120.0",
|
||||
"cross-env": "^10.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.14.1"
|
||||
"npm": ">=11.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@epic-web/invariant": {
|
||||
@@ -25,9 +25,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-cdk": {
|
||||
"version": "2.1121.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1121.0.tgz",
|
||||
"integrity": "sha512-cG7CHt/SytYTfwrK+BUNQpqmS1dwhjt8z6ExKL6GK4n+8/6ZCwFzxlZWA/jUd2+Y9xPc+Q8cLKfMqGmgxEXbkg==",
|
||||
"version": "2.1120.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1120.0.tgz",
|
||||
"integrity": "sha512-vDVa0IX0FhizARdY/GLSParFglKbdHCIhM8IDmynrAv9w8uLLljzWMeLUOhC1XpMErDZ/npYEihAOjfKxTaMIw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"aws-cfn": "cross-env CI=false cdk synth --version-reporting=false > template.yaml"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1121.0",
|
||||
"aws-cdk": "^2.1120.0",
|
||||
"cross-env": "^10.1.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-05-14 00:33+0000\n"
|
||||
"POT-Creation-Date: 2026-05-13 05:39+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -2801,11 +2801,7 @@ msgid "SAML Sessions"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "OAuth (Silent)"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "OAuth (interactive)"
|
||||
msgid "OAuth"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
|
||||
37
packages/client-go/model_session_end_challenge.go
generated
@@ -29,7 +29,6 @@ type SessionEndChallenge struct {
|
||||
ApplicationName *string `json:"application_name,omitempty"`
|
||||
ApplicationLaunchUrl *string `json:"application_launch_url,omitempty"`
|
||||
InvalidationFlowUrl *string `json:"invalidation_flow_url,omitempty"`
|
||||
OverviewUrl *string `json:"overview_url,omitempty"`
|
||||
BrandName string `json:"brand_name"`
|
||||
AdditionalProperties map[string]interface{}
|
||||
}
|
||||
@@ -300,38 +299,6 @@ func (o *SessionEndChallenge) SetInvalidationFlowUrl(v string) {
|
||||
o.InvalidationFlowUrl = &v
|
||||
}
|
||||
|
||||
// GetOverviewUrl returns the OverviewUrl field value if set, zero value otherwise.
|
||||
func (o *SessionEndChallenge) GetOverviewUrl() string {
|
||||
if o == nil || IsNil(o.OverviewUrl) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.OverviewUrl
|
||||
}
|
||||
|
||||
// GetOverviewUrlOk returns a tuple with the OverviewUrl field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *SessionEndChallenge) GetOverviewUrlOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.OverviewUrl) {
|
||||
return nil, false
|
||||
}
|
||||
return o.OverviewUrl, true
|
||||
}
|
||||
|
||||
// HasOverviewUrl returns a boolean if a field has been set.
|
||||
func (o *SessionEndChallenge) HasOverviewUrl() bool {
|
||||
if o != nil && !IsNil(o.OverviewUrl) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetOverviewUrl gets a reference to the given string and assigns it to the OverviewUrl field.
|
||||
func (o *SessionEndChallenge) SetOverviewUrl(v string) {
|
||||
o.OverviewUrl = &v
|
||||
}
|
||||
|
||||
// GetBrandName returns the BrandName field value
|
||||
func (o *SessionEndChallenge) GetBrandName() string {
|
||||
if o == nil {
|
||||
@@ -386,9 +353,6 @@ func (o SessionEndChallenge) ToMap() (map[string]interface{}, error) {
|
||||
if !IsNil(o.InvalidationFlowUrl) {
|
||||
toSerialize["invalidation_flow_url"] = o.InvalidationFlowUrl
|
||||
}
|
||||
if !IsNil(o.OverviewUrl) {
|
||||
toSerialize["overview_url"] = o.OverviewUrl
|
||||
}
|
||||
toSerialize["brand_name"] = o.BrandName
|
||||
|
||||
for key, value := range o.AdditionalProperties {
|
||||
@@ -443,7 +407,6 @@ func (o *SessionEndChallenge) UnmarshalJSON(data []byte) (err error) {
|
||||
delete(additionalProperties, "application_name")
|
||||
delete(additionalProperties, "application_launch_url")
|
||||
delete(additionalProperties, "invalidation_flow_url")
|
||||
delete(additionalProperties, "overview_url")
|
||||
delete(additionalProperties, "brand_name")
|
||||
o.AdditionalProperties = additionalProperties
|
||||
}
|
||||
|
||||
@@ -35,8 +35,6 @@ pub struct SessionEndChallenge {
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub invalidation_flow_url: Option<String>,
|
||||
#[serde(rename = "overview_url", skip_serializing_if = "Option::is_none")]
|
||||
pub overview_url: Option<String>,
|
||||
#[serde(rename = "brand_name")]
|
||||
pub brand_name: String,
|
||||
}
|
||||
@@ -57,7 +55,6 @@ impl SessionEndChallenge {
|
||||
application_name: None,
|
||||
application_launch_url: None,
|
||||
invalidation_flow_url: None,
|
||||
overview_url: None,
|
||||
brand_name,
|
||||
}
|
||||
}
|
||||
|
||||
16
packages/client-ts/src/models/CaptchaStage.ts
generated
@@ -14,11 +14,6 @@
|
||||
|
||||
import type { FlowSet } from "./FlowSet";
|
||||
import { FlowSetFromJSON } from "./FlowSet";
|
||||
import type { RequestContentTypeEnum } from "./RequestContentTypeEnum";
|
||||
import {
|
||||
RequestContentTypeEnumFromJSON,
|
||||
RequestContentTypeEnumToJSON,
|
||||
} from "./RequestContentTypeEnum";
|
||||
|
||||
/**
|
||||
* CaptchaStage Serializer
|
||||
@@ -86,12 +81,6 @@ export interface CaptchaStage {
|
||||
* @memberof CaptchaStage
|
||||
*/
|
||||
apiUrl?: string;
|
||||
/**
|
||||
*
|
||||
* @type {RequestContentTypeEnum}
|
||||
* @memberof CaptchaStage
|
||||
*/
|
||||
requestContentType?: RequestContentTypeEnum;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
@@ -152,10 +141,6 @@ export function CaptchaStageFromJSONTyped(json: any, ignoreDiscriminator: boolea
|
||||
publicKey: json["public_key"],
|
||||
jsUrl: json["js_url"] == null ? undefined : json["js_url"],
|
||||
apiUrl: json["api_url"] == null ? undefined : json["api_url"],
|
||||
requestContentType:
|
||||
json["request_content_type"] == null
|
||||
? undefined
|
||||
: RequestContentTypeEnumFromJSON(json["request_content_type"]),
|
||||
interactive: json["interactive"] == null ? undefined : json["interactive"],
|
||||
scoreMinThreshold:
|
||||
json["score_min_threshold"] == null ? undefined : json["score_min_threshold"],
|
||||
@@ -186,7 +171,6 @@ export function CaptchaStageToJSONTyped(
|
||||
public_key: value["publicKey"],
|
||||
js_url: value["jsUrl"],
|
||||
api_url: value["apiUrl"],
|
||||
request_content_type: RequestContentTypeEnumToJSON(value["requestContentType"]),
|
||||
interactive: value["interactive"],
|
||||
score_min_threshold: value["scoreMinThreshold"],
|
||||
score_max_threshold: value["scoreMaxThreshold"],
|
||||
|
||||
17
packages/client-ts/src/models/CaptchaStageRequest.ts
generated
@@ -12,12 +12,6 @@
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import type { RequestContentTypeEnum } from "./RequestContentTypeEnum";
|
||||
import {
|
||||
RequestContentTypeEnumFromJSON,
|
||||
RequestContentTypeEnumToJSON,
|
||||
} from "./RequestContentTypeEnum";
|
||||
|
||||
/**
|
||||
* CaptchaStage Serializer
|
||||
* @export
|
||||
@@ -54,12 +48,6 @@ export interface CaptchaStageRequest {
|
||||
* @memberof CaptchaStageRequest
|
||||
*/
|
||||
apiUrl?: string;
|
||||
/**
|
||||
*
|
||||
* @type {RequestContentTypeEnum}
|
||||
* @memberof CaptchaStageRequest
|
||||
*/
|
||||
requestContentType?: RequestContentTypeEnum;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
@@ -113,10 +101,6 @@ export function CaptchaStageRequestFromJSONTyped(
|
||||
privateKey: json["private_key"],
|
||||
jsUrl: json["js_url"] == null ? undefined : json["js_url"],
|
||||
apiUrl: json["api_url"] == null ? undefined : json["api_url"],
|
||||
requestContentType:
|
||||
json["request_content_type"] == null
|
||||
? undefined
|
||||
: RequestContentTypeEnumFromJSON(json["request_content_type"]),
|
||||
interactive: json["interactive"] == null ? undefined : json["interactive"],
|
||||
scoreMinThreshold:
|
||||
json["score_min_threshold"] == null ? undefined : json["score_min_threshold"],
|
||||
@@ -145,7 +129,6 @@ export function CaptchaStageRequestToJSONTyped(
|
||||
private_key: value["privateKey"],
|
||||
js_url: value["jsUrl"],
|
||||
api_url: value["apiUrl"],
|
||||
request_content_type: RequestContentTypeEnumToJSON(value["requestContentType"]),
|
||||
interactive: value["interactive"],
|
||||
score_min_threshold: value["scoreMinThreshold"],
|
||||
score_max_threshold: value["scoreMaxThreshold"],
|
||||
|
||||
@@ -12,12 +12,6 @@
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import type { RequestContentTypeEnum } from "./RequestContentTypeEnum";
|
||||
import {
|
||||
RequestContentTypeEnumFromJSON,
|
||||
RequestContentTypeEnumToJSON,
|
||||
} from "./RequestContentTypeEnum";
|
||||
|
||||
/**
|
||||
* CaptchaStage Serializer
|
||||
* @export
|
||||
@@ -54,12 +48,6 @@ export interface PatchedCaptchaStageRequest {
|
||||
* @memberof PatchedCaptchaStageRequest
|
||||
*/
|
||||
apiUrl?: string;
|
||||
/**
|
||||
*
|
||||
* @type {RequestContentTypeEnum}
|
||||
* @memberof PatchedCaptchaStageRequest
|
||||
*/
|
||||
requestContentType?: RequestContentTypeEnum;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
@@ -112,10 +100,6 @@ export function PatchedCaptchaStageRequestFromJSONTyped(
|
||||
privateKey: json["private_key"] == null ? undefined : json["private_key"],
|
||||
jsUrl: json["js_url"] == null ? undefined : json["js_url"],
|
||||
apiUrl: json["api_url"] == null ? undefined : json["api_url"],
|
||||
requestContentType:
|
||||
json["request_content_type"] == null
|
||||
? undefined
|
||||
: RequestContentTypeEnumFromJSON(json["request_content_type"]),
|
||||
interactive: json["interactive"] == null ? undefined : json["interactive"],
|
||||
scoreMinThreshold:
|
||||
json["score_min_threshold"] == null ? undefined : json["score_min_threshold"],
|
||||
@@ -144,7 +128,6 @@ export function PatchedCaptchaStageRequestToJSONTyped(
|
||||
private_key: value["privateKey"],
|
||||
js_url: value["jsUrl"],
|
||||
api_url: value["apiUrl"],
|
||||
request_content_type: RequestContentTypeEnumToJSON(value["requestContentType"]),
|
||||
interactive: value["interactive"],
|
||||
score_min_threshold: value["scoreMinThreshold"],
|
||||
score_max_threshold: value["scoreMaxThreshold"],
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* authentik
|
||||
* Making authentication simple.
|
||||
*
|
||||
* The version of the OpenAPI document: 2026.8.0-rc1
|
||||
* Contact: hello@goauthentik.io
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const RequestContentTypeEnum = {
|
||||
ApplicationXWwwFormUrlencoded: "application/x-www-form-urlencoded",
|
||||
ApplicationJson: "application/json",
|
||||
UnknownDefaultOpenApi: "11184809",
|
||||
} as const;
|
||||
export type RequestContentTypeEnum =
|
||||
(typeof RequestContentTypeEnum)[keyof typeof RequestContentTypeEnum];
|
||||
|
||||
export function instanceOfRequestContentTypeEnum(value: any): boolean {
|
||||
for (const key in RequestContentTypeEnum) {
|
||||
if (Object.prototype.hasOwnProperty.call(RequestContentTypeEnum, key)) {
|
||||
if (RequestContentTypeEnum[key as keyof typeof RequestContentTypeEnum] === value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function RequestContentTypeEnumFromJSON(json: any): RequestContentTypeEnum {
|
||||
return RequestContentTypeEnumFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function RequestContentTypeEnumFromJSONTyped(
|
||||
json: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): RequestContentTypeEnum {
|
||||
return json as RequestContentTypeEnum;
|
||||
}
|
||||
|
||||
export function RequestContentTypeEnumToJSON(value?: RequestContentTypeEnum | null): any {
|
||||
return value as any;
|
||||
}
|
||||
|
||||
export function RequestContentTypeEnumToJSONTyped(
|
||||
value: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): RequestContentTypeEnum {
|
||||
return value as RequestContentTypeEnum;
|
||||
}
|
||||
@@ -70,12 +70,6 @@ export interface SessionEndChallenge {
|
||||
* @memberof SessionEndChallenge
|
||||
*/
|
||||
invalidationFlowUrl?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SessionEndChallenge
|
||||
*/
|
||||
overviewUrl?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -117,7 +111,6 @@ export function SessionEndChallengeFromJSONTyped(
|
||||
json["application_launch_url"] == null ? undefined : json["application_launch_url"],
|
||||
invalidationFlowUrl:
|
||||
json["invalidation_flow_url"] == null ? undefined : json["invalidation_flow_url"],
|
||||
overviewUrl: json["overview_url"] == null ? undefined : json["overview_url"],
|
||||
brandName: json["brand_name"],
|
||||
};
|
||||
}
|
||||
@@ -143,7 +136,6 @@ export function SessionEndChallengeToJSONTyped(
|
||||
application_name: value["applicationName"],
|
||||
application_launch_url: value["applicationLaunchUrl"],
|
||||
invalidation_flow_url: value["invalidationFlowUrl"],
|
||||
overview_url: value["overviewUrl"],
|
||||
brand_name: value["brandName"],
|
||||
};
|
||||
}
|
||||
|
||||
1
packages/client-ts/src/models/index.ts
generated
@@ -713,7 +713,6 @@ export * from "./RelatedRule";
|
||||
export * from "./Reputation";
|
||||
export * from "./ReputationPolicy";
|
||||
export * from "./ReputationPolicyRequest";
|
||||
export * from "./RequestContentTypeEnum";
|
||||
export * from "./Review";
|
||||
export * from "./ReviewRequest";
|
||||
export * from "./ReviewerGroup";
|
||||
|
||||
@@ -36,7 +36,7 @@ dependencies = [
|
||||
"fido2==2.2.0",
|
||||
"geoip2==5.2.0",
|
||||
"geopy==2.4.1",
|
||||
"google-api-python-client==2.196.0",
|
||||
"google-api-python-client==2.195.0",
|
||||
"gssapi==1.11.1",
|
||||
"gunicorn==25.3.0",
|
||||
"jsonpatch==1.33",
|
||||
@@ -76,7 +76,7 @@ dependencies = [
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"aws-cdk-lib==2.253.0",
|
||||
"aws-cdk-lib==2.252.0",
|
||||
"bandit==1.9.4",
|
||||
"black==26.3.1",
|
||||
"bpython==0.26",
|
||||
|
||||
13
schema.yml
@@ -36339,8 +36339,6 @@ components:
|
||||
type: string
|
||||
api_url:
|
||||
type: string
|
||||
request_content_type:
|
||||
$ref: '#/components/schemas/RequestContentTypeEnum'
|
||||
interactive:
|
||||
type: boolean
|
||||
score_min_threshold:
|
||||
@@ -36386,8 +36384,6 @@ components:
|
||||
api_url:
|
||||
type: string
|
||||
minLength: 1
|
||||
request_content_type:
|
||||
$ref: '#/components/schemas/RequestContentTypeEnum'
|
||||
interactive:
|
||||
type: boolean
|
||||
score_min_threshold:
|
||||
@@ -48291,8 +48287,6 @@ components:
|
||||
api_url:
|
||||
type: string
|
||||
minLength: 1
|
||||
request_content_type:
|
||||
$ref: '#/components/schemas/RequestContentTypeEnum'
|
||||
interactive:
|
||||
type: boolean
|
||||
score_min_threshold:
|
||||
@@ -53874,11 +53868,6 @@ components:
|
||||
minimum: -2147483648
|
||||
required:
|
||||
- name
|
||||
RequestContentTypeEnum:
|
||||
enum:
|
||||
- application/x-www-form-urlencoded
|
||||
- application/json
|
||||
type: string
|
||||
Review:
|
||||
type: object
|
||||
description: |-
|
||||
@@ -55974,8 +55963,6 @@ components:
|
||||
type: string
|
||||
invalidation_flow_url:
|
||||
type: string
|
||||
overview_url:
|
||||
type: string
|
||||
brand_name:
|
||||
type: string
|
||||
required:
|
||||
|
||||
16
uv.lock
generated
@@ -345,7 +345,7 @@ requires-dist = [
|
||||
{ name = "fido2", specifier = "==2.2.0" },
|
||||
{ name = "geoip2", specifier = "==5.2.0" },
|
||||
{ name = "geopy", specifier = "==2.4.1" },
|
||||
{ name = "google-api-python-client", specifier = "==2.196.0" },
|
||||
{ name = "google-api-python-client", specifier = "==2.195.0" },
|
||||
{ name = "gssapi", specifier = "==1.11.1" },
|
||||
{ name = "gunicorn", specifier = "==25.3.0" },
|
||||
{ name = "jsonpatch", specifier = "==1.33" },
|
||||
@@ -385,7 +385,7 @@ requires-dist = [
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "aws-cdk-lib", specifier = "==2.253.0" },
|
||||
{ name = "aws-cdk-lib", specifier = "==2.252.0" },
|
||||
{ name = "bandit", specifier = "==1.9.4" },
|
||||
{ name = "black", specifier = "==26.3.1" },
|
||||
{ name = "bpython", specifier = "==0.26" },
|
||||
@@ -495,7 +495,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-cdk-lib"
|
||||
version = "2.253.0"
|
||||
version = "2.252.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aws-cdk-asset-awscli-v1" },
|
||||
@@ -506,9 +506,9 @@ dependencies = [
|
||||
{ name = "publication" },
|
||||
{ name = "typeguard" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2d/ee/be9ce71331823a9860a8473a3a301359c154a5402f2895a9a30d222acaec/aws_cdk_lib-2.253.0.tar.gz", hash = "sha256:6db33e164f9afd6207698d382579bf62f762f14325f4b6d77643865e92448f20", size = 49584110, upload-time = "2026-05-06T17:42:37.086Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0b/2e/468ed756570af782831bc0518b4f187773b036342ce1b6f3d4e13e6127d8/aws_cdk_lib-2.252.0.tar.gz", hash = "sha256:2498d771ab141599c48494bd2564ee9a4fbaade54befa9356811e9454616d0a0", size = 49479070, upload-time = "2026-04-30T12:31:54.452Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/94/53b9f2aca8d5111ee952c0355f64ecba92809daced3364170e9bcfed81c6/aws_cdk_lib-2.253.0-py3-none-any.whl", hash = "sha256:58997c8a5af49d5328f5106468581a14235a1356dfe7574bb14656799594a2b5", size = 50269112, upload-time = "2026-05-06T17:41:54.504Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/94/32c21ad93dc21554286955fd5ebc68cb91149cc5f7f3154b07927c3fc693/aws_cdk_lib-2.252.0-py3-none-any.whl", hash = "sha256:c96d02582d344ee81ea2ef8a5e22b6e680789973804720ec9f0e95a050257db1", size = 50157828, upload-time = "2026-04-30T12:31:11.041Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1597,7 +1597,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "google-api-python-client"
|
||||
version = "2.196.0"
|
||||
version = "2.195.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-api-core" },
|
||||
@@ -1606,9 +1606,9 @@ dependencies = [
|
||||
{ name = "httplib2" },
|
||||
{ name = "uritemplate" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6d/f3/34ef8aca7909675fe327f96c1ed927f0520e7acf68af19157e96acc05e76/google_api_python_client-2.196.0.tar.gz", hash = "sha256:9f335d38f6caaa2747bcf64335ed1a9a19047d53e86538eda6a1b17d37f1743d", size = 14628129, upload-time = "2026-05-06T23:47:35.655Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/69/07/08d759b9cb10f48af14b25262dd0d6685ca8cda6c1f9e8a8109f57457205/google_api_python_client-2.195.0.tar.gz", hash = "sha256:c72cf2661c3addf01c880ce60541e83e1df354644b874f7f9d8d5ed2070446ae", size = 14584819, upload-time = "2026-04-30T21:51:50.638Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/99/c7/1817b4edf966d5afcac1c0781ca36d621bc0cb58104c4e7c2a475ab185f7/google_api_python_client-2.196.0-py3-none-any.whl", hash = "sha256:2591e9b47dcb17e4e62a09370aaee3bcf323af8f28ccecdabcd0a42a23ca4db5", size = 15206663, upload-time = "2026-05-06T23:47:32.886Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/b9/2c71095e31fff57668fec7c07ac897df065f15521d070e63229e13689590/google_api_python_client-2.195.0-py3-none-any.whl", hash = "sha256:753e62057f23049a89534bea0162b60fe391b85fb86d80bcdf884d05ec91c5bf", size = 15162418, upload-time = "2026-04-30T21:51:47.444Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
1
web/.gitignore
vendored
@@ -32,6 +32,7 @@ lib-cov
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
playwright-report
|
||||
playwright-traces
|
||||
test-results
|
||||
*.lcov
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* @import { StorybookConfig } from "@storybook/web-components-vite";
|
||||
*/
|
||||
|
||||
import { copyAssets } from "../scripts/build-assets.mjs";
|
||||
|
||||
/**
|
||||
* @param {TemplateStringsArray} strings
|
||||
* @param {...any} values
|
||||
@@ -12,14 +10,15 @@ import { copyAssets } from "../scripts/build-assets.mjs";
|
||||
*/
|
||||
const html = (strings, ...values) => String.raw({ raw: strings }, ...values);
|
||||
|
||||
await copyAssets();
|
||||
|
||||
/**
|
||||
* @satisfies {StorybookConfig}
|
||||
*/
|
||||
const config = {
|
||||
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
||||
staticDirs: [{ from: "../dist/assets", to: "/static/dist/assets" }],
|
||||
staticDirs: [
|
||||
{ from: "../icons", to: "/static/dist/assets/icons" },
|
||||
{ from: "../authentik", to: "/static/authentik" },
|
||||
],
|
||||
addons: [
|
||||
// ---
|
||||
"@storybook/addon-links",
|
||||
|
||||
@@ -2,12 +2,6 @@ import { PageFixture } from "#e2e/fixtures/PageFixture";
|
||||
|
||||
import { Page } from "@playwright/test";
|
||||
|
||||
export const GOOD_USERNAME = "test-admin@goauthentik.io";
|
||||
export const GOOD_PASSWORD = "test-runner";
|
||||
|
||||
export const BAD_USERNAME = "bad-username@bad-login.io";
|
||||
export const BAD_PASSWORD = "-this-is-a-bad-password-";
|
||||
|
||||
export interface LoginInit {
|
||||
username?: string;
|
||||
password?: string;
|
||||
|
||||
26
web/e2e/types/node.d.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @file Node.js global types and environment variables.
|
||||
*/
|
||||
|
||||
declare module "process" {
|
||||
global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
/**
|
||||
* The email address of the bootstrap user created when running the tests.
|
||||
*
|
||||
* @format email
|
||||
*/
|
||||
readonly AUTHENTIK_BOOTSTRAP_EMAIL?: string;
|
||||
/**
|
||||
* The password of the bootstrap user created when running the tests.
|
||||
*/
|
||||
readonly AUTHENTIK_BOOTSTRAP_PASSWORD?: string;
|
||||
/**
|
||||
* The directory where the authentik blueprints are stored.
|
||||
*/
|
||||
readonly AUTHENTIK_BLUEPRINTS_DIR?: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
web/icons/brand.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
1
web/icons/brand.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="d" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 3064.87 487.37"><defs><symbol id="a" viewBox="0 0 2865.3 437.72"><g style="isolation:isolate;"><path d="M238.73,125.38h76.4v304.5h-76.4v-32.18c-14.91,14.18-29.87,24.4-44.87,30.65-15,6.25-31.26,9.37-48.78,9.37-39.32,0-73.33-15.25-102.04-45.76C14.35,361.45,0,323.53,0,278.19s13.89-85.54,41.65-115.58c27.77-30.04,61.5-45.06,101.19-45.06,18.26,0,35.4,3.45,51.43,10.35,16.03,6.91,30.84,17.26,44.45,31.07v-33.58ZM158.41,188.07c-23.62,0-43.24,8.35-58.86,25.05-15.62,16.7-23.43,38.11-23.43,64.23s7.95,47.96,23.84,64.93c15.9,16.98,35.47,25.47,58.72,25.47s43.89-8.35,59.69-25.05c15.8-16.7,23.71-38.57,23.71-65.63s-7.9-47.95-23.71-64.37c-15.81-16.42-35.8-24.63-59.97-24.63Z" style="fill:#fd4b2d;"/><path d="M403.16,125.38h77.24v146.65c0,28.55,1.96,48.37,5.89,59.47,3.93,11.1,10.24,19.73,18.94,25.89,8.69,6.16,19.4,9.24,32.12,9.24s23.52-3.03,32.4-9.1c8.88-6.06,15.47-14.97,19.78-26.73,3.18-8.77,4.77-27.52,4.77-56.25V125.38h76.41v129.02c0,53.18-4.2,89.56-12.59,109.15-10.26,23.88-25.38,42.22-45.34,54.99-19.97,12.78-45.34,19.17-76.13,19.17-33.4,0-60.41-7.46-81.02-22.39-20.62-14.92-35.13-35.73-43.52-62.41-5.97-18.47-8.96-52.06-8.96-100.75v-126.78Z" style="fill:#fd4b2d;"/><path d="M796.76,13.15h76.41v112.23h45.34v65.77h-45.34v238.73h-76.41v-238.73h-39.18v-65.77h39.18V13.15Z" style="fill:#fd4b2d;"/><path d="M999.76,7.84h75.85v148.33c14.93-12.88,29.95-22.53,45.06-28.97,15.11-6.44,30.41-9.65,45.9-9.65,30.23,0,55.7,10.45,76.41,31.34,17.73,18.1,26.59,44.69,26.59,79.76v201.23h-75.29v-133.5c0-35.27-1.68-59.15-5.04-71.65-3.36-12.5-9.09-21.83-17.21-27.99-8.12-6.15-18.15-9.23-30.09-9.23-15.49,0-28.78,5.13-39.88,15.39-11.11,10.26-18.8,24.26-23.09,41.98-2.24,9.14-3.36,30.04-3.36,62.69v122.3h-75.85V7.84Z" style="fill:#fd4b2d;"/><path d="M1688.63,299.74h-245.45c3.54,21.65,13.01,38.86,28.41,51.64,15.39,12.78,35.03,19.17,58.91,19.17,28.55,0,53.08-9.98,73.6-29.95l64.37,30.23c-16.05,22.77-35.26,39.6-57.65,50.52-22.39,10.91-48.98,16.37-79.76,16.37-47.77,0-86.67-15.06-116.71-45.2-30.04-30.13-45.06-67.87-45.06-113.21s14.97-85.03,44.92-115.73c29.95-30.69,67.49-46.04,112.65-46.04,47.95,0,86.95,15.35,116.99,46.04,30.04,30.69,45.06,71.23,45.06,121.61l-.28,14.55ZM1612.22,239.57c-5.05-16.98-15-30.79-29.86-41.42-14.86-10.63-32.1-15.95-51.72-15.95-21.3,0-40,5.98-56.07,17.91-10.09,7.47-19.44,20.62-28.03,39.46h165.68Z" style="fill:#fd4b2d;"/><path d="M1790.6,125.38h76.41v31.21c17.33-14.61,33.02-24.77,47.09-30.48,14.06-5.71,28.46-8.57,43.18-8.57,30.18,0,55.8,10.54,76.85,31.62,17.7,17.91,26.55,44.41,26.55,79.48v201.23h-75.57v-133.35c0-36.34-1.63-60.47-4.89-72.4-3.26-11.93-8.93-21.01-17.03-27.26-8.1-6.24-18.1-9.36-30.01-9.36-15.45,0-28.71,5.17-39.78,15.51-11.08,10.35-18.76,24.65-23.04,42.91-2.24,9.5-3.35,30.1-3.35,61.78v122.16h-76.41V125.38Z" style="fill:#fd4b2d;"/><path d="M2183.92,13.15h76.41v112.23h45.34v65.77h-45.34v238.73h-76.41v-238.73h-39.18v-65.77h39.18V13.15Z" style="fill:#fd4b2d;"/><path d="M2416.46,0c13.39,0,24.88,4.85,34.46,14.55,9.58,9.7,14.38,21.46,14.38,35.27s-4.75,25.24-14.24,34.84c-9.49,9.61-20.84,14.41-34.04,14.41s-25.16-4.9-34.75-14.69c-9.58-9.79-14.37-21.69-14.37-35.68s4.74-24.91,14.23-34.43c9.49-9.51,20.93-14.27,34.33-14.27ZM2378.26,125.38h76.41v304.5h-76.41V125.38Z" style="fill:#fd4b2d;"/><path d="M2564.75,7.84h76.41v243.09l112.51-125.54h95.96l-131.17,145.94,146.86,158.56h-94.85l-129.3-140.34v140.34h-76.41V7.84Z" style="fill:#fd4b2d;"/></g></symbol></defs><use width="2865.3" height="437.72" transform="translate(99.78 24.83)" xlink:href="#a"/></svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
BIN
web/icons/icon.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
1
web/icons/icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="c" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1000 1000"><defs><symbol id="a" viewBox="0 0 998.94 763.82"><path d="M829.67,0h-425.28c-93.1,0-169.27,76.17-169.27,169.27v425.28c0,93.1,76.17,169.27,169.27,169.27h50.18v-165.68h324.96v165.68h50.14c93.1,0,169.27-76.17,169.27-169.27V169.27C998.94,76.17,922.77,0,829.67,0ZM755.98,463.53H235.4v-114.49h268.96v-158.97h43.68v94.7h25.61v-94.7h30.88v69.64h25.61v-69.64h30.88v116.35h25.61v-116.35h43.68v158.97h25.69v114.49Z" style="fill:#fd4b2d;"/><g id="b"><path d="M237.36,342.19h-.02c-25.34-34.27-63.32-69.15-105.42-69.15-48.4.03-92.89,26.58-115.91,69.15-48.08,83.85,18.39,196.94,115.91,194.36,75.46,0,137.69-111.95,137.69-131.75,0-8.76-12.18-35.49-32.25-62.61ZM77.32,342.19c27.16-23.43,66.59-30.27,95.1,0h.02c21.51,19.51,40.28,47.91,47.08,62.35-84.6,176.88-232.87,26.13-142.2-62.35Z" style="fill:#fd4b2d;"/></g></symbol></defs><use width="998.94" height="763.82" transform="translate(1 117.03)" xlink:href="#a"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
web/icons/icon_left_brand.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
1
web/icons/icon_left_brand.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="i" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 3767.3 592.89"><defs><symbol id="a" viewBox="0 0 998.94 763.82"><path d="M829.67,0h-425.28c-93.1,0-169.27,76.17-169.27,169.27v425.28c0,93.1,76.17,169.27,169.27,169.27h50.18v-165.68h324.96v165.68h50.14c93.1,0,169.27-76.17,169.27-169.27V169.27C998.94,76.17,922.77,0,829.67,0ZM755.98,463.53H235.4v-114.49h268.96v-158.97h43.68v94.7h25.61v-94.7h30.88v69.64h25.61v-69.64h30.88v116.35h25.61v-116.35h43.68v158.97h25.69v114.49Z" style="fill:#fd4b2d;"/><g id="b"><path d="M237.36,342.19h-.02c-25.34-34.27-63.32-69.15-105.42-69.15-48.4.03-92.89,26.58-115.91,69.15-48.08,83.85,18.39,196.94,115.91,194.36,75.46,0,137.69-111.95,137.69-131.75,0-8.76-12.18-35.49-32.25-62.61ZM77.32,342.19c27.16-23.43,66.59-30.27,95.1,0h.02c21.51,19.51,40.28,47.91,47.08,62.35-84.6,176.88-232.87,26.13-142.2-62.35Z" style="fill:#fd4b2d;"/></g></symbol><symbol id="c" viewBox="0 0 2865.3 437.72"><g style="isolation:isolate;"><path d="M238.73,125.38h76.4v304.5h-76.4v-32.18c-14.91,14.18-29.87,24.4-44.87,30.65-15,6.25-31.26,9.37-48.78,9.37-39.32,0-73.33-15.25-102.04-45.76C14.35,361.45,0,323.53,0,278.19s13.89-85.54,41.65-115.58c27.77-30.04,61.5-45.06,101.19-45.06,18.26,0,35.4,3.45,51.43,10.35,16.03,6.91,30.84,17.26,44.45,31.07v-33.58ZM158.41,188.07c-23.62,0-43.24,8.35-58.86,25.05-15.62,16.7-23.43,38.11-23.43,64.23s7.95,47.96,23.84,64.93c15.9,16.98,35.47,25.47,58.72,25.47s43.89-8.35,59.69-25.05c15.8-16.7,23.71-38.57,23.71-65.63s-7.9-47.95-23.71-64.37c-15.81-16.42-35.8-24.63-59.97-24.63Z" style="fill:#fd4b2d;"/><path d="M403.16,125.38h77.24v146.65c0,28.55,1.96,48.37,5.89,59.47,3.93,11.1,10.24,19.73,18.94,25.89,8.69,6.16,19.4,9.24,32.12,9.24s23.52-3.03,32.4-9.1c8.88-6.06,15.47-14.97,19.78-26.73,3.18-8.77,4.77-27.52,4.77-56.25V125.38h76.41v129.02c0,53.18-4.2,89.56-12.59,109.15-10.26,23.88-25.38,42.22-45.34,54.99-19.97,12.78-45.34,19.17-76.13,19.17-33.4,0-60.41-7.46-81.02-22.39-20.62-14.92-35.13-35.73-43.52-62.41-5.97-18.47-8.96-52.06-8.96-100.75v-126.78Z" style="fill:#fd4b2d;"/><path d="M796.76,13.15h76.41v112.23h45.34v65.77h-45.34v238.73h-76.41v-238.73h-39.18v-65.77h39.18V13.15Z" style="fill:#fd4b2d;"/><path d="M999.76,7.84h75.85v148.33c14.93-12.88,29.95-22.53,45.06-28.97,15.11-6.44,30.41-9.65,45.9-9.65,30.23,0,55.7,10.45,76.41,31.34,17.73,18.1,26.59,44.69,26.59,79.76v201.23h-75.29v-133.5c0-35.27-1.68-59.15-5.04-71.65-3.36-12.5-9.09-21.83-17.21-27.99-8.12-6.15-18.15-9.23-30.09-9.23-15.49,0-28.78,5.13-39.88,15.39-11.11,10.26-18.8,24.26-23.09,41.98-2.24,9.14-3.36,30.04-3.36,62.69v122.3h-75.85V7.84Z" style="fill:#fd4b2d;"/><path d="M1688.63,299.74h-245.45c3.54,21.65,13.01,38.86,28.41,51.64,15.39,12.78,35.03,19.17,58.91,19.17,28.55,0,53.08-9.98,73.6-29.95l64.37,30.23c-16.05,22.77-35.26,39.6-57.65,50.52-22.39,10.91-48.98,16.37-79.76,16.37-47.77,0-86.67-15.06-116.71-45.2-30.04-30.13-45.06-67.87-45.06-113.21s14.97-85.03,44.92-115.73c29.95-30.69,67.49-46.04,112.65-46.04,47.95,0,86.95,15.35,116.99,46.04,30.04,30.69,45.06,71.23,45.06,121.61l-.28,14.55ZM1612.22,239.57c-5.05-16.98-15-30.79-29.86-41.42-14.86-10.63-32.1-15.95-51.72-15.95-21.3,0-40,5.98-56.07,17.91-10.09,7.47-19.44,20.62-28.03,39.46h165.68Z" style="fill:#fd4b2d;"/><path d="M1790.6,125.38h76.41v31.21c17.33-14.61,33.02-24.77,47.09-30.48,14.06-5.71,28.46-8.57,43.18-8.57,30.18,0,55.8,10.54,76.85,31.62,17.7,17.91,26.55,44.41,26.55,79.48v201.23h-75.57v-133.35c0-36.34-1.63-60.47-4.89-72.4-3.26-11.93-8.93-21.01-17.03-27.26-8.1-6.24-18.1-9.36-30.01-9.36-15.45,0-28.71,5.17-39.78,15.51-11.08,10.35-18.76,24.65-23.04,42.91-2.24,9.5-3.35,30.1-3.35,61.78v122.16h-76.41V125.38Z" style="fill:#fd4b2d;"/><path d="M2183.92,13.15h76.41v112.23h45.34v65.77h-45.34v238.73h-76.41v-238.73h-39.18v-65.77h39.18V13.15Z" style="fill:#fd4b2d;"/><path d="M2416.46,0c13.39,0,24.88,4.85,34.46,14.55,9.58,9.7,14.38,21.46,14.38,35.27s-4.75,25.24-14.24,34.84c-9.49,9.61-20.84,14.41-34.04,14.41s-25.16-4.9-34.75-14.69c-9.58-9.79-14.37-21.69-14.37-35.68s4.74-24.91,14.23-34.43c9.49-9.51,20.93-14.27,34.33-14.27ZM2378.26,125.38h76.41v304.5h-76.41V125.38Z" style="fill:#fd4b2d;"/><path d="M2564.75,7.84h76.41v243.09l112.51-125.54h95.96l-131.17,145.94,146.86,158.56h-94.85l-129.3-140.34v140.34h-76.41V7.84Z" style="fill:#fd4b2d;"/></g></symbol></defs><use width="998.94" height="763.82" transform="translate(28.54 36.14) scale(.68)" xlink:href="#a"/><use width="2865.3" height="437.72" transform="translate(802.22 67.81)" xlink:href="#c"/></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
BIN
web/icons/icon_pride_lgbt.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
web/icons/icon_pride_trans.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
web/icons/icon_top_brand.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
1
web/icons/icon_top_brand.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="h" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000"><g id="i"><g style="isolation:isolate;"><path d="M97.01,790.9h26.11v104.07h-26.11v-11c-5.1,4.85-10.21,8.34-15.34,10.47-5.13,2.14-10.68,3.2-16.67,3.2-13.44,0-25.06-5.21-34.87-15.64-9.81-10.43-14.71-23.39-14.71-38.88s4.75-29.24,14.24-39.5c9.49-10.27,21.02-15.4,34.59-15.4,6.24,0,12.1,1.18,17.58,3.54,5.48,2.36,10.54,5.9,15.19,10.62v-11.48ZM69.56,812.32c-8.07,0-14.78,2.86-20.12,8.56-5.34,5.71-8.01,13.03-8.01,21.95s2.72,16.39,8.15,22.19c5.44,5.8,12.12,8.7,20.07,8.7s15-2.85,20.4-8.56c5.4-5.71,8.1-13.18,8.1-22.43s-2.7-16.39-8.1-22c-5.4-5.61-12.23-8.42-20.5-8.42Z" style="fill:#fd4b2d;"/><path d="M153.21,790.9h26.4v50.12c0,9.76.67,16.53,2.01,20.33,1.34,3.79,3.5,6.74,6.47,8.85,2.97,2.1,6.63,3.16,10.98,3.16s8.04-1.04,11.08-3.11c3.04-2.07,5.29-5.12,6.76-9.14,1.09-3,1.63-9.41,1.63-19.23v-50.98h26.11v44.1c0,18.17-1.44,30.61-4.3,37.31-3.51,8.16-8.67,14.43-15.5,18.8-6.83,4.37-15.5,6.55-26.02,6.55-11.42,0-20.65-2.55-27.69-7.65-7.05-5.1-12.01-12.21-14.87-21.33-2.04-6.31-3.06-17.79-3.06-34.44v-43.33Z" style="fill:#fd4b2d;"/><path d="M287.73,752.54h26.11v38.36h15.5v22.48h-15.5v81.59h-26.11v-81.59h-13.39v-22.48h13.39v-38.36Z" style="fill:#fd4b2d;"/><path d="M357.11,750.72h25.92v50.7c5.1-4.4,10.23-7.7,15.4-9.9s10.39-3.3,15.69-3.3c10.33,0,19.04,3.57,26.11,10.71,6.06,6.19,9.09,15.27,9.09,27.26v68.78h-25.73v-45.63c0-12.05-.57-20.21-1.72-24.49-1.15-4.27-3.11-7.46-5.88-9.57-2.77-2.1-6.2-3.16-10.28-3.16-5.29,0-9.84,1.75-13.63,5.26-3.8,3.51-6.43,8.29-7.89,14.35-.77,3.13-1.15,10.27-1.15,21.43v41.8h-25.92v-144.25Z" style="fill:#fd4b2d;"/><path d="M592.55,850.49h-83.89c1.21,7.4,4.45,13.28,9.71,17.65,5.26,4.37,11.97,6.55,20.14,6.55,9.76,0,18.14-3.41,25.16-10.23l22,10.33c-5.48,7.78-12.05,13.54-19.7,17.27-7.65,3.73-16.74,5.6-27.26,5.6-16.33,0-29.62-5.15-39.89-15.45-10.27-10.3-15.4-23.2-15.4-38.69s5.12-29.06,15.35-39.55c10.24-10.49,23.07-15.73,38.5-15.73,16.39,0,29.72,5.25,39.98,15.73,10.27,10.49,15.4,24.34,15.4,41.56l-.1,4.97ZM566.44,829.92c-1.72-5.8-5.13-10.52-10.2-14.16-5.08-3.63-10.97-5.45-17.68-5.45-7.28,0-13.67,2.04-19.16,6.12-3.45,2.55-6.64,7.05-9.58,13.49h56.63Z" style="fill:#fd4b2d;"/><path d="M627.41,790.9h26.11v10.67c5.92-4.99,11.29-8.46,16.09-10.42,4.81-1.95,9.73-2.93,14.76-2.93,10.32,0,19.07,3.6,26.27,10.81,6.05,6.12,9.07,15.18,9.07,27.17v68.78h-25.83v-45.57c0-12.42-.56-20.67-1.67-24.74-1.11-4.08-3.05-7.18-5.82-9.32-2.77-2.13-6.19-3.2-10.26-3.2-5.28,0-9.81,1.77-13.6,5.3-3.79,3.54-6.41,8.43-7.87,14.67-.76,3.25-1.14,10.29-1.14,21.11v41.75h-26.11v-104.07Z" style="fill:#fd4b2d;"/><path d="M761.83,752.54h26.11v38.36h15.5v22.48h-15.5v81.59h-26.11v-81.59h-13.39v-22.48h13.39v-38.36Z" style="fill:#fd4b2d;"/><path d="M841.31,748.04c4.58,0,8.5,1.66,11.78,4.97,3.28,3.32,4.91,7.33,4.91,12.05s-1.62,8.63-4.87,11.91c-3.24,3.29-7.12,4.93-11.64,4.93s-8.6-1.67-11.88-5.02c-3.28-3.35-4.91-7.41-4.91-12.2s1.62-8.51,4.86-11.77c3.24-3.25,7.15-4.88,11.73-4.88ZM828.25,790.9h26.11v104.07h-26.11v-104.07Z" style="fill:#fd4b2d;"/><path d="M891.99,750.72h26.11v83.08l38.45-42.91h32.8l-44.83,49.88,50.19,54.19h-32.42l-44.19-47.96v47.96h-26.11v-144.25Z" style="fill:#fd4b2d;"/></g></g><path d="M689.34,93.81h-329.14c-72.05,0-131,58.95-131,131v329.14c0,72.05,58.95,131,131,131h38.83v-128.22h251.49v128.22h38.81c72.05,0,131-58.95,131-131V224.81c0-72.05-58.95-131-131-131ZM632.3,452.55H229.41v-88.61h208.16v-123.03h33.8v73.29h19.82v-73.29h23.9v53.9h19.82v-53.9h23.9v90.04h19.82v-90.04h33.8v123.03h19.88v88.61Z" style="fill:#fd4b2d;"/><g id="j"><path d="M230.92,358.64h-.02c-19.61-26.52-49.01-53.52-81.58-53.52-37.46.03-71.89,20.57-89.7,53.52-37.21,64.9,14.23,152.42,89.7,150.42,58.4,0,106.56-86.64,106.56-101.97,0-6.78-9.42-27.46-24.96-48.46ZM107.07,358.64c21.02-18.14,51.54-23.42,73.6,0h.02c16.65,15.1,31.18,37.08,36.44,48.26-65.47,136.9-180.22,20.22-110.06-48.26Z" style="fill:#fd4b2d;"/></g></svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
117
web/package-lock.json
generated
@@ -24,7 +24,6 @@
|
||||
"@formatjs/intl-listformat": "^8.3.5",
|
||||
"@fortawesome/fontawesome-free": "^7.2.0",
|
||||
"@goauthentik/api": "0.0.0",
|
||||
"@goauthentik/brand-assets": "^2.0.0",
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
"@goauthentik/esbuild-plugin-live-reload": "^2.0.1",
|
||||
"@goauthentik/eslint-config": "^1.3.0",
|
||||
@@ -58,7 +57,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.57.2",
|
||||
"@typescript-eslint/parser": "^8.57.2",
|
||||
"@typescript-eslint/utils": "^8.57.2",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260506.1",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260421.2",
|
||||
"@vitest/browser": "^4.1.6",
|
||||
"@vitest/browser-playwright": "^4.1.6",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
@@ -80,7 +79,7 @@
|
||||
"globals": "^17.6.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"hastscript": "^9.0.1",
|
||||
"knip": "^6.12.0",
|
||||
"knip": "^6.11.0",
|
||||
"lex": "^2025.11.0",
|
||||
"lit": "^3.3.2",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
@@ -1552,12 +1551,6 @@
|
||||
"resolved": "packages/client-ts",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@goauthentik/brand-assets": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/brand-assets/-/brand-assets-2.0.0.tgz",
|
||||
"integrity": "sha512-yRJrV+KuGrz7MNcRzAkZa4e7LuciuFZBVSyPFRd/EndxgiqcFuFHyn+6tEurKNmianBNURhe2qm5ytoLFgEWFQ==",
|
||||
"license": "UNLICENSED"
|
||||
},
|
||||
"node_modules/@goauthentik/core": {
|
||||
"resolved": "packages/core",
|
||||
"link": true
|
||||
@@ -5868,30 +5861,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/native-preview": {
|
||||
"version": "7.0.0-dev.20260506.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260506.1.tgz",
|
||||
"integrity": "sha512-UcEslgHBaHYPAisVQcyARDfps7nKyugmUyXcsfE1HiHcVuvZ4tBJ5C93sG1FDeHWJ9skGQ68ec+Xsx086geAfg==",
|
||||
"version": "7.0.0-dev.20260421.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260421.2.tgz",
|
||||
"integrity": "sha512-CmajHI25HpVWE9R1XFoxr+cphJPxoYD3eFioQtAvXYkMFKnLdICMS9pXre9Pybizb75ejRxjKD5/CVG055rEIg==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsgo": "bin/tsgo.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260506.1",
|
||||
"@typescript/native-preview-darwin-x64": "7.0.0-dev.20260506.1",
|
||||
"@typescript/native-preview-linux-arm": "7.0.0-dev.20260506.1",
|
||||
"@typescript/native-preview-linux-arm64": "7.0.0-dev.20260506.1",
|
||||
"@typescript/native-preview-linux-x64": "7.0.0-dev.20260506.1",
|
||||
"@typescript/native-preview-win32-arm64": "7.0.0-dev.20260506.1",
|
||||
"@typescript/native-preview-win32-x64": "7.0.0-dev.20260506.1"
|
||||
"@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260421.2",
|
||||
"@typescript/native-preview-darwin-x64": "7.0.0-dev.20260421.2",
|
||||
"@typescript/native-preview-linux-arm": "7.0.0-dev.20260421.2",
|
||||
"@typescript/native-preview-linux-arm64": "7.0.0-dev.20260421.2",
|
||||
"@typescript/native-preview-linux-x64": "7.0.0-dev.20260421.2",
|
||||
"@typescript/native-preview-win32-arm64": "7.0.0-dev.20260421.2",
|
||||
"@typescript/native-preview-win32-x64": "7.0.0-dev.20260421.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/native-preview-darwin-arm64": {
|
||||
"version": "7.0.0-dev.20260506.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260506.1.tgz",
|
||||
"integrity": "sha512-dAd7qG2J508+4CRSuoEA0EUxViIedQ0D+8xKoZiM0EQHCwww8glWYCo72UTjcRZctS3QbJY3PtGSvo3nzL4oVw==",
|
||||
"version": "7.0.0-dev.20260421.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260421.2.tgz",
|
||||
"integrity": "sha512-fHv1r3ZmVo6zxuAIFmuX3w9QxbcauoG0SsWhmDwm6VmRubLlOJIcmTtlmV3JAb9oOnq8LuzZljzT7Q39fSMQDw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5899,15 +5889,12 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-darwin-x64": {
|
||||
"version": "7.0.0-dev.20260506.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260506.1.tgz",
|
||||
"integrity": "sha512-1Q7Elncpuiozvx3HCTgFbSxNz2m2FIkO1QW5f15igcZDG3vMW4QglNflmXosc69bzYI7KfYZuaGX3yGzJkGbfg==",
|
||||
"version": "7.0.0-dev.20260421.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260421.2.tgz",
|
||||
"integrity": "sha512-KWTR6xbW9t+JS7D5DQIzo75pqVXVWUxF9PMv/+S6xsnOjCVd6g0ixHcFpFMJMKSUQpGPr8Z5f7b8ks6LHW01jg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -5915,15 +5902,12 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-linux-arm": {
|
||||
"version": "7.0.0-dev.20260506.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260506.1.tgz",
|
||||
"integrity": "sha512-MfYn1p+aOorZ2Y+7sqLvSoAXPEz/RfKgHfeYO240Udco30B4oapm7Hsq2PsS9Z2Oth/RorGjY0jLP2OhnkY2Ig==",
|
||||
"version": "7.0.0-dev.20260421.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260421.2.tgz",
|
||||
"integrity": "sha512-BWLQO3nemLDSV5PoE5GPHe1dU9Dth77Kv8/cle9Ujcp4LhPo0KincdPqFH/qKeU/xvW25mgFueflZ1nc4rKuww==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -5931,15 +5915,12 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-linux-arm64": {
|
||||
"version": "7.0.0-dev.20260506.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260506.1.tgz",
|
||||
"integrity": "sha512-Q1W4DHplR2urmtPwoz9tw6XUGWRNXF+CIXJQ8ZpIZFj/OHgvTw8vkYkKFuaEao3lSjTsR4lQe/wL2Xr5K0hxuA==",
|
||||
"version": "7.0.0-dev.20260421.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260421.2.tgz",
|
||||
"integrity": "sha512-VLMEuml3BhUb+jaL0TXQ4xvVODxJF+RhkI+tBWvlynsJI4khTXEiwWh+wPOJrsfBRYFRMXEu28Odl/HXkYze8w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5947,15 +5928,12 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-linux-x64": {
|
||||
"version": "7.0.0-dev.20260506.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260506.1.tgz",
|
||||
"integrity": "sha512-b+sbLBCIchbrGQNbjIvVN2qd+ieqqp/nghi0n2zOAKGPsfd5wG6ceqxWJKADdBDCohsCCGt//rZccUwFugIsyA==",
|
||||
"version": "7.0.0-dev.20260421.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260421.2.tgz",
|
||||
"integrity": "sha512-qUrJWTB5/wv4wnRG0TRXElAxc2kykNiRNyEIEqBbLmzDlrcvAW7RRy8MXoY1ZyTiKGMu14itZ3x9oW6+blFpRw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -5963,15 +5941,12 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-win32-arm64": {
|
||||
"version": "7.0.0-dev.20260506.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260506.1.tgz",
|
||||
"integrity": "sha512-l59d8pZjFT7GoWpgCOy6aBcxLSALphA91X4Z/2XHo5HnM0bQ/yJjB7XMeUQZBdk5DZCdZL+sWTfmXLRggm7sFg==",
|
||||
"version": "7.0.0-dev.20260421.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260421.2.tgz",
|
||||
"integrity": "sha512-Rc6NsWlZmCs5YUKVzKgwoBOoRUGsPzct4BDMRX0csD1devLBBc4AbUXWKsJRbpwIAnqMO1ld4sNHEb+wXgfNHQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5979,15 +5954,12 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@typescript/native-preview-win32-x64": {
|
||||
"version": "7.0.0-dev.20260506.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260506.1.tgz",
|
||||
"integrity": "sha512-dJDLSzaz2xjRYYmTSfcCepZUi3ITjQSJ6Gk5YGplMF57UmZCAGI+ns4Te/V74IJiQigXqTnyEIGorwsOqhW8gQ==",
|
||||
"version": "7.0.0-dev.20260421.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260421.2.tgz",
|
||||
"integrity": "sha512-GQv1+dya1t6EqF2Cpsb+xoozovdX10JUSf6Kl/8xNkTapzmlHd+uMr+8ku3jIASTxoRGn0Mklgjj3MDKrOTuLg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -5995,10 +5967,7 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@ungap/structured-clone": {
|
||||
"version": "1.3.0",
|
||||
@@ -11978,9 +11947,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/knip": {
|
||||
"version": "6.12.0",
|
||||
"resolved": "https://registry.npmjs.org/knip/-/knip-6.12.0.tgz",
|
||||
"integrity": "sha512-nRg8+DOFcfBD6NjmNzu9+3D35QnEmMsnojJGOHQUqv+70r1aOx99wpSUXvEV7syQVOL5E6tNXXkoyG1Fuz8BWg==",
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/knip/-/knip-6.11.0.tgz",
|
||||
"integrity": "sha512-84PTlN8Q5smLpTbzs8smTVh8PMbTDXtw0tFksXq/m6auGFC/KSzJykKFmnYh3As38kiWDkoDBvdTTyKk5M1TAQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
||||
@@ -99,7 +99,6 @@
|
||||
"@formatjs/intl-listformat": "^8.3.5",
|
||||
"@fortawesome/fontawesome-free": "^7.2.0",
|
||||
"@goauthentik/api": "0.0.0",
|
||||
"@goauthentik/brand-assets": "^2.0.0",
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
"@goauthentik/esbuild-plugin-live-reload": "^2.0.1",
|
||||
"@goauthentik/eslint-config": "^1.3.0",
|
||||
@@ -133,7 +132,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.57.2",
|
||||
"@typescript-eslint/parser": "^8.57.2",
|
||||
"@typescript-eslint/utils": "^8.57.2",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260506.1",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260421.2",
|
||||
"@vitest/browser": "^4.1.6",
|
||||
"@vitest/browser-playwright": "^4.1.6",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
@@ -155,7 +154,7 @@
|
||||
"globals": "^17.6.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"hastscript": "^9.0.1",
|
||||
"knip": "^6.12.0",
|
||||
"knip": "^6.11.0",
|
||||
"lex": "^2025.11.0",
|
||||
"lit": "^3.3.2",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
|
||||
@@ -23,10 +23,16 @@ export default defineConfig({
|
||||
testDir: "./test/browser",
|
||||
fullyParallel: true,
|
||||
forbidOnly: CI,
|
||||
retries: CI ? 2 : 0,
|
||||
workers: CI ? 1 : undefined,
|
||||
retries: CI ? 1 : 0,
|
||||
workers: "50%",
|
||||
maxFailures: CI ? 5 : 2,
|
||||
reporter: CI
|
||||
? "github"
|
||||
? [
|
||||
// ---
|
||||
["github"],
|
||||
["html", { open: "never", outputFolder: "playwright-report" }],
|
||||
["json", { outputFile: "playwright-report/results.json" }],
|
||||
]
|
||||
: [
|
||||
// ---
|
||||
["list", { printSteps: true }],
|
||||
@@ -36,6 +42,8 @@ export default defineConfig({
|
||||
testIdAttribute: "data-test-id",
|
||||
baseURL,
|
||||
trace: "on-first-retry",
|
||||
screenshot: "only-on-failure",
|
||||
video: CI ? "retain-on-failure" : "off",
|
||||
colorScheme: "dark",
|
||||
launchOptions: {
|
||||
logger: {
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import "@goauthentik/core/environment/load/node";
|
||||
|
||||
import * as fs from "node:fs/promises";
|
||||
import { createRequire } from "node:module";
|
||||
import * as path from "node:path";
|
||||
|
||||
import { ConsoleLogger } from "#logger/node";
|
||||
import { DistDirectory, EntryPoint, PackageRoot } from "#paths/node";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const logger = ConsoleLogger.child({ name: "Assets" });
|
||||
|
||||
/**
|
||||
* @typedef {[from: string, to: string]} SourceDestinationPair
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {SourceDestinationPair[]}
|
||||
*/
|
||||
const assets = [
|
||||
[
|
||||
path.join(path.dirname(EntryPoint.StandaloneLoading.in), "startup"),
|
||||
path.dirname(EntryPoint.StandaloneLoading.out),
|
||||
],
|
||||
[path.resolve(PackageRoot, "src", "assets", "images"), "./assets/images"],
|
||||
[require.resolve("@goauthentik/brand-assets/brand.png"), "./assets/icons/brand.png"],
|
||||
[require.resolve("@goauthentik/brand-assets/brand.svg"), "./assets/icons/brand.svg"],
|
||||
[require.resolve("@goauthentik/brand-assets/icon.png"), "./assets/icons/icon.png"],
|
||||
[require.resolve("@goauthentik/brand-assets/icon.svg"), "./assets/icons/icon.svg"],
|
||||
[
|
||||
require.resolve("@goauthentik/brand-assets/icon_left_brand.png"),
|
||||
"./assets/icons/icon_left_brand.png",
|
||||
],
|
||||
[
|
||||
require.resolve("@goauthentik/brand-assets/icon_left_brand.svg"),
|
||||
"./assets/icons/icon_left_brand.svg",
|
||||
],
|
||||
[
|
||||
require.resolve("@goauthentik/brand-assets/icon_pride_lgbt.png"),
|
||||
"./assets/icons/icon_pride_lgbt.png",
|
||||
],
|
||||
[
|
||||
require.resolve("@goauthentik/brand-assets/icon_pride_trans.png"),
|
||||
"./assets/icons/icon_pride_trans.png",
|
||||
],
|
||||
[
|
||||
require.resolve("@goauthentik/brand-assets/icon_top_brand.png"),
|
||||
"./assets/icons/icon_top_brand.png",
|
||||
],
|
||||
[
|
||||
require.resolve("@goauthentik/brand-assets/icon_top_brand.svg"),
|
||||
"./assets/icons/icon_top_brand.svg",
|
||||
],
|
||||
];
|
||||
|
||||
export async function copyAssets() {
|
||||
/**
|
||||
* @param {SourceDestinationPair} pair
|
||||
*/
|
||||
const copy = ([from, to]) => {
|
||||
const resolvedDestination = path.resolve(DistDirectory, to);
|
||||
|
||||
logger.debug(`📋 Copying assets from ${from} to ${to}`);
|
||||
|
||||
return fs
|
||||
.cp(from, resolvedDestination, {
|
||||
recursive: true,
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`Failed to copy assets from ${from} to ${to}: ${error}`);
|
||||
});
|
||||
};
|
||||
|
||||
return await Promise.all(assets.map(copy));
|
||||
}
|
||||
@@ -7,8 +7,6 @@ import "@goauthentik/core/environment/load/node";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
|
||||
import { copyAssets } from "./build-assets.mjs";
|
||||
|
||||
/**
|
||||
* @file ESBuild script for building the authentik web UI.
|
||||
*
|
||||
@@ -37,6 +35,22 @@ const publicBundledDefinitions = Object.fromEntries(
|
||||
);
|
||||
logger.info(publicBundledDefinitions, "Bundle definitions");
|
||||
|
||||
/**
|
||||
* @typedef {[from: string, to: string]} SourceDestinationPair
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {SourceDestinationPair[]}
|
||||
*/
|
||||
const assets = [
|
||||
[
|
||||
path.join(path.dirname(EntryPoint.StandaloneLoading.in), "startup"),
|
||||
path.dirname(EntryPoint.StandaloneLoading.out),
|
||||
],
|
||||
[path.resolve(PackageRoot, "src", "assets", "images"), "./assets/images"],
|
||||
[path.resolve(PackageRoot, "icons"), "./assets/icons"],
|
||||
];
|
||||
|
||||
const entryPointNames = Object.keys(EntryPoint);
|
||||
const entryPoints = Object.values(EntryPoint);
|
||||
const entryPointsDescription = entryPointNames.join("\n\t");
|
||||
@@ -54,7 +68,29 @@ const BASE_ESBUILD_PLUGINS = [
|
||||
*/
|
||||
const errors = [];
|
||||
|
||||
await copyAssets();
|
||||
/**
|
||||
* @param {SourceDestinationPair} pair
|
||||
*/
|
||||
const copy = ([from, to]) => {
|
||||
const resolvedDestination = path.resolve(DistDirectory, to);
|
||||
|
||||
logger.debug(`📋 Copying assets from ${from} to ${to}`);
|
||||
|
||||
return fs
|
||||
.cp(from, resolvedDestination, {
|
||||
recursive: true,
|
||||
})
|
||||
.catch((error) => {
|
||||
errors.push({
|
||||
text: `Failed to copy assets from ${from} to ${to}: ${error}`,
|
||||
location: {
|
||||
file: from,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
await Promise.all(assets.map(copy));
|
||||
|
||||
return { errors };
|
||||
});
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
availableHashes,
|
||||
DEFAULT_HASH_ALGORITHM,
|
||||
digestAlgorithmOptions,
|
||||
logoutMethodOptions,
|
||||
retrieveSignatureAlgorithm,
|
||||
SAMLSupportedKeyTypes,
|
||||
} from "./SAMLProviderOptions.js";
|
||||
@@ -30,6 +29,7 @@ import {
|
||||
PropertymappingsApi,
|
||||
PropertymappingsProviderSamlListRequest,
|
||||
SAMLBindingsEnum,
|
||||
SAMLLogoutMethods,
|
||||
SAMLNameIDPolicyEnum,
|
||||
SAMLPropertyMapping,
|
||||
SAMLProvider,
|
||||
@@ -90,6 +90,23 @@ function renderHasSlsUrl(
|
||||
logoutMethod: string,
|
||||
setLogoutMethod?: (ev: Event) => void,
|
||||
) {
|
||||
const logoutMethodOptions: RadioOption<string>[] = [
|
||||
{
|
||||
label: msg("Front-channel (Iframe)"),
|
||||
value: SAMLLogoutMethods.FrontchannelIframe,
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
label: msg("Front-channel (Native)"),
|
||||
value: SAMLLogoutMethods.FrontchannelNative,
|
||||
},
|
||||
{
|
||||
label: msg("Back-channel (POST)"),
|
||||
value: SAMLLogoutMethods.Backchannel,
|
||||
disabled: !hasPostBinding,
|
||||
},
|
||||
];
|
||||
|
||||
return html`<ak-radio-input
|
||||
label=${msg("SLS Binding")}
|
||||
name="slsBinding"
|
||||
@@ -104,7 +121,7 @@ function renderHasSlsUrl(
|
||||
<ak-radio-input
|
||||
label=${msg("Logout Method")}
|
||||
name="logoutMethod"
|
||||
.options=${logoutMethodOptions(hasPostBinding)}
|
||||
.options=${logoutMethodOptions}
|
||||
.value=${logoutMethod}
|
||||
help=${msg("Method to use for logout when SLS URL is configured.")}
|
||||
@change=${setLogoutMethod}
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
DigestAlgorithmEnum,
|
||||
KeyTypeEnum,
|
||||
SAMLBindingsEnum,
|
||||
SAMLLogoutMethods,
|
||||
SignatureAlgorithmEnum,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
@@ -23,38 +22,6 @@ export const spBindingOptions = toOptions([
|
||||
[msg("Post"), SAMLBindingsEnum.Post],
|
||||
]);
|
||||
|
||||
export function logoutMethodLabel(method?: SAMLLogoutMethods | string): string {
|
||||
switch (method) {
|
||||
case SAMLLogoutMethods.FrontchannelIframe:
|
||||
return msg("Front-channel (Iframe)");
|
||||
case SAMLLogoutMethods.FrontchannelNative:
|
||||
return msg("Front-channel (Native)");
|
||||
case SAMLLogoutMethods.Backchannel:
|
||||
return msg("Back-channel (POST)");
|
||||
default:
|
||||
return method ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
export function logoutMethodOptions(hasPostBinding: boolean) {
|
||||
return [
|
||||
{
|
||||
label: logoutMethodLabel(SAMLLogoutMethods.FrontchannelIframe),
|
||||
value: SAMLLogoutMethods.FrontchannelIframe,
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
label: logoutMethodLabel(SAMLLogoutMethods.FrontchannelNative),
|
||||
value: SAMLLogoutMethods.FrontchannelNative,
|
||||
},
|
||||
{
|
||||
label: logoutMethodLabel(SAMLLogoutMethods.Backchannel),
|
||||
value: SAMLLogoutMethods.Backchannel,
|
||||
disabled: !hasPostBinding,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export const digestAlgorithmOptions = toOptions([
|
||||
["SHA1", DigestAlgorithmEnum.HttpWwwW3Org200009Xmldsigsha1],
|
||||
["SHA256", DigestAlgorithmEnum.HttpWwwW3Org200104Xmlencsha256, true],
|
||||
|
||||
@@ -9,8 +9,6 @@ import "#elements/buttons/ActionButton/index";
|
||||
import "#elements/buttons/ModalButton";
|
||||
import "#elements/buttons/SpinnerButton/index";
|
||||
|
||||
import { logoutMethodLabel } from "./SAMLProviderOptions.js";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { EVENT_REFRESH } from "#common/constants";
|
||||
import { MessageLevel } from "#common/messages";
|
||||
@@ -154,13 +152,6 @@ export class SAMLProviderViewPage extends AKElement {
|
||||
}
|
||||
}
|
||||
|
||||
renderLogoutMethod(): string {
|
||||
if (!this.provider?.slsUrl) {
|
||||
return "-";
|
||||
}
|
||||
return logoutMethodLabel(this.provider.logoutMethod) || "-";
|
||||
}
|
||||
|
||||
renderRelatedObjects(): TemplateResult {
|
||||
const relatedObjects = [];
|
||||
if (this.provider?.assignedApplicationName) {
|
||||
@@ -327,18 +318,6 @@ export class SAMLProviderViewPage extends AKElement {
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg(
|
||||
"Audience",
|
||||
)}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.audience || "-"}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg(
|
||||
@@ -354,24 +333,24 @@ export class SAMLProviderViewPage extends AKElement {
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg(
|
||||
"SLS URL",
|
||||
"Audience",
|
||||
)}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.slsUrl || "-"}
|
||||
${this.provider.audience || "-"}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg(
|
||||
"Logout Method",
|
||||
"Issuer",
|
||||
)}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.renderLogoutMethod()}
|
||||
${this.provider.issuerOverride}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@ import { RoleForm } from "#admin/roles/ak-role-form";
|
||||
|
||||
import { RbacApi, Role } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { html, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
@@ -83,7 +83,11 @@ export class RoleListPage extends TablePage<Role> {
|
||||
|
||||
row(item: Role): SlottedTemplateResult[] {
|
||||
return [
|
||||
html`<a href="#/identity/roles/${item.pk}">${item.name}</a>`,
|
||||
html`<a
|
||||
href="#/identity/roles/${item.pk}"
|
||||
aria-label=${msg(str`View details of role "${item.name}"`)}
|
||||
>${item.name}</a
|
||||
>`,
|
||||
html`<div class="ak-c-table__actions">${IconEditButton(RoleForm, item.pk)}</div>`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -14,11 +14,9 @@ import { SlottedTemplateResult } from "#elements/types";
|
||||
import { BaseStageForm } from "#admin/stages/BaseStageForm";
|
||||
import {
|
||||
CAPTCHA_PROVIDERS,
|
||||
CAPTCHA_REQUEST_CONTENT_TYPES,
|
||||
CaptchaProviderKey,
|
||||
CaptchaProviderKeys,
|
||||
CaptchaProviderPreset,
|
||||
deriveCapSiteVerifyURL,
|
||||
detectProviderFromInstance,
|
||||
pluckFormValues,
|
||||
} from "#admin/stages/captcha/shared";
|
||||
@@ -85,15 +83,6 @@ export class CaptchaStageForm extends BaseStageForm<CaptchaStage> {
|
||||
public async send(
|
||||
data: CaptchaStageRequest | PatchedCaptchaStageRequest,
|
||||
): Promise<CaptchaStage> {
|
||||
if (this.selectedProvider === "cap" && data.publicKey) {
|
||||
const presetURL = CAPTCHA_PROVIDERS.cap.apiUrl;
|
||||
// The Cap verification URL includes the site key, so derive it from the
|
||||
// widget endpoint unless the advanced field was explicitly customized.
|
||||
if (!data.apiUrl || data.apiUrl === presetURL) {
|
||||
data.apiUrl = deriveCapSiteVerifyURL(data.publicKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.instance) {
|
||||
return this.#api.stagesCaptchaPartialUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
@@ -151,34 +140,20 @@ export class CaptchaStageForm extends BaseStageForm<CaptchaStage> {
|
||||
}
|
||||
|
||||
protected renderKeyFields(): SlottedTemplateResult {
|
||||
const publicKeyLabel =
|
||||
this.selectedProvider === "cap" ? msg("API Endpoint") : msg("Public Key");
|
||||
const publicKeyPlaceholder =
|
||||
this.selectedProvider === "cap"
|
||||
? msg("https://cap.example.com/site-key/")
|
||||
: msg("Paste your CAPTCHA public key...");
|
||||
const publicKeyHelp =
|
||||
this.selectedProvider === "cap"
|
||||
? msg("The public Cap endpoint used by the widget, including the site key path.", {
|
||||
id: "captcha.cap-endpoint.description",
|
||||
desc: "Description for Cap endpoint field.",
|
||||
})
|
||||
: msg("The public key is used by authentik to render the CAPTCHA widget.", {
|
||||
id: "captcha.public-key.description",
|
||||
desc: "Description for CAPTCHA public key field.",
|
||||
});
|
||||
|
||||
return html`
|
||||
<ak-text-input
|
||||
label=${publicKeyLabel}
|
||||
label=${msg("Public Key")}
|
||||
required
|
||||
name="publicKey"
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.publicKey || "")}"
|
||||
autocomplete="off"
|
||||
input-hint="code"
|
||||
placeholder=${publicKeyPlaceholder}
|
||||
help=${publicKeyHelp}
|
||||
placeholder=${msg("Paste your CAPTCHA public key...")}
|
||||
help=${msg("The public key is used by authentik to render the CAPTCHA widget.", {
|
||||
id: "captcha.public-key.description",
|
||||
desc: "Description for CAPTCHA public key field.",
|
||||
})}
|
||||
>
|
||||
</ak-text-input>
|
||||
|
||||
@@ -271,35 +246,10 @@ export class CaptchaStageForm extends BaseStageForm<CaptchaStage> {
|
||||
type="url"
|
||||
value="${ifDefined(formValues.apiUrl)}"
|
||||
required
|
||||
help=${this.selectedProvider === "cap"
|
||||
? msg(
|
||||
"Cap's server-side verification endpoint, for example https://cap.example.com/site-key/siteverify.",
|
||||
)
|
||||
: msg(
|
||||
"URL used to validate CAPTCHA response on the backend. Automatically set based on provider selection but can be customized.",
|
||||
)}
|
||||
help=${msg(
|
||||
"URL used to validate CAPTCHA response on the backend. Automatically set based on provider selection but can be customized.",
|
||||
)}
|
||||
></ak-text-input>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Request Content Type")}
|
||||
name="requestContentType"
|
||||
>
|
||||
<select class="pf-c-form-control" name="requestContentType">
|
||||
${CAPTCHA_REQUEST_CONTENT_TYPES.map(
|
||||
(type) =>
|
||||
html`<option
|
||||
value=${type.value}
|
||||
?selected=${type.value === formValues.requestContentType}
|
||||
>
|
||||
${type.formatDisplayName()}
|
||||
</option>`,
|
||||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Content-Type used for server-side verification. Cap requires JSON; most other providers use form-encoded requests.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>`;
|
||||
}
|
||||
|
||||
@@ -2,35 +2,12 @@ import { CaptchaStage, CaptchaStageRequest } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
|
||||
export type CaptchaRequestContentType = "application/x-www-form-urlencoded" | "application/json";
|
||||
|
||||
export const CAPTCHA_REQUEST_CONTENT_TYPES = [
|
||||
{
|
||||
value: "application/x-www-form-urlencoded",
|
||||
formatDisplayName: () =>
|
||||
msg("Form encoded", {
|
||||
id: "captcha.request-content-type.form",
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: "application/json",
|
||||
formatDisplayName: () =>
|
||||
msg("JSON", {
|
||||
id: "captcha.request-content-type.json",
|
||||
}),
|
||||
},
|
||||
] as const satisfies {
|
||||
value: CaptchaRequestContentType;
|
||||
formatDisplayName: () => string;
|
||||
}[];
|
||||
|
||||
export const CaptchaProviderKeys = [
|
||||
"recaptcha_v2",
|
||||
"recaptcha_v3",
|
||||
"recaptcha_enterprise",
|
||||
"hcaptcha",
|
||||
"turnstile",
|
||||
"cap",
|
||||
"custom",
|
||||
] as const satisfies string[];
|
||||
|
||||
@@ -40,7 +17,6 @@ export interface CaptchaProviderPreset {
|
||||
formatDisplayName: () => string;
|
||||
jsUrl: string;
|
||||
apiUrl: string;
|
||||
requestContentType: CaptchaRequestContentType;
|
||||
interactive: boolean;
|
||||
supportsScore: boolean;
|
||||
score?: { min: number; max: number };
|
||||
@@ -61,7 +37,6 @@ export const CAPTCHA_PROVIDERS = {
|
||||
}),
|
||||
jsUrl: "https://www.recaptcha.net/recaptcha/api.js",
|
||||
apiUrl: "https://www.recaptcha.net/recaptcha/api/siteverify",
|
||||
requestContentType: "application/x-www-form-urlencoded",
|
||||
interactive: true,
|
||||
supportsScore: false,
|
||||
formatAPISource: () =>
|
||||
@@ -77,7 +52,6 @@ export const CAPTCHA_PROVIDERS = {
|
||||
}),
|
||||
jsUrl: "https://www.recaptcha.net/recaptcha/api.js",
|
||||
apiUrl: "https://www.recaptcha.net/recaptcha/api/siteverify",
|
||||
requestContentType: "application/x-www-form-urlencoded",
|
||||
interactive: false,
|
||||
supportsScore: true,
|
||||
score: { min: 0.5, max: 1.0 },
|
||||
@@ -94,7 +68,6 @@ export const CAPTCHA_PROVIDERS = {
|
||||
}),
|
||||
jsUrl: "https://www.recaptcha.net/recaptcha/enterprise.js",
|
||||
apiUrl: "https://www.recaptcha.net/recaptcha/api/siteverify",
|
||||
requestContentType: "application/x-www-form-urlencoded",
|
||||
interactive: false,
|
||||
supportsScore: true,
|
||||
score: { min: 0.5, max: 1.0 },
|
||||
@@ -111,7 +84,6 @@ export const CAPTCHA_PROVIDERS = {
|
||||
}),
|
||||
jsUrl: "https://js.hcaptcha.com/1/api.js",
|
||||
apiUrl: "https://api.hcaptcha.com/siteverify",
|
||||
requestContentType: "application/x-www-form-urlencoded",
|
||||
interactive: true,
|
||||
supportsScore: true,
|
||||
score: { min: 0.0, max: 0.5 },
|
||||
@@ -128,7 +100,6 @@ export const CAPTCHA_PROVIDERS = {
|
||||
}),
|
||||
jsUrl: "https://challenges.cloudflare.com/turnstile/v0/api.js",
|
||||
apiUrl: "https://challenges.cloudflare.com/turnstile/v0/siteverify",
|
||||
requestContentType: "application/x-www-form-urlencoded",
|
||||
interactive: true,
|
||||
supportsScore: false,
|
||||
formatAPISource: () =>
|
||||
@@ -137,22 +108,6 @@ export const CAPTCHA_PROVIDERS = {
|
||||
}),
|
||||
keyURL: "https://dash.cloudflare.com",
|
||||
},
|
||||
cap: {
|
||||
formatDisplayName: () =>
|
||||
msg("Cap", {
|
||||
id: "captcha.providers.cap",
|
||||
}),
|
||||
jsUrl: "https://cdn.jsdelivr.net/npm/cap-widget",
|
||||
apiUrl: "https://cap.example.com/site-key/siteverify",
|
||||
requestContentType: "application/json",
|
||||
interactive: true,
|
||||
supportsScore: false,
|
||||
formatAPISource: () =>
|
||||
msg("Cap setup guide", {
|
||||
id: "captcha.providers.cap.setup-guide",
|
||||
}),
|
||||
keyURL: "https://capjs.js.org/guide/",
|
||||
},
|
||||
custom: {
|
||||
formatDisplayName: () =>
|
||||
msg("Custom", {
|
||||
@@ -160,19 +115,12 @@ export const CAPTCHA_PROVIDERS = {
|
||||
}),
|
||||
jsUrl: "https://www.recaptcha.net/recaptcha/api.js",
|
||||
apiUrl: "https://www.recaptcha.net/recaptcha/api/siteverify",
|
||||
requestContentType: "application/x-www-form-urlencoded",
|
||||
interactive: false,
|
||||
supportsScore: true,
|
||||
score: { min: 0.5, max: 1.0 },
|
||||
},
|
||||
} as const satisfies Record<CaptchaProviderKey, CaptchaProviderPreset>;
|
||||
|
||||
export function deriveCapSiteVerifyURL(endpoint: string): string {
|
||||
const normalizedEndpoint = endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
||||
|
||||
return new URL("siteverify", normalizedEndpoint).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect which provider preset matches the given {@linkcode CaptchaStage} instance.
|
||||
* This allows the form to show the correct provider in the dropdown when editing
|
||||
@@ -184,14 +132,6 @@ export function detectProviderFromInstance(stage?: CaptchaStage | null): Captcha
|
||||
for (const key of CaptchaProviderKeys) {
|
||||
const preset = CAPTCHA_PROVIDERS[key];
|
||||
|
||||
if (
|
||||
key === "cap" &&
|
||||
stage.jsUrl === preset.jsUrl &&
|
||||
stage.requestContentType === preset.requestContentType
|
||||
) {
|
||||
return key;
|
||||
}
|
||||
|
||||
if (stage.jsUrl === preset.jsUrl && stage.apiUrl === preset.apiUrl) {
|
||||
return key;
|
||||
}
|
||||
@@ -213,7 +153,6 @@ export function pluckFormValues(
|
||||
return {
|
||||
jsUrl: instance.jsUrl,
|
||||
apiUrl: instance.apiUrl,
|
||||
requestContentType: instance.requestContentType,
|
||||
interactive: instance.interactive,
|
||||
scoreMinThreshold: instance.scoreMinThreshold,
|
||||
scoreMaxThreshold: instance.scoreMaxThreshold,
|
||||
@@ -224,7 +163,6 @@ export function pluckFormValues(
|
||||
return {
|
||||
jsUrl: preset.jsUrl,
|
||||
apiUrl: preset.apiUrl,
|
||||
requestContentType: preset.requestContentType,
|
||||
interactive: preset.interactive,
|
||||
scoreMinThreshold: preset.score?.min ?? 0.5,
|
||||
scoreMaxThreshold: preset.score?.max ?? 1.0,
|
||||
|
||||
@@ -7,7 +7,6 @@ import "#components/ak-radio-input";
|
||||
import "#components/ak-switch-input";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { DefaultUIConfig } from "#common/ui/config";
|
||||
|
||||
import { ModelForm } from "#elements/forms/ModelForm";
|
||||
import { RadioOption } from "#elements/forms/Radio";
|
||||
@@ -59,8 +58,8 @@ export class UserForm extends ModelForm<User, number> {
|
||||
@property({ attribute: false })
|
||||
public targetRole: Role | null = null;
|
||||
|
||||
@property({ type: String, attribute: "default-path", useDefault: true })
|
||||
public defaultPath: string = DefaultUIConfig.defaults.userPath;
|
||||
@property({ type: String, attribute: "default-path" })
|
||||
public defaultPath: string = "users";
|
||||
|
||||
@property({ attribute: false })
|
||||
public userType: UserTypeEnum | null = null;
|
||||
|
||||
@@ -91,15 +91,15 @@ export class UserListPage extends WithLicenseSummary(
|
||||
public override searchPlaceholder = msg("Search by username, email, etc...");
|
||||
public override searchLabel = msg("User Search");
|
||||
|
||||
public override pageTitle = msg("Users");
|
||||
public override pageDescription = "";
|
||||
public override pageIcon = "pf-icon pf-icon-user";
|
||||
public pageTitle = msg("Users");
|
||||
public pageDescription = "";
|
||||
public pageIcon = "pf-icon pf-icon-user";
|
||||
|
||||
@property({ type: String })
|
||||
public order = "-last_login";
|
||||
|
||||
@property({ type: String, useDefault: true })
|
||||
public activePath: string = DefaultUIConfig.defaults.userPath;
|
||||
@property({ type: String })
|
||||
public activePath: string;
|
||||
|
||||
@state()
|
||||
protected hideDeactivated = getURLParam<boolean>("hideDeactivated", false);
|
||||
@@ -107,23 +107,27 @@ export class UserListPage extends WithLicenseSummary(
|
||||
@state()
|
||||
protected userPaths: UserPath | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
const defaultPath = DefaultUIConfig.defaults.userPath;
|
||||
|
||||
this.activePath = getURLParam<string>("path", defaultPath);
|
||||
|
||||
if (this.uiConfig.defaults.userPath !== defaultPath) {
|
||||
this.activePath = this.uiConfig.defaults.userPath;
|
||||
}
|
||||
}
|
||||
|
||||
protected canImpersonate = false;
|
||||
|
||||
public override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
this.canImpersonate = this.can(CapabilitiesEnum.CanImpersonate);
|
||||
|
||||
const initialDefaultUserPath = DefaultUIConfig.defaults.userPath;
|
||||
const brandDefaultUserPath = this.uiConfig.defaults.userPath;
|
||||
|
||||
this.activePath = getURLParam<string>(
|
||||
"path",
|
||||
brandDefaultUserPath || initialDefaultUserPath,
|
||||
);
|
||||
}
|
||||
|
||||
protected override async apiEndpoint(): Promise<PaginatedResponse<User>> {
|
||||
async apiEndpoint(): Promise<PaginatedResponse<User>> {
|
||||
const users = await this.#api.coreUsersList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
pathStartswith: this.activePath,
|
||||
@@ -138,18 +142,6 @@ export class UserListPage extends WithLicenseSummary(
|
||||
return users;
|
||||
}
|
||||
|
||||
protected buildExportParams = async (): Promise<CoreUsersExportCreateRequest> => {
|
||||
return {
|
||||
...(await this.defaultEndpointConfig()),
|
||||
pathStartswith: this.activePath,
|
||||
isActive: this.hideDeactivated ? true : undefined,
|
||||
};
|
||||
};
|
||||
|
||||
protected createExport = (params: CoreUsersExportCreateRequest) => {
|
||||
return this.#api.coreUsersExportCreate(params);
|
||||
};
|
||||
|
||||
protected override rowLabel(item: User): string {
|
||||
if (item.name) {
|
||||
return msg(str`${item.username} (${item.name})`);
|
||||
@@ -167,8 +159,6 @@ export class UserListPage extends WithLicenseSummary(
|
||||
[msg("Actions"), null, msg("Row Actions")],
|
||||
];
|
||||
|
||||
//#region Renderering
|
||||
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
const { currentUser, originalUser } = this;
|
||||
@@ -259,7 +249,7 @@ export class UserListPage extends WithLicenseSummary(
|
||||
</div>`;
|
||||
}
|
||||
|
||||
protected override row(item: User): SlottedTemplateResult[] {
|
||||
protected row(item: User) {
|
||||
const { currentUser } = this;
|
||||
|
||||
const showImpersonation = this.canImpersonate && currentUser && item.pk !== currentUser.pk;
|
||||
@@ -304,7 +294,7 @@ export class UserListPage extends WithLicenseSummary(
|
||||
];
|
||||
}
|
||||
|
||||
protected override renderExpanded(item: User): SlottedTemplateResult {
|
||||
renderExpanded(item: User): TemplateResult {
|
||||
return html`<dl class="pf-c-description-list pf-m-horizontal">
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
@@ -345,6 +335,18 @@ export class UserListPage extends WithLicenseSummary(
|
||||
</dl>`;
|
||||
}
|
||||
|
||||
protected buildExportParams = async () => {
|
||||
return {
|
||||
...(await this.defaultEndpointConfig()),
|
||||
pathStartswith: this.activePath,
|
||||
isActive: this.hideDeactivated ? true : undefined,
|
||||
};
|
||||
};
|
||||
|
||||
protected createExport = (params: CoreUsersExportCreateRequest) => {
|
||||
return this.#api.coreUsersExportCreate(params);
|
||||
};
|
||||
|
||||
protected renderObjectCreate(): SlottedTemplateResult {
|
||||
const { activePath } = this;
|
||||
|
||||
@@ -368,7 +370,7 @@ export class UserListPage extends WithLicenseSummary(
|
||||
});
|
||||
}
|
||||
|
||||
protected renderSidebarBefore(): SlottedTemplateResult {
|
||||
protected renderSidebarBefore(): TemplateResult {
|
||||
return html`<aside aria-labelledby="sidebar-left-panel-header" class="pf-c-sidebar__panel">
|
||||
<div class="pf-c-card tree">
|
||||
<div
|
||||
@@ -392,8 +394,6 @@ export class UserListPage extends WithLicenseSummary(
|
||||
</div>
|
||||
</aside>`;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -6,8 +6,6 @@ import "#elements/wizard/FormWizardPage";
|
||||
import "#elements/wizard/TypeCreateWizardPage";
|
||||
import "#elements/wizard/Wizard";
|
||||
|
||||
import { DefaultUIConfig } from "#common/ui/config";
|
||||
|
||||
import { LitPropertyRecord, SlottedTemplateResult } from "#elements/types";
|
||||
import { CreateWizard } from "#elements/wizard/CreateWizard";
|
||||
import { TypeCreateWizardPageLayouts } from "#elements/wizard/TypeCreateWizardPage";
|
||||
@@ -123,8 +121,8 @@ export class AKUserWizard extends CreateWizard {
|
||||
/**
|
||||
* Default path to assign to new users created via the wizard.
|
||||
*/
|
||||
@property({ type: String, attribute: "default-path", useDefault: true })
|
||||
public defaultPath: string = DefaultUIConfig.defaults.userPath;
|
||||
@property({ type: String, attribute: "default-path" })
|
||||
public defaultPath: string = "users";
|
||||
|
||||
protected apiEndpoint(): Promise<TypeCreate[]> {
|
||||
return Promise.resolve(DEFAULT_USER_TYPES);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import "#flow/FormStatic";
|
||||
import "#flow/components/ak-flow-card";
|
||||
|
||||
import { globalAK } from "#common/global";
|
||||
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import { FlowUserDetails } from "#flow/FormStatic";
|
||||
@@ -22,19 +24,6 @@ import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
export class SessionEnd extends BaseStage<SessionEndChallenge, unknown> {
|
||||
static styles: CSSResult[] = [PFLogin, PFForm, PFFormControl, PFTitle, PFButton];
|
||||
|
||||
getText(challenge: SessionEndChallenge) {
|
||||
if (challenge.overviewUrl && challenge.invalidationFlowUrl) {
|
||||
return msg(
|
||||
str`You've logged out of ${challenge.applicationName}. You can go back to the overview to launch another application, or log out of your authentik account.`,
|
||||
);
|
||||
} else if (challenge.invalidationFlowUrl) {
|
||||
return msg(
|
||||
str`You've logged out of ${challenge.applicationName}. You can log out of your authentik account.`,
|
||||
);
|
||||
}
|
||||
return msg(str`You've logged out of ${challenge.applicationName}.`);
|
||||
}
|
||||
|
||||
protected render(): SlottedTemplateResult {
|
||||
const { challenge } = this;
|
||||
|
||||
@@ -42,16 +31,18 @@ export class SessionEnd extends BaseStage<SessionEndChallenge, unknown> {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`<ak-flow-card .challenge=${challenge}>
|
||||
return html`<ak-flow-card .challenge=${this.challenge}>
|
||||
<form class="pf-c-form">
|
||||
${FlowUserDetails({ challenge: challenge })}
|
||||
${FlowUserDetails({ challenge: this.challenge })}
|
||||
|
||||
<p>${this.getText(challenge)}</p>
|
||||
${challenge.overviewUrl
|
||||
? html`<a href="${challenge.overviewUrl}" class="pf-c-button pf-m-primary">
|
||||
${msg("Go back to overview")}
|
||||
</a>`
|
||||
: nothing}
|
||||
<p>
|
||||
${msg(
|
||||
str`You've logged out of ${challenge.applicationName}. You can go back to the overview to launch another application, or log out of your authentik account.`,
|
||||
)}
|
||||
</p>
|
||||
<a href="${globalAK().api.base}" class="pf-c-button pf-m-primary">
|
||||
${msg("Go back to overview")}
|
||||
</a>
|
||||
${challenge.invalidationFlowUrl
|
||||
? html`
|
||||
<a
|
||||
|
||||
@@ -36,9 +36,4 @@ ak-stage-captcha[theme="dark"].style-scope {
|
||||
background-color: var(--captcha-background-from);
|
||||
animation: captcha-background-animation 1s infinite var(--pf-global--TimingFunction);
|
||||
}
|
||||
|
||||
&[data-transparent-loading="true"][data-ready="loading"] {
|
||||
background-color: transparent;
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import { AKFormErrors, ErrorProp } from "#components/ak-field-errors";
|
||||
import { FlowUserDetails } from "#flow/FormStatic";
|
||||
import { BaseStage } from "#flow/stages/base";
|
||||
import Styles from "#flow/stages/captcha/CaptchaStage.css";
|
||||
import { CapController } from "#flow/stages/captcha/controllers/cap";
|
||||
import {
|
||||
CaptchaController,
|
||||
CaptchaControllerConstructor,
|
||||
@@ -54,14 +53,7 @@ interface LoadMessage {
|
||||
message: "load";
|
||||
}
|
||||
|
||||
interface ErrorMessage {
|
||||
source?: string;
|
||||
context?: string;
|
||||
message: "error";
|
||||
error: string;
|
||||
}
|
||||
|
||||
type IframeMessageEvent = MessageEvent<CaptchaMessage | LoadMessage | ErrorMessage>;
|
||||
type IframeMessageEvent = MessageEvent<CaptchaMessage | LoadMessage>;
|
||||
|
||||
@customElement("ak-stage-captcha")
|
||||
export class CaptchaStage
|
||||
@@ -87,7 +79,6 @@ export class CaptchaStage
|
||||
HCaptchaController,
|
||||
GReCaptchaController,
|
||||
TurnstileController,
|
||||
CapController,
|
||||
]);
|
||||
|
||||
#logger = ConsoleLogger.prefix("flow:captcha");
|
||||
@@ -174,9 +165,6 @@ export class CaptchaStage
|
||||
return match(data)
|
||||
.with({ message: "captcha" }, ({ token }) => this.onTokenChange(token))
|
||||
.with({ message: "load" }, this.#loadListener)
|
||||
.with({ message: "error" }, ({ error }) => {
|
||||
this.error = error;
|
||||
})
|
||||
.otherwise(({ message }) => {
|
||||
this.#logger.debug(`Unknown message: ${message}`);
|
||||
});
|
||||
@@ -195,17 +183,12 @@ export class CaptchaStage
|
||||
}
|
||||
|
||||
if (this.challenge?.interactive) {
|
||||
// Cap renders its own framed widget, so the generic iframe loading shimmer looks like
|
||||
// an extra CAPTCHA box flashing behind it.
|
||||
const isCapChallenge = this.challenge.jsUrl.includes("cap-widget");
|
||||
|
||||
return html`
|
||||
<iframe
|
||||
aria-label=${msg("CAPTCHA challenge")}
|
||||
${ref(this.iframeRef)}
|
||||
style="height: ${this.iframeHeight}px;"
|
||||
data-ready=${this.#iframeLoaded ? "ready" : "loading"}
|
||||
data-transparent-loading=${isCapChallenge ? "true" : "false"}
|
||||
class="ak-interactive-challenge"
|
||||
id="ak-captcha"
|
||||
></iframe>
|
||||
@@ -323,13 +306,8 @@ export class CaptchaStage
|
||||
|
||||
// Then, load the new script...
|
||||
const scriptElement = document.createElement("script");
|
||||
const matchedController = Array.from(CaptchaStage.controllers).find((Controller) =>
|
||||
Controller.matchesURL(challengeURL),
|
||||
);
|
||||
|
||||
scriptElement.src = challengeURL.toString();
|
||||
scriptElement.type =
|
||||
matchedController?.scriptType === "module" ? "module" : "text/javascript";
|
||||
scriptElement.async = true;
|
||||
scriptElement.defer = true;
|
||||
scriptElement.onload = this.#scriptLoadListener;
|
||||
@@ -552,7 +530,6 @@ export class CaptchaStage
|
||||
challengeURL: challengeURL.toString(),
|
||||
theme: this.activeTheme,
|
||||
scriptOnLoad: !(controller instanceof TurnstileController),
|
||||
scriptType: controller.scriptType,
|
||||
});
|
||||
|
||||
if (
|
||||
|
||||
@@ -28,20 +28,6 @@ export abstract class CaptchaController implements ReactiveController {
|
||||
return (this.constructor as typeof CaptchaController).globalName;
|
||||
}
|
||||
|
||||
public static readonly scriptType: "classic" | "module" = "classic";
|
||||
|
||||
public get scriptType(): "classic" | "module" {
|
||||
return (this.constructor as typeof CaptchaController).scriptType;
|
||||
}
|
||||
|
||||
public static isAvailable(): boolean {
|
||||
return Object.hasOwn(window, this.globalName);
|
||||
}
|
||||
|
||||
public static matchesURL(_url: URL): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A prefix for log messages from this controller.
|
||||
*/
|
||||
@@ -56,7 +42,7 @@ export abstract class CaptchaController implements ReactiveController {
|
||||
): Array<CaptchaControllerConstructor | undefined> {
|
||||
return Array.from(controllerConstructors).filter((Controller) => {
|
||||
// Can we find the global for this captcha provider?
|
||||
return Controller.isAvailable();
|
||||
return Object.hasOwn(window, Controller.globalName);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -112,9 +98,6 @@ export abstract class CaptchaController implements ReactiveController {
|
||||
|
||||
export type CaptchaControllerConstructor = {
|
||||
globalName: string;
|
||||
scriptType: "classic" | "module";
|
||||
isAvailable: () => boolean;
|
||||
matchesURL: (url: URL) => boolean;
|
||||
} & (new (host: CaptchaHandlerHost) => CaptchaController);
|
||||
|
||||
export interface CaptchaHandlerHost extends ReactiveControllerHost {
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import { CaptchaController } from "#flow/stages/captcha/controllers/CaptchaController";
|
||||
|
||||
import { html } from "lit";
|
||||
|
||||
export class CapController extends CaptchaController {
|
||||
public static readonly globalName = "cap-widget";
|
||||
|
||||
public static readonly scriptType = "module";
|
||||
|
||||
public static override isAvailable(): boolean {
|
||||
return customElements.get("cap-widget") !== undefined;
|
||||
}
|
||||
|
||||
public static override matchesURL(url: URL): boolean {
|
||||
return url.pathname.includes("cap-widget");
|
||||
}
|
||||
|
||||
public interactive = () => {
|
||||
const endpoint = this.host.challenge?.siteKey ?? "";
|
||||
|
||||
return html`<div id="ak-container" class="cap-container">
|
||||
<cap-widget
|
||||
id="ak-cap-widget"
|
||||
required
|
||||
data-cap-api-endpoint=${endpoint}
|
||||
></cap-widget>
|
||||
</div>
|
||||
<script>
|
||||
const widget = document.getElementById("ak-cap-widget");
|
||||
|
||||
widget.addEventListener("solve", (event) => {
|
||||
callback(event.detail.token);
|
||||
});
|
||||
|
||||
widget.addEventListener("error", (event) => {
|
||||
self.parent.postMessage({
|
||||
message: "error",
|
||||
source: "goauthentik.io",
|
||||
context: "flow-executor",
|
||||
error: event.detail.message,
|
||||
});
|
||||
});
|
||||
</script>`;
|
||||
};
|
||||
|
||||
public refreshInteractive = async () => {
|
||||
this.host.iframeRef.value?.contentWindow?.location.reload();
|
||||
};
|
||||
|
||||
public execute = async () => {
|
||||
throw new Error("Cap requires interactive mode.");
|
||||
};
|
||||
|
||||
public refresh = async () => {
|
||||
throw new Error("Cap requires interactive mode.");
|
||||
};
|
||||
}
|
||||
@@ -30,7 +30,6 @@ export interface IFrameTemplateInit {
|
||||
* Defaults to `true`.
|
||||
*/
|
||||
scriptOnLoad?: boolean;
|
||||
scriptType?: "classic" | "module";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +42,7 @@ export interface IFrameTemplateInit {
|
||||
*/
|
||||
export function iframeTemplate(
|
||||
children: TemplateResult,
|
||||
{ challengeURL, theme, scriptOnLoad = true, scriptType = "classic" }: IFrameTemplateInit,
|
||||
{ challengeURL, theme, scriptOnLoad = true }: IFrameTemplateInit,
|
||||
) {
|
||||
return createDocumentTemplate({
|
||||
head: html`
|
||||
@@ -76,7 +75,7 @@ export function iframeTemplate(
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
background: transparent;
|
||||
background: ${ThemeColor[theme]};
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -89,23 +88,15 @@ export function iframeTemplate(
|
||||
}
|
||||
|
||||
.g-recaptcha,
|
||||
.h-captcha,
|
||||
.cap-container {
|
||||
.h-captcha {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.cap-container {
|
||||
box-sizing: border-box;
|
||||
padding-block: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
${children}
|
||||
<script
|
||||
${scriptOnLoad ? 'onload="loadListener()"' : ""}
|
||||
${scriptType === "module" ? 'type="module"' : ""}
|
||||
src="${challengeURL.toString()}"
|
||||
></script>
|
||||
`,
|
||||
|
||||
@@ -49,12 +49,6 @@ export class CaptchaDisplayController implements ReactiveController {
|
||||
const input = this.#inputRef.value;
|
||||
if (!input) return;
|
||||
input.value = token;
|
||||
// The surrounding identification form only updates its validity when form controls
|
||||
// emit normal input events, so mirror a user's field change after the CAPTCHA solves.
|
||||
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
||||
input.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
||||
this.#loaded = true;
|
||||
this.host.requestUpdate();
|
||||
};
|
||||
|
||||
public onFailure() {
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
version: 1
|
||||
metadata:
|
||||
name: Playwright e2e test admin
|
||||
entries:
|
||||
- attrs:
|
||||
- model: authentik_core.group
|
||||
state: present
|
||||
identifiers:
|
||||
name: "authentik Admins"
|
||||
attrs:
|
||||
is_superuser: true
|
||||
id: admin-group
|
||||
- model: authentik_core.user
|
||||
state: present
|
||||
identifiers:
|
||||
username: akadmin
|
||||
attrs:
|
||||
email: test-admin@goauthentik.io
|
||||
is_active: true
|
||||
name: authentik Default Admin
|
||||
@@ -8,9 +21,4 @@ entries:
|
||||
path: users
|
||||
type: internal
|
||||
groups:
|
||||
- !Find [authentik_core.group, [name, "authentik Admins"]]
|
||||
conditions: []
|
||||
identifiers:
|
||||
username: akadmin
|
||||
model: authentik_core.user
|
||||
state: present
|
||||
- !KeyOf admin-group
|
||||
|
||||
@@ -17,8 +17,6 @@ test.describe("Session Lifecycle", () => {
|
||||
test.beforeAll(
|
||||
'Ensure "Enable Remember me on this device" is on for the default identification stage',
|
||||
async ({ browser }, { title: testName }) => {
|
||||
if (Date.now()) return;
|
||||
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const navigator = new NavigatorFixture(page, testName);
|
||||
@@ -161,8 +161,13 @@ test.describe("Roles", () => {
|
||||
});
|
||||
|
||||
await test.step("Verify role name updated on view page", async () => {
|
||||
// The role name shows up in both the page-navbar heading (as
|
||||
// "Role <name>") and the description-list text (raw <name>); a
|
||||
// bare getByText match hits both and triggers strict-mode. Pin
|
||||
// to the description list so we're asserting the canonical
|
||||
// value the edit just wrote, not the heading template.
|
||||
await expect(
|
||||
page.getByText(updatedName),
|
||||
page.getByText(updatedName, { exact: true }),
|
||||
"Updated role name is visible on view page",
|
||||
).toBeVisible();
|
||||
});
|
||||
@@ -17,7 +17,15 @@ test.describe("Provider Wizard", () => {
|
||||
|
||||
const dialog = page.getByRole("dialog", { name: "New Provider Wizard" });
|
||||
|
||||
await test.step("Authenticate", async () => session.login());
|
||||
// session.login() with no `to` waits for the auth flow URL pattern to
|
||||
// re-appear, which it does immediately (we just navigated there), so
|
||||
// the helper returns *before* the post-login redirect lands. The
|
||||
// wizard buttons probed below live on /if/admin/#/core/providers, so
|
||||
// pin the destination explicitly — same shape as the other test files.
|
||||
await test.step("Authenticate", async () =>
|
||||
session.login({
|
||||
to: "/if/admin/#/core/providers",
|
||||
}));
|
||||
|
||||
await test.step("Navigate to provider wizard", async () => {
|
||||
await expect(dialog, "Dialog is initially closed").toBeHidden();
|
||||
@@ -3,9 +3,14 @@ import { expect, test as setup } from "#e2e";
|
||||
setup("Web server availability", async ({ baseURL }) => {
|
||||
expect(baseURL, "Base URL is set").toBeTruthy();
|
||||
|
||||
const ok = await fetch(baseURL!)
|
||||
// Probe the default authentication flow rather than the root URL — the root
|
||||
// redirects to /setup when AUTHENTIK_BOOTSTRAP_* env vars are unset, and
|
||||
// /setup raises FlowNonApplicableException (HTTP 500) once an admin user
|
||||
// already has a usable password (as our test-admin blueprint installs).
|
||||
const probeURL = new URL("/if/flow/default-authentication-flow/", baseURL!);
|
||||
const ok = await fetch(probeURL)
|
||||
.then((res) => res.ok)
|
||||
.catch(() => false);
|
||||
|
||||
expect(ok, `Web server should be listening on ${baseURL}`).toBeTruthy();
|
||||
expect(ok, `Web server should be serving ${probeURL}`).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -2349,6 +2349,10 @@
|
||||
<trans-unit id="s24d7ac2fc280ca5e">
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
<target>OAuth</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
</trans-unit>
|
||||
@@ -11199,36 +11203,6 @@ Vazby na skupiny/uživatele jsou kontrolovány vůči uživateli události.</tar
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2371,6 +2371,10 @@
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
<target>SCIM-Anfragen mit einem statischen Token authentifizieren.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
<target>OAuth</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
<target>SCIM-Anfragen per OAuth authentifizieren.</target>
|
||||
@@ -11231,36 +11235,6 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -1808,6 +1808,9 @@
|
||||
<trans-unit id="s24d7ac2fc280ca5e">
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
</trans-unit>
|
||||
@@ -9221,36 +9224,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2326,6 +2326,9 @@ Si se deja vacío, AuthnContextClassRef se establecerá según los métodos de a
|
||||
<trans-unit id="s24d7ac2fc280ca5e">
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
</trans-unit>
|
||||
@@ -11157,36 +11160,6 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2415,6 +2415,10 @@
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
<target>Todenna SCIM-pyynnöt staattisella tunnisteella.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
<target>OAuth</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
<target>Todenna SCIM-pyynnöt OAuthilla.</target>
|
||||
@@ -11397,36 +11401,6 @@ Liitokset käyttäjiin/ryhmiin tarkistetaan tapahtuman käyttäjästä.</target>
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2412,6 +2412,10 @@
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
<target>Authentifier les requêtes SCIM avec un jeton statique.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
<target>OAuth</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
<target>Authentifier les requêtes SCIM avec OAuth.</target>
|
||||
@@ -11386,36 +11390,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2293,6 +2293,9 @@
|
||||
<trans-unit id="s24d7ac2fc280ca5e">
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
</trans-unit>
|
||||
@@ -11106,36 +11109,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2410,6 +2410,10 @@
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
<target>静的トークンを使用してSCIMリクエストを認証します。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
<target>OAuth</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
<target>OAuthを使用してSCIMリクエストを認証します。</target>
|
||||
@@ -11387,36 +11391,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2235,6 +2235,9 @@
|
||||
<trans-unit id="s24d7ac2fc280ca5e">
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
</trans-unit>
|
||||
@@ -10758,36 +10761,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2147,6 +2147,9 @@
|
||||
<trans-unit id="s24d7ac2fc280ca5e">
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
</trans-unit>
|
||||
@@ -10443,36 +10446,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2234,6 +2234,9 @@
|
||||
<trans-unit id="s24d7ac2fc280ca5e">
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
</trans-unit>
|
||||
@@ -10782,36 +10785,6 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2414,6 +2414,10 @@
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
<target>Autenticar solicitações SCIM usando um token estático.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
<target>OAuth</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
<target>Autenticar solicitações SCIM usando OAuth.</target>
|
||||
@@ -11379,36 +11383,6 @@ por exemplo: <x id="0" equiv-text="<code>"/>oci://registry.domain.tld/path
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2255,6 +2255,9 @@
|
||||
<trans-unit id="s24d7ac2fc280ca5e">
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
</trans-unit>
|
||||
@@ -10868,36 +10871,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2240,6 +2240,9 @@
|
||||
<trans-unit id="s24d7ac2fc280ca5e">
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
</trans-unit>
|
||||
@@ -10858,36 +10861,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2409,6 +2409,10 @@
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
<target>使用静态令牌验证 SCIM 请求。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
<target>OAuth</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
<target>使用 OAuth 验证 SCIM 请求</target>
|
||||
@@ -11681,36 +11685,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2166,6 +2166,9 @@
|
||||
<trans-unit id="s24d7ac2fc280ca5e">
|
||||
<source>Authenticate SCIM requests using a static token.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e1641692e0dac94">
|
||||
<source>OAuth</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c34cb0a4cccc041">
|
||||
<source>Authenticate SCIM requests using OAuth.</source>
|
||||
</trans-unit>
|
||||
@@ -10495,36 +10498,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s3162a5abea92514e">
|
||||
<source>View Credentials</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7cbd6ddbb3cdbda4">
|
||||
<source>OAuth (Silent)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf74719393c9d600b">
|
||||
<source>OAuth (Interactive)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s364366c872ccd349">
|
||||
<source>Authenticate SCIM requests using OAuth, interactively authorized.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s67dbb822a17bf6fc">
|
||||
<source>OAuth Token last updated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb586db2f11959745">
|
||||
<source>OAuth Token expires</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4011de0d7365d106">
|
||||
<source>OAuth Status</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s169a1a1dce3987b8">
|
||||
<source>Authenticated</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s02e7dfce69646f50">
|
||||
<source>No token saved</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde2c41117de5db9d">
|
||||
<source>(Re-)authenticate</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s13c4adf0d185f12e">
|
||||
<source>OAuth Callback URL</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: Captcha stage
|
||||
---
|
||||
|
||||
The Captcha stage adds CAPTCHA verification to a flow by using Google reCAPTCHA or compatible alternatives like hCaptcha, Cloudflare Turnstile, and Cap.
|
||||
The Captcha stage adds CAPTCHA verification to a flow by using Google reCAPTCHA or compatible alternatives like hCaptcha and Cloudflare Turnstile.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -20,7 +20,6 @@ It can either be bound to a flow or embedded inside the [Identification stage](.
|
||||
- **Error on invalid score**: show an error immediately when the score is outside the configured threshold. If disabled, the flow continues and policies can inspect the result from context.
|
||||
- **JS URL**: JavaScript loader URL for the provider.
|
||||
- **API URL**: verification endpoint URL for the provider.
|
||||
- **Request content type**: content type used when authentik verifies the CAPTCHA token with the provider.
|
||||
|
||||
## Flow integration
|
||||
|
||||
|
||||
@@ -205,6 +205,13 @@ Copy the generated recovery key and paste it into the URL, after the domain. For
|
||||
|
||||
## End-to-end (E2E) setup
|
||||
|
||||
authentik ships two end-to-end test suites:
|
||||
|
||||
- The Django/Selenium suite under `tests/e2e/` — driven by Django's test runner. It is exercised in CI by the `test-e2e` job in `ci-main.yml`.
|
||||
- The browser-side Playwright suite under `web/test/browser/` — driven by `web/playwright.config.js`. It is exercised in CI by the `e2e (playwright)` job in `ci-web.yml` and runs against a live authentik server.
|
||||
|
||||
### Django/Selenium suite
|
||||
|
||||
Start the E2E test services with the following command:
|
||||
|
||||
```shell
|
||||
@@ -219,6 +226,40 @@ Alternatively, you can connect directly via VNC on port `5900` using the passwor
|
||||
When using Docker Desktop, host networking needs to be enabled via **Docker Settings** > **Resources** > **Network** > **Enable host networking**.
|
||||
:::
|
||||
|
||||
### Playwright suite
|
||||
|
||||
The Playwright suite assumes that:
|
||||
|
||||
- An authentik stack is reachable at `http://localhost:9000` (override with `AK_TEST_RUNNER_PAGE_URL`).
|
||||
- The blueprint at `web/test/blueprints/test-admin-user.yaml` has been applied so that the `test-admin@goauthentik.io` user (password `test-runner`) can log in.
|
||||
|
||||
Both prerequisites are satisfied by the standard full development environment described above, plus a one-off blueprint apply. From the repository root:
|
||||
|
||||
```shell
|
||||
# Make the test admin blueprint discoverable by the worker, then start authentik.
|
||||
mkdir -p blueprints/local
|
||||
cp web/test/blueprints/test-admin-user.yaml blueprints/local/test-admin-user.yaml
|
||||
|
||||
# In separate terminals:
|
||||
make run-server
|
||||
make run-worker
|
||||
```
|
||||
|
||||
Once the worker has applied the blueprint, install Playwright's browsers (one-time) and run the suite — these are the same npm scripts CI runs:
|
||||
|
||||
```shell
|
||||
corepack npm exec --prefix web -- playwright install --with-deps chromium
|
||||
corepack npm run --prefix web test:e2e
|
||||
```
|
||||
|
||||
After a failing run, open the HTML report to inspect traces, screenshots, and (in CI) videos:
|
||||
|
||||
```shell
|
||||
corepack npm exec --prefix web -- playwright show-report
|
||||
```
|
||||
|
||||
In CI, the same artifacts are uploaded under the `playwright-report` and `playwright-traces` artifact names on the failed workflow run.
|
||||
|
||||
## Contributing code
|
||||
|
||||
### Before submitting a pull request
|
||||
|
||||
@@ -102,7 +102,7 @@ To support the integration of AWS with authentik using SCIM, you need to create
|
||||
#### Create property mappings
|
||||
|
||||
1. Log in to authentik as an administrator and open the authentik Admin interface.
|
||||
2. Navigate to **Customization** > **Property Mappings**, click **Create**, select **SCIM Provider Mapping**, and click **Next**.
|
||||
2. Navigate to **Customization** > **Property Mappings**, click **Create**, select **SCIM Mapping**, and click **Next**.
|
||||
3. Configure the first _user mapping_ property mapping:
|
||||
- **Name**: Provide a name lexically lower than `authentik default` (e.g. `AWS SCIM User mapping`).
|
||||
- **Expression**:
|
||||
|
||||