Compare commits

..

185 Commits

Author SHA1 Message Date
Marc 'risson' Schmitt
880457aadf root: add flake
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2026-01-05 16:18:12 +01:00
authentik-automation[bot]
880afb6f69 core, web: update translations (#19179)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2026-01-05 14:34:35 +00:00
dependabot[bot]
3142cfa050 web: bump @formatjs/intl-listformat from 8.1.0 to 8.1.1 in /web (#19182)
Bumps [@formatjs/intl-listformat](https://github.com/formatjs/formatjs) from 8.1.0 to 8.1.1.
- [Release notes](https://github.com/formatjs/formatjs/releases)
- [Commits](https://github.com/formatjs/formatjs/compare/@formatjs/intl-listformat@8.1.0...@formatjs/intl-listformat@8.1.1)

---
updated-dependencies:
- dependency-name: "@formatjs/intl-listformat"
  dependency-version: 8.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-05 13:23:45 +01:00
dependabot[bot]
cb2d509945 ci: bump getsentry/action-release from 3.4.0 to 3.5.0 (#19183)
Bumps [getsentry/action-release](https://github.com/getsentry/action-release) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/getsentry/action-release/releases)
- [Changelog](https://github.com/getsentry/action-release/blob/master/CHANGELOG.md)
- [Commits](128c5058bb...dab6548b3c)

---
updated-dependencies:
- dependency-name: getsentry/action-release
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-05 12:52:05 +01:00
dependabot[bot]
9a8c7bb23b web: bump knip from 5.78.0 to 5.79.0 in /web (#19181)
Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 5.78.0 to 5.79.0.
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/knip@5.79.0/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 5.79.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-05 12:51:33 +01:00
Duncan Tasker
bc24815ae6 lifecycle: fix migration conn_options for psycopg connection (#19134)
fix migrations to add conn_options to the psycopg connection
2026-01-05 09:58:40 +01:00
Adithya S Narasinghe
c3eb401600 website/docs: remove duplicates in slo docs (#19170)
remove duplicated points in the iframe mode points in slo docs
2026-01-04 18:28:12 +00:00
Jens L.
1dd7269871 web/admin: adjust sync threshold, add tooltip (#19131)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2026-01-03 23:00:59 -05:00
Teffen Ellis
38381504c0 web: Fix user library colors, modal z-indexes, table progress bars (#19152)
* Fix progress bar fade out, positioning, labels.

* Export parts. Fix z-index, colors.

* Fix clickable area.

* Ignore clickable icons.
2026-01-03 22:43:31 -05:00
Dominic R
366ebc3a91 web: fix slug auto-updating when editing existing applications (#19169) 2026-01-04 03:36:13 +00:00
Dominic R
32f77f05f4 core: handle deserialization errors from FileField migration (#19067)
after migration 0054 changed icon fields from Django FileField to a TextField based custom FileField, old sessions which had serialized Source/Application model instances fail to deserialize.

The old FieldFile descriptors try to access field.storage which no longer exists.

We can't edit that migration since it has already been ran by many/

So, you  add AttributeError and TypeError to exception handling in SessionStore.decode() to return an empty session instead of crashing with 500.
2026-01-03 15:32:22 -05:00
authentik-automation[bot]
f077506c41 stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#19137)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2026-01-03 21:30:16 +01:00
William Kray
9183e4967b website/integrations: vaultwarden: add custom email scope (#19160)
* update vaultwarden integration doc to include custom scope mapping needed

* wip

---------

Co-authored-by: Dominic R <dominic@sdko.org>
2026-01-03 02:31:54 +00:00
dependabot[bot]
af5bff725f ci: bump int128/docker-manifest-create-action from 2.10.0 to 2.12.0 (#19138)
Bumps [int128/docker-manifest-create-action](https://github.com/int128/docker-manifest-create-action) from 2.10.0 to 2.12.0.
- [Release notes](https://github.com/int128/docker-manifest-create-action/releases)
- [Commits](b60433fd43...6cdd53a833)

---
updated-dependencies:
- dependency-name: int128/docker-manifest-create-action
  dependency-version: 2.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 11:57:48 +01:00
authentik-automation[bot]
1c2cf4bb5c core, web: update translations (#19135)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2026-01-02 11:33:11 +01:00
dependabot[bot]
c6a5062cd7 web: bump globals from 16.5.0 to 17.0.0 in /web (#19154)
Bumps [globals](https://github.com/sindresorhus/globals) from 16.5.0 to 17.0.0.
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v16.5.0...v17.0.0)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 17.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 11:32:59 +01:00
Jens L.
e4127fdaf0 web/user: fix consent delete form missing details (#19147)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2026-01-02 11:31:11 +01:00
Teffen Ellis
7085743cac web: Token Form Fixes (#19121)
* Fix autofocus attribute.

* web: Fix label alignment, focus handlers, edit states.

* Tidy date functions.

* Use Dates over strings.
2026-01-01 19:03:57 -05:00
Jens L.
27bd6d6e92 website/docs: fix build (#19148)
* ensure we never throw errors in the browser

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* cleaner

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* rework

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix misleading variable

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Tidy behavior.

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Teffen Ellis <teffen@goauthentik.io>
2026-01-01 18:30:27 +00:00
Jens L.
fecd852c2b website/docs: endpoint agent release notes (#19042)
* website/docs: endpoint agent release notes

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Apply suggestion from @dominic-r

Signed-off-by: Dominic R <dominic@sdko.org>

* rename, update

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
2026-01-01 13:53:54 +01:00
Dominic R
c45110ed10 web: disable user settings fields when changes are not allowed (#19132)
* web: disable user settings fields when changes are not allowed

Previously, when admins disabled the ability to change email, username, or name in System Settings, users would only see an error after attempting to submit the form.

Now, the field is marked as disabled.

* wip
2025-12-31 16:00:09 -05:00
SKAL
b1bfd457eb website/integrations: karakeep: don't hardcode wellknown's slug (#19127)
* Clarify OAUTH_WELLKNOWN_URL in documentation

Updated OAUTH_WELLKNOWN_URL placeholder for clarity.
I've updated it because this way it copies also the correct slug (which might be changed)

Signed-off-by: SKAL <pippo73@users.noreply.github.com>

* Update website/integrations/documentation/karakeep/index.md

Signed-off-by: Dominic R <dominic@sdko.org>

---------

Signed-off-by: SKAL <pippo73@users.noreply.github.com>
Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-31 16:06:14 +01:00
dependabot[bot]
8b579f064e core, web: bump qs from 6.14.0 to 6.14.1 in /packages/docusaurus-config (#19130)
Bumps [qs](https://github.com/ljharb/qs) from 6.14.0 to 6.14.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.0...v6.14.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-31 15:53:02 +01:00
dependabot[bot]
79b01f8c56 core: bump library/node from ccfd9da to 03729a7 in /website (#19125)
Bumps library/node from `ccfd9da` to `03729a7`.

---
updated-dependencies:
- dependency-name: library/node
  dependency-version: 25.2.1-trixie
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-31 15:08:32 +01:00
dependabot[bot]
d7e4b5c1ed core: bump github.com/jackc/pgx/v5 from 5.7.6 to 5.8.0 (#19088)
Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.7.6 to 5.8.0.
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.7.6...v5.8.0)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  dependency-version: 5.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-31 15:08:21 +01:00
dependabot[bot]
8b2ccc9568 web: bump the swc group across 1 directory with 11 updates (#19124)
Bumps the swc group with 1 update in the /web directory: [@swc/core](https://github.com/swc-project/swc).


Updates `@swc/core` from 1.15.7 to 1.15.8
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.7...v1.15.8)

Updates `@swc/core-darwin-arm64` from 1.15.7 to 1.15.8
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.7...v1.15.8)

Updates `@swc/core-darwin-x64` from 1.15.7 to 1.15.8
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.7...v1.15.8)

Updates `@swc/core-linux-arm-gnueabihf` from 1.15.7 to 1.15.8
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.7...v1.15.8)

Updates `@swc/core-linux-arm64-gnu` from 1.15.7 to 1.15.8
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.7...v1.15.8)

Updates `@swc/core-linux-arm64-musl` from 1.15.7 to 1.15.8
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.7...v1.15.8)

Updates `@swc/core-linux-x64-gnu` from 1.15.7 to 1.15.8
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.7...v1.15.8)

Updates `@swc/core-linux-x64-musl` from 1.15.7 to 1.15.8
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.7...v1.15.8)

Updates `@swc/core-win32-arm64-msvc` from 1.15.7 to 1.15.8
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.7...v1.15.8)

Updates `@swc/core-win32-ia32-msvc` from 1.15.7 to 1.15.8
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.7...v1.15.8)

Updates `@swc/core-win32-x64-msvc` from 1.15.7 to 1.15.8
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.7...v1.15.8)

---
updated-dependencies:
- dependency-name: "@swc/core"
  dependency-version: 1.15.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.15.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-x64"
  dependency-version: 1.15.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm-gnueabihf"
  dependency-version: 1.15.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.15.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-musl"
  dependency-version: 1.15.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-version: 1.15.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-musl"
  dependency-version: 1.15.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-arm64-msvc"
  dependency-version: 1.15.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-ia32-msvc"
  dependency-version: 1.15.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-x64-msvc"
  dependency-version: 1.15.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-31 14:57:41 +01:00
dependabot[bot]
84fa4f9dc8 core: bump library/nginx from ad85427 to ca871a8 in /website (#19126)
Bumps library/nginx from `ad85427` to `ca871a8`.

---
updated-dependencies:
- dependency-name: library/nginx
  dependency-version: 1.29-trixie
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-31 14:06:53 +01:00
Teffen Ellis
e5852b46cb web: Capitalize language display names, code owner fix (#19119)
* web: Capitalize locale display names.

* Fix broad code owner.
2025-12-30 18:04:36 -05:00
Teffen Ellis
c108877a85 web: Fix Impersonation, Lit Reactive Controller Contexts (#19114)
* web: Fix issue where impersonation does not trigger updates.

* web: Fix issues surrounding abort controller types, lifecycle.
2025-12-30 11:37:09 -05:00
dependabot[bot]
84f74ee306 web: bump the eslint group across 1 directory with 3 updates (#19110)
Bumps the eslint group with 3 updates in the /web directory: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@typescript-eslint/eslint-plugin` from 8.50.1 to 8.51.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.51.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.50.1 to 8.51.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.51.0/packages/parser)

Updates `typescript-eslint` from 8.50.1 to 8.51.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.51.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.51.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.51.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.51.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-30 13:08:21 +01:00
dependabot[bot]
32cb16ec2f core: bump library/nginx from fb01117 to ad85427 in /website (#19112)
Bumps library/nginx from `fb01117` to `ad85427`.

---
updated-dependencies:
- dependency-name: library/nginx
  dependency-version: 1.29-trixie
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-30 13:08:08 +01:00
dependabot[bot]
d5a2f2dc14 web: bump the storybook group across 1 directory with 5 updates (#19111)
Bumps the storybook group with 4 updates in the /web directory: [@storybook/addon-docs](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/docs), [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links), [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) and [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite).


Updates `@storybook/addon-docs` from 10.1.10 to 10.1.11
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.11/code/addons/docs)

Updates `@storybook/addon-links` from 10.1.10 to 10.1.11
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.11/code/addons/links)

Updates `@storybook/web-components` from 10.1.10 to 10.1.11
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.11/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 10.1.10 to 10.1.11
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.11/code/frameworks/web-components-vite)

Updates `storybook` from 10.1.10 to 10.1.11
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.11/code/core)

---
updated-dependencies:
- dependency-name: "@storybook/addon-docs"
  dependency-version: 10.1.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-version: 10.1.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-version: 10.1.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-version: 10.1.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-version: 10.1.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-30 13:07:56 +01:00
Dominic R
c22f043ad6 website/docs: release notes: Add more integrations (#19109) 2025-12-30 08:10:31 +00:00
Lukas
b2778702d0 website/integrations: Add Audiobookshelf (#19104)
* website/docs: Add Audiobookshelf Community Integration Documentation

* wip

---------

Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-30 03:22:54 +00:00
Lukas
d43724deb8 website/integrations: Add Pulse (#19105)
* website/docs: Add Pulse Community Integration Documentation

* wip

---------

Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-30 03:19:03 +00:00
Ken Sternberg
8b21392aa3 web/maintenance/no unknown attributes (part 1) (#18970)
* web: Add InvalidationFlow to Radius Provider dialogues

## What

- Bugfix: adds the InvalidationFlow to the Radius Provider dialogues
  - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated
    to the Notification.
- Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/`

## Note

Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the
Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of
the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current
dialogues at the moment.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* website: fix bad escaping of URLs in release notes

## What

Fixes bad escaping of URLs in the release notes that resulted in mangled output.

v2024.6.4 had entries that looked like this:

```
##### `GET` /providers/google_workspace/{#123;id}#125;/
```

v2025.4.md had entries that looked like this:

```
##### `GET` /policies/unique_password/{#125;#123;policy_uuid}/
```

A couple of straightforward search-and-replaces has fixed the issue.

## Notes

Two of the release notes had bad escaping of URLs. I'm not sure how the error was made or got past,
but it was obvious when visiting the page.

@Beryju suggested that the bug is due to our using `{...}` to symbolize parameters in a URL while
Docusaurus wants to interpret `{...}` as an internal template instruction, resulting in odd
behavior. In either case, docusarus interpreted the hashtagged entries as links to unrelated issues
in Github (the same two issues, which were "bump version of pylint" and "bump version of sentry"),
which could be very confusing.

The inconsistencies between the two releases, and the working releases, suggests that the error was
introduced manually.

* web/maintenance/no-unknown-attributes-1

# What

This commit is a collection of fixes and adaptations discovered while running lit-analyzer in a stricter role than usual. These fixes are to 9 of the existing issues; there are 16 more that will be addressed in the next two pull requests.

The following issues were uncovered.

- `ak-slug-input` does not take `autocomplete`.
- `ak-wizard-page-type-create` does not take, or use, the `name` attribute. It also has no `value` of its own, so it is not processed as a form object.
- `ak-endpoints-device-access-groups-form` does not take a `pk` attribute. It takes an `.instancePk` property.
- `ak-provider-oauth2-redirect-uri` is only used in one place, and that place uses the term `input-id` for the key. The component was expected `inputId`. Since it is a string and therefore an attribute, kebab-case is the appropriate fix here.
- `input-mode` is not a valid attribute. The attribute is `inputmode`, and the property is `inputMode`. It may not be undefined. If it is defined, the default is `text`. I have fixed this in the attribute and in the two Forms that used it.
- `form-associated-element` had both `name` and `type` as readonly. Since they are native attributes, they can be attributes or they can be readonly. They can’t be both. I have made them read-write.
- `user-source-settings-page` is only used in one place, and that place uses the term `input-id` for the key. The component was expected `inputId`. Since it is a string and therefore an attribute, kebab-case is the appropriate fix here.

These guideposts will be placed on the PR.

* Update web/src/admin/providers/oauth2/OAuth2ProviderRedirectURI.ts

Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Signed-off-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>

* Update web/src/components/ak-text-input.ts

Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Signed-off-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>

* Fix duplicate lines in property declaration. Not sure how that snuck in there.

* Prettier has opinions.

---------

Signed-off-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
2025-12-29 10:50:16 -08:00
Austin Dudzik
7246681815 Update Vaultwarden documentation by removing warning (#19102)
Removed warning about SSO feature availability on testing images.

Signed-off-by: Austin Dudzik <65981261+austin-dudzik@users.noreply.github.com>
2025-12-29 18:01:49 +00:00
Dominic R
d5869674b7 web: Fix stale flow background (#19015) 2025-12-29 17:30:12 +00:00
Dominic R
2a97dd2912 web: fix promoted source button hover losing blue color (#19048) 2025-12-29 08:59:53 -08:00
dependabot[bot]
485d413548 web: bump knip from 5.77.1 to 5.78.0 in /web (#19090) 2025-12-29 17:55:01 +01:00
Dewi Roberts
cef54653ec website/docs: endpoint devices: add path to macos setup (#19093)
* Add path

* Update macos.md

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
2025-12-29 08:15:36 -05:00
Dewi Roberts
e16a0ab99d website/docs: endpoint devices: update features table (#19094)
* Update table

* Remove wording
2025-12-29 08:14:15 -05:00
Dominic R
722b425c3b website/docs: rel notes .12: add wallos (#19063) 2025-12-29 09:24:26 +00:00
Dominic R
ec227532c4 website/docs: endpoints: mention connector key required for stage to work (#19084)
keypair = CertificateKeyPair.objects.filter(pk=stage.connector.challenge_key_id).first()
  if not keypair:
      return self.executor.stage_ok()  # < --- skips the stage

took me a bit of time to find this and yea
2025-12-29 09:23:15 +00:00
Jens L.
7300737a12 web/admin: fix button alignment on user view page (#19079)
* web/admin: fix button alignment on user view page

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix width

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-28 16:15:18 +01:00
dependabot[bot]
9d70c5a3ed web: bump @formatjs/intl-listformat from 7.7.13 to 8.1.0 in /web (#19054)
* web: bump @formatjs/intl-listformat from 7.7.13 to 8.1.0 in /web

Bumps [@formatjs/intl-listformat](https://github.com/formatjs/formatjs) from 7.7.13 to 8.1.0.
- [Release notes](https://github.com/formatjs/formatjs/releases)
- [Commits](https://github.com/formatjs/formatjs/compare/@formatjs/intl-listformat@7.7.13...@formatjs/intl-listformat@8.1.0)

---
updated-dependencies:
- dependency-name: "@formatjs/intl-listformat"
  dependency-version: 8.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-12-28 15:19:53 +01:00
Jens L.
b5848765b2 internal: update TLS Suite (#19076)
* internal: update TLS Suite

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* disable chacha20 due to fips

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-28 14:46:27 +01:00
dependabot[bot]
1877a9d286 web: bump @lit/localize-tools from 0.8.0 to 0.8.1 in /web (#19040)
Bumps [@lit/localize-tools](https://github.com/lit/lit/tree/HEAD/packages/localize-tools) from 0.8.0 to 0.8.1.
- [Release notes](https://github.com/lit/lit/releases)
- [Changelog](https://github.com/lit/lit/blob/main/packages/localize-tools/CHANGELOG.md)
- [Commits](https://github.com/lit/lit/commits/HEAD/packages/localize-tools)

---
updated-dependencies:
- dependency-name: "@lit/localize-tools"
  dependency-version: 0.8.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-28 14:00:39 +01:00
dependabot[bot]
a5ed6b05e2 web: bump knip from 5.77.0 to 5.77.1 in /web (#19049)
Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 5.77.0 to 5.77.1.
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/knip@5.77.1/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 5.77.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-28 14:00:20 +01:00
authentik-automation[bot]
5d66ba341d core, web: update translations (#18991)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-12-28 14:00:00 +01:00
Jens L.
4f4e37f2b0 tests/e2e: add endpoint tests (#19072)
* tests/e2e: add endpoint tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* dont rely on hostname

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-28 12:53:00 +01:00
ocmateusz
a98d70ea7b website/integrations: owncloud: fix php (#19073) 2025-12-27 21:45:35 +00:00
Dominic R
aa8644ea27 web: fix file search input not resetting results properly (#19034) 2025-12-27 16:19:50 -05:00
Dominic R
fe0106886a blueprints: fix deadlock and task context error in MetaApplyBlueprint (#19033)
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-12-26 13:47:07 -05:00
authentik-automation[bot]
0a27684dbd *: Auto compress images (#19065)
*: compress images

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dominic-r <157558804+dominic-r@users.noreply.github.com>
2025-12-26 12:55:08 -05:00
Borut Mrak
25cae250cd website/docs: FreeIPA documentation updates (#15183)
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Borut Mrak <borut.mrak@acex.si>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-12-26 17:14:59 +00:00
Lukas
960d09300f website/integrations: Add Wallos (#19013)
Co-authored-by: dewi-tik <dewi@goauthentik.io>
2025-12-26 11:50:31 -05:00
Jens L.
46297698d6 blueprints: set enrollment token key (#19061)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-26 17:37:48 +01:00
Jens L.
448c8f8745 endpoints/devices: cleanup (#19047)
* endpoints: make device token internally managed

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix text and defaults for agent

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* re-org some code

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-26 14:20:20 +01:00
Amélie Lilith Krejčí
1685f8f9eb lib/sync: fix sync_dispatch (#19053)
* fix: add missing call to all on self.schedules

Fixes #19051

* fix: change the name of syncOutgoingTriggerMode ak-radio-input

Fixes #19052
2025-12-26 14:01:27 +01:00
Marcelo Elizeche Landó
61e45ca59a docs/release notes: update 2025.12 release notes (#19043)
* Add links and tags

* Update website/docs/releases/2025/v2025.12.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

---------

Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-24 13:01:26 -03:00
dependabot[bot]
0a5e421c9c core: bump goauthentik.io/api/v3 from 3.2026020.5 to 3.2026020.6 (#19036)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2026020.5 to 3.2026020.6.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2026020.5...v3.2026020.6)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-version: 3.2026020.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-24 13:58:28 +01:00
dependabot[bot]
097b39d773 web: bump knip from 5.75.1 to 5.77.0 in /web (#19041)
Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 5.75.1 to 5.77.0.
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/knip@5.77.0/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 5.77.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-24 13:56:04 +01:00
dependabot[bot]
a9c2613ba6 web: bump @lit/reactive-element from 2.1.1 to 2.1.2 in /web (#19037)
Bumps [@lit/reactive-element](https://github.com/lit/lit/tree/HEAD/packages/reactive-element) from 2.1.1 to 2.1.2.
- [Release notes](https://github.com/lit/lit/releases)
- [Changelog](https://github.com/lit/lit/blob/main/packages/reactive-element/CHANGELOG.md)
- [Commits](https://github.com/lit/lit/commits/lit@2.1.2/packages/reactive-element)

---
updated-dependencies:
- dependency-name: "@lit/reactive-element"
  dependency-version: 2.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-24 13:55:39 +01:00
dependabot[bot]
4684f8a4c4 web: bump lit from 3.3.1 to 3.3.2 in /web (#19038)
Bumps [lit](https://github.com/lit/lit/tree/HEAD/packages/lit) from 3.3.1 to 3.3.2.
- [Release notes](https://github.com/lit/lit/releases)
- [Changelog](https://github.com/lit/lit/blob/main/packages/lit/CHANGELOG.md)
- [Commits](https://github.com/lit/lit/commits/lit-element@3.3.2/packages/lit)

---
updated-dependencies:
- dependency-name: lit
  dependency-version: 3.3.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-24 13:54:36 +01:00
Dominic R
7845dcf687 web/admin: use consistent icon for inactive user status (#19032) 2025-12-24 03:23:16 +00:00
Jens L.
cc2dbf4db5 core: use chunked_queryset for expired message deletion (#19028)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-24 01:15:45 +01:00
Teffen Ellis
f520c90e16 web: Fix Storybook package resolution with npm link (#19016) 2025-12-23 18:40:47 +00:00
Teffen Ellis
342d9eb726 web: Locale selector UI fixes (#18972)
* Fix alignment, focus.

* Clean up.

* Tidy click area.

* Fix compatibility mode.

* Fix alignment.

* Fix issues surrounding labels, alignment, consistency.

* Update web/src/common/ui/locale/format.ts

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* Tidy hover states.

* Tidy.

* Clean up parsing.

* Tidy comments, usage.

* Always use script naming over region.

* Remove unused.

* Spacing.

---------

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
2025-12-23 18:40:02 +00:00
Teffen Ellis
2f8dfff207 website/docs: Prioritize "Release Candidate" over "Current Release" (#18975)
Normalize labels.
2025-12-23 15:41:42 +01:00
dependabot[bot]
0ba071e1e5 core: bump goauthentik.io/api/v3 from 3.2026020.4 to 3.2026020.5 (#19017)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2026020.4 to 3.2026020.5.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2026020.4...v3.2026020.5)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-version: 3.2026020.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 14:50:54 +01:00
dependabot[bot]
6443d56d2b web: bump the eslint group across 1 directory with 3 updates (#19019)
Bumps the eslint group with 3 updates in the /web directory: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@typescript-eslint/eslint-plugin` from 8.50.0 to 8.50.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.50.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.50.0 to 8.50.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.50.1/packages/parser)

Updates `typescript-eslint` from 8.50.0 to 8.50.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.50.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.50.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.50.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.50.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 13:43:43 +01:00
Jens L.
fcc0438961 web/admin: prevent file upload attempt when backend not managed (#18646)
* web/admin: prevent file upload attempt when backend not managed

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* wip

* fixup

* rework

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* format

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add check for reports

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix delete table for data exports missing details

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-23 13:41:27 +01:00
Jens L.
d9cd1096b9 api: rework schema generation (#18977)
* api: rework schema generation

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* better error message

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* actually fix tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* different test

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-23 12:20:24 +01:00
dependabot[bot]
fc4765d265 web: bump globby from 16.0.0 to 16.1.0 in /web (#18995)
Bumps [globby](https://github.com/sindresorhus/globby) from 16.0.0 to 16.1.0.
- [Release notes](https://github.com/sindresorhus/globby/releases)
- [Commits](https://github.com/sindresorhus/globby/compare/v16.0.0...v16.1.0)

---
updated-dependencies:
- dependency-name: globby
  dependency-version: 16.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 11:50:15 +01:00
dependabot[bot]
4b54684efa core: bump openapitools/openapi-generator-cli from v7.16.0 to v7.18.0 in /scripts/api (#19018)
core: bump openapitools/openapi-generator-cli in /scripts/api

Bumps openapitools/openapi-generator-cli from v7.16.0 to v7.18.0.

---
updated-dependencies:
- dependency-name: openapitools/openapi-generator-cli
  dependency-version: v7.18.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 11:49:39 +01:00
dependabot[bot]
519958fdbc web: bump the rollup group across 1 directory with 4 updates (#18994)
Bumps the rollup group with 4 updates in the /web directory: [@rollup/rollup-darwin-arm64](https://github.com/rollup/rollup), [@rollup/rollup-linux-arm64-gnu](https://github.com/rollup/rollup), [@rollup/rollup-linux-x64-gnu](https://github.com/rollup/rollup) and [rollup](https://github.com/rollup/rollup).


Updates `@rollup/rollup-darwin-arm64` from 4.53.5 to 4.54.0
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.5...v4.54.0)

Updates `@rollup/rollup-linux-arm64-gnu` from 4.53.5 to 4.54.0
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.5...v4.54.0)

Updates `@rollup/rollup-linux-x64-gnu` from 4.53.5 to 4.54.0
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.5...v4.54.0)

Updates `rollup` from 4.53.5 to 4.54.0
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.5...v4.54.0)

---
updated-dependencies:
- dependency-name: "@rollup/rollup-darwin-arm64"
  dependency-version: 4.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rollup
- dependency-name: "@rollup/rollup-linux-arm64-gnu"
  dependency-version: 4.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rollup
- dependency-name: "@rollup/rollup-linux-x64-gnu"
  dependency-version: 4.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rollup
- dependency-name: rollup
  dependency-version: 4.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rollup
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-22 23:32:24 +01:00
Dewi Roberts
97ef6a5eb2 website/docs: improve endpoint devices docs (#19007)
* Remove sudo auth sections

* Add firefox extension link

* Add chrome extension

* Update release notes

* Remove link

* Fix link

* Fix release note wording
2025-12-22 20:32:20 +00:00
Jens L.
e23c28373c enterprise/search: add static autocomplete structure (#19008)
* enterprise/search: add static autocomplete structure

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add recursive structured for context

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-22 20:53:33 +01:00
Jens L.
7fa28c60c7 enterprise/reports: improve export list, confirmation (#18981)
* enterprise/reports: use verbose name for model label

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add confirmation for export

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update docs

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove duplicated api

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix duplicate

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix search query not updated

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* exclude page & page size

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* improve query display

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix user display

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* exclude unset params

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Apply suggestions from code review

Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Signed-off-by: Jens L. <jens@beryju.org>

* more code style

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* format

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix types

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
2025-12-22 20:35:18 +01:00
Jens L.
3cd1a31365 providers/oauth2: Automated OpenID Conformance tests (#14785)
* some progress

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* somewhat working?

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove some previous debugging things

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make it kinda work

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* revert more debugging stuff

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make tests mostly work

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* smaller screenshots?

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove debugging

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* sleep a bit before checking again

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* cleanup, restart loop when we finished an operation

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* refactor conformance helper to requests (thanks chatgpt)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* properly install subtests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* maybe run in CI?

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* dont hardcode IP

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix network and cookie deletion

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* upload cert exports

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* test

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* attempt to switch to generated

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make it work generated?

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix teardown

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* re-add implicit and fix?

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Revert "re-add implicit and fix?"

This reverts commit 6a4d15fc22cf4b27ffa428be9ecc9a0e778961c6.

* Revert "fix teardown"

This reverts commit cb96b0cb988acedec1fe72ec437b68e2c38ed6b1.

* Revert "make it work generated?"

This reverts commit 4e29d2c5737ee9aaad6c0f4701caf7e0fb110e15.

* Revert "attempt to switch to generated"

This reverts commit 6f851e021d305a93be9cfbb4a9b6783231b6d7cf.

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* check authorize request param earlier

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix some

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix basic suite?

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* another actual fix; don't return access_token when using response_type id_token

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add implicit test

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add custom profile scope that includes standard scopes to return number of warnings

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* use actual timestamp

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix missing offline_access, use scoped issuer

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* only run basic+implicit for now, fix other tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* split up

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix offline_access tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix waiting for compete on error

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix duplicate artifact

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix artifact

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* 👀

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix typing

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* typing

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix implicit

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* don't wait for conformance test

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* more disk space

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-22 20:21:22 +01:00
dependabot[bot]
b66e4074e2 ci: bump docker/setup-buildx-action from 3.11.1 to 3.12.0 (#18999)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.11.1 to 3.12.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](e468171a9d...8d2750c68a)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: 3.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-22 16:34:46 +01:00
Jens L.
fa65d4730c blueprints: fix flaky tests (#19002)
* blueprints: attempt to fix tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix postgres debug logging

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-22 15:39:05 +01:00
Dominic R
e3d774b3ef web: fix Open button selecting row instead of navigating (#18992)
the `isEventTargetingListener()` function only checked the click target and the immediate parent for interactive elements (like links, buttons and more). when clicking the icon inside the Open button, the DOM structure is:

<a href=...>  <--- 2 levels up, never checked
<pf-tooltip>  <--- immediate parent, not interactive
<i> <---- click target, not interactive

Because <i> and <pf-tooltip> did not match the interactive elements query, the function returned false which caused the table rowClickListener to continue with row selection isntead of allowing the click.

The fix is to update the function to to traverse (up) the entire dom tree from the click target to the listener element (the table cell) and check for each ancestor for the interactive elements.
2025-12-22 15:02:59 +01:00
Jens L.
162e05ff9d events: notifications live update (#18980)
* this has been broken for a while but no one noticed...? cc @rissson

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* send WS broadcast for new notifications

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* better layout

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix e2e tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-21 23:02:56 +01:00
Henry Skrtich
238cefba45 web/admin: Fix haveibeenpwned link in PasswordPolicyForm (#18984)
web: Fix haveibeenpwned link in PasswordPolicyForm
2025-12-21 15:02:04 +01:00
Jens L.
bfa46ddc7e web/admin: fix dark theme on map (#18985)
web/admin: fix dark theme on map broken

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-21 15:01:26 +01:00
Jens L.
e24b60f806 blueprints: add InternallyManagedMixin instead of large list (#18983)
* blueprints: add InternallyManagedMixin instead of large list

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix recursion

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix e2e tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-21 03:31:41 +01:00
Stijn van Hees
0c792c8463 website/integrations: Fix path for Cloudflare Access (#18979)
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-20 16:57:57 +00:00
Ken Sternberg
1143de97d0 web/maintenance: no unknown tag names (#18944)
* web: Add InvalidationFlow to Radius Provider dialogues

## What

- Bugfix: adds the InvalidationFlow to the Radius Provider dialogues
  - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated
    to the Notification.
- Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/`

## Note

Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the
Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of
the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current
dialogues at the moment.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* website: fix bad escaping of URLs in release notes

## What

Fixes bad escaping of URLs in the release notes that resulted in mangled output.

v2024.6.4 had entries that looked like this:

```
##### `GET` /providers/google_workspace/{#123;id}#125;/
```

v2025.4.md had entries that looked like this:

```
##### `GET` /policies/unique_password/{#125;#123;policy_uuid}/
```

A couple of straightforward search-and-replaces has fixed the issue.

## Notes

Two of the release notes had bad escaping of URLs. I'm not sure how the error was made or got past,
but it was obvious when visiting the page.

@Beryju suggested that the bug is due to our using `{...}` to symbolize parameters in a URL while
Docusaurus wants to interpret `{...}` as an internal template instruction, resulting in odd
behavior. In either case, docusarus interpreted the hashtagged entries as links to unrelated issues
in Github (the same two issues, which were "bump version of pylint" and "bump version of sentry"),
which could be very confusing.

The inconsistencies between the two releases, and the working releases, suggests that the error was
introduced manually.

* web/maintenance: lint pass to fix broken or unrecognized tag names

# What

This code removes two places in the code that referenced obsolete tag names.

In AkWizardFormPage, the case was a tag that was defined but never used. It, in turn, referenced a tag that did not exist.

In AkApplicationWizard’s ProviderChoices, we referenced eight custom components that did not exist and were never defined anywhere in the code. The references to `renderers` were obsolete; despite being defined they were never used. (This lack of use was covered up by lots of `export`s discarding Typescript’s check against unused field.)

- [x] The code has been formatted

# Why

- WizardFormPage references ‘ak-wizard-form’, which does not exist
- No other component imports, inherits, or extends WizardFormPage. It only exists by itself.

``` shell
$ rg 'WizardFormPage'
src/elements/wizard/WizardFormPage.ts
39:export class WizardFormPage extends WizardPage {
```

- The objects referenced here in these renderers do not exist.
- Without them, the priority ordering code becomes much simpler
- No LocalTypeCreate calls are needed; just use the default API TypeCreate types now

<!-- -->

    ./src/admin/applications/wizard/steps/ProviderChoices.ts

        Unknown tag <ak-application-wizard-authentication-by-oauth>. Did you mean <ak-application-wizard-application-step>?
        19:  html`<ak-application-wizard-authentication-by-oauth></ak-appl
        no-unknown-tag-name

        Unknown tag <ak-application-wizard-authentication-by-saml-configuration>. Did you mean <ak-application-wizard-application-step>?
        24:  html`<ak-application-wizard-authentication-by-saml-configuration></ak-appl
        no-unknown-tag-name

* A better way to prioritise, without losing the original script.

* further streamlining the priority order.

* Making it harder to add bad provider models to the application wizard.

* Prettier has opinions.

* Adding these to the tagname maps so lit-analyze can validate their uses.

* Made testing for valid returns from the server generic.  Suggestion from @GirlBossRush
2025-12-19 08:20:33 -08:00
Ken Sternberg
d6bc5871fa web/maintenance: fix missing custom web component imports (#18942)
* web: Add InvalidationFlow to Radius Provider dialogues

## What

- Bugfix: adds the InvalidationFlow to the Radius Provider dialogues
  - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated
    to the Notification.
- Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/`

## Note

Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the
Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of
the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current
dialogues at the moment.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* website: fix bad escaping of URLs in release notes

## What

Fixes bad escaping of URLs in the release notes that resulted in mangled output.

v2024.6.4 had entries that looked like this:

```
##### `GET` /providers/google_workspace/{#123;id}#125;/
```

v2025.4.md had entries that looked like this:

```
##### `GET` /policies/unique_password/{#125;#123;policy_uuid}/
```

A couple of straightforward search-and-replaces has fixed the issue.

## Notes

Two of the release notes had bad escaping of URLs. I'm not sure how the error was made or got past,
but it was obvious when visiting the page.

@Beryju suggested that the bug is due to our using `{...}` to symbolize parameters in a URL while
Docusaurus wants to interpret `{...}` as an internal template instruction, resulting in odd
behavior. In either case, docusarus interpreted the hashtagged entries as links to unrelated issues
in Github (the same two issues, which were "bump version of pylint" and "bump version of sentry"),
which could be very confusing.

The inconsistencies between the two releases, and the working releases, suggests that the error was
introduced manually.

* web: lint pass to add all missing custom component imports

# What

The latest version of lit-analyze found 53(!) places in the codebase where we referenced a custom web component but not guarantee that it had been registered with the browser. Most of these are so commonplace that they had already been pulled in and registered elsewhere, but it’s still bad practice to leave these out.

* web/maintenance: lint pass to fix broken or unrecognized tag names

# What

This code removes two places in the code that referenced obsolete tag names.

In AkWizardFormPage, the case was a tag that was defined but never used. It, in turn, referenced a tag that did not exist.

In AkApplicationWizard’s ProviderChoices, we referenced eight custom components that did not exist and were never defined anywhere in the code. The references to `renderers` were obsolete; despite being defined they were never used. (This lack of use was covered up by lots of `export`s discarding Typescript’s check against unused field.)

- [x] The code has been formatted

# Why

- WizardFormPage references ‘ak-wizard-form’, which does not exist
- No other component imports, inherits, or extends WizardFormPage. It only exists by itself.

``` shell
$ rg 'WizardFormPage'
src/elements/wizard/WizardFormPage.ts
39:export class WizardFormPage extends WizardPage {
```

- The objects referenced here in these renderers do not exist.
- Without them, the priority ordering code becomes much simpler
- No LocalTypeCreate calls are needed; just use the default API TypeCreate types now

<!-- -->

    ./src/admin/applications/wizard/steps/ProviderChoices.ts

        Unknown tag <ak-application-wizard-authentication-by-oauth>. Did you mean <ak-application-wizard-application-step>?
        19:  html`<ak-application-wizard-authentication-by-oauth></ak-appl
        no-unknown-tag-name

        Unknown tag <ak-application-wizard-authentication-by-saml-configuration>. Did you mean <ak-application-wizard-application-step>?
        24:  html`<ak-application-wizard-authentication-by-saml-configuration></ak-appl
        no-unknown-tag-name

* Revert "web/maintenance: lint pass to fix broken or unrecognized tag names"

This reverts commit e9e073fbcc.
2025-12-19 08:19:25 -08:00
Dewi Roberts
0fec6fb81b website/docs: add note to active directory source doc (#18787)
Adds note

Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-19 16:00:17 +01:00
dependabot[bot]
f8cf6b8530 ci: bump actions/attest-build-provenance from 3.0.0 to 3.1.0 (#18960)
Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](977bb373ed...00014ed6ed)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-version: 3.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-19 15:57:50 +01:00
dependabot[bot]
320ca655b4 web: bump @sentry/browser from 10.31.0 to 10.32.0 in /web in the sentry group across 1 directory (#18957)
web: bump @sentry/browser in /web in the sentry group across 1 directory

Bumps the sentry group with 1 update in the /web directory: [@sentry/browser](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/browser` from 10.31.0 to 10.32.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/10.31.0...10.32.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 10.32.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-19 15:57:32 +01:00
dependabot[bot]
72dd63af31 web: bump the swc group across 1 directory with 11 updates (#18958)
Bumps the swc group with 1 update in the /web directory: [@swc/core](https://github.com/swc-project/swc).


Updates `@swc/core` from 1.15.6 to 1.15.7
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.6...v1.15.7)

Updates `@swc/core-darwin-arm64` from 1.15.6 to 1.15.7
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.6...v1.15.7)

Updates `@swc/core-darwin-x64` from 1.15.6 to 1.15.7
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.6...v1.15.7)

Updates `@swc/core-linux-arm-gnueabihf` from 1.15.6 to 1.15.7
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.6...v1.15.7)

Updates `@swc/core-linux-arm64-gnu` from 1.15.6 to 1.15.7
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.6...v1.15.7)

Updates `@swc/core-linux-arm64-musl` from 1.15.6 to 1.15.7
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.6...v1.15.7)

Updates `@swc/core-linux-x64-gnu` from 1.15.6 to 1.15.7
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.6...v1.15.7)

Updates `@swc/core-linux-x64-musl` from 1.15.6 to 1.15.7
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.6...v1.15.7)

Updates `@swc/core-win32-arm64-msvc` from 1.15.6 to 1.15.7
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.6...v1.15.7)

Updates `@swc/core-win32-ia32-msvc` from 1.15.6 to 1.15.7
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.6...v1.15.7)

Updates `@swc/core-win32-x64-msvc` from 1.15.6 to 1.15.7
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.6...v1.15.7)

---
updated-dependencies:
- dependency-name: "@swc/core"
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-x64"
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm-gnueabihf"
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-musl"
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-musl"
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-arm64-msvc"
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-ia32-msvc"
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-x64-msvc"
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-19 15:47:34 +01:00
dependabot[bot]
4cbffd5b27 web: bump chromedriver from 143.0.2 to 143.0.3 in /web (#18959)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 143.0.2 to 143.0.3.
- [Commits](https://github.com/giggio/node-chromedriver/compare/143.0.2...143.0.3)

---
updated-dependencies:
- dependency-name: chromedriver
  dependency-version: 143.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-19 15:29:13 +01:00
dependabot[bot]
4d9f4638fa core: bump goauthentik.io/api/v3 from 3.2026020.3 to 3.2026020.4 (#18956)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2026020.3 to 3.2026020.4.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2026020.3...v3.2026020.4)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-version: 3.2026020.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-19 11:36:45 +00:00
Connor Peshek
646a0d3692 root: move docker files to lifecycle/containers and change docker-compose to compose (#16624) 2025-12-18 18:23:54 -06:00
Dominic R
59460ac840 flows/executor: fix KeyError when session has no existing plan (#18951) 2025-12-19 00:21:32 +00:00
Jens L.
c67cd41c22 web/admin: fix endpoints user binding (#18935)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-19 00:07:52 +01:00
Teffen Ellis
5a840e8f6d website/docs: Fix version parsing. (#18948) 2025-12-18 21:59:48 +00:00
Dewi Roberts
81771ce609 website/docs: release notes: add endpoint device links to 2025.12 notes (#18940)
Add links to release notes
2025-12-18 20:16:59 +00:00
Teffen Ellis
5babdf64cb website/docs: Fix labels, Pre-Release detection (#18945)
* Fix import path.

* Show unlisted entries if release.

* Fix sidebar rendering.

* Fix positioning of pre-release note. Tidy phrasing.

* Clarify pre-release vs draft.
2025-12-18 21:01:47 +01:00
Dewi Roberts
de298d72de website/docs: endpoint devices (#18634)
* Initial

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Apply suggestions

* Apply suggestions

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* WIP

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* fixes

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* WIP

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Fix anchor

* Update website/docs/endpoint-devices/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* WIP

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-12-18 19:09:11 +00:00
Jens L.
d7f3055cd8 stages/identification: replace sleep with make_password (#18883)
* stages/identification: replace sleep with make_password

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-18 19:09:10 +01:00
Jens L.
0beb8f9a12 web/elements: progress-bar and table loading header (#18934)
* add ak-progress-bar

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make intermediate smaller

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add table

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* hide table overflow

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-18 17:44:21 +01:00
Marc 'risson' Schmitt
6bfa07d761 crypto: fix extra cert data in db migration (#18937) 2025-12-18 16:34:31 +00:00
Weilu Jia
8d4c05c7e7 website/integrations: Add launch URL for Immich (#18921)
Signed-off-by: Weilu Jia <optix2000@users.noreply.github.com>
2025-12-18 16:24:51 +00:00
Victor Nawothnig
598b92d928 web/flow: Fix spurious double submit on ak-stage-autosubmit (#18727)
* Fix double submission on ak-stage-autosubmit

* use updated correctly

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Victor Nawothnig <Victor.Nawothnig+git@icloud.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-12-18 14:35:18 +00:00
Simonyi Gergő
90c8f23862 packages/ak-guardian: cast safely (#18929)
* packages/ak-guardian: cast safely

* use `regexp_like` instead of `pg_input_is_valid`

* alternative approach: RawSQL subquery

* remove extra fields we don't need

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>

* prevent subquery collapse

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>

* take into account foreignkeys

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>

* shut up bandit

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>

* clean up a bit

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-18 15:29:10 +01:00
Jens L.
62dc04a684 stages: remove more global state (#18641)
* add auth for active flow

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* migrate duo

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* migrate sms

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* migrate consent

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* migrate email and fix broken tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fallback

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* merge flow plan when restoring from token

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-18 13:12:35 +01:00
dependabot[bot]
68f3bf6ec1 web: bump the storybook group across 1 directory with 5 updates (#18817)
Bumps the storybook group with 4 updates in the /web directory: [@storybook/addon-docs](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/docs), [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links), [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) and [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite).


Updates `@storybook/addon-docs` from 10.1.7 to 10.1.8
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.8/code/addons/docs)

Updates `@storybook/addon-links` from 10.1.7 to 10.1.8
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.8/code/addons/links)

Updates `@storybook/web-components` from 10.1.7 to 10.1.8
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.8/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 10.1.7 to 10.1.8
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.8/code/frameworks/web-components-vite)

Updates `storybook` from 10.1.7 to 10.1.8
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.8/code/core)

---
updated-dependencies:
- dependency-name: "@storybook/addon-docs"
  dependency-version: 10.1.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-version: 10.1.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-version: 10.1.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-version: 10.1.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-version: 10.1.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-18 13:02:01 +01:00
dependabot[bot]
8234613b76 web: bump the swc group across 1 directory with 11 updates (#18923)
Bumps the swc group with 1 update in the /web directory: [@swc/core](https://github.com/swc-project/swc).


Updates `@swc/core` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.5...v1.15.6)

Updates `@swc/core-darwin-arm64` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.5...v1.15.6)

Updates `@swc/core-darwin-x64` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.5...v1.15.6)

Updates `@swc/core-linux-arm-gnueabihf` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.5...v1.15.6)

Updates `@swc/core-linux-arm64-gnu` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.5...v1.15.6)

Updates `@swc/core-linux-arm64-musl` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.5...v1.15.6)

Updates `@swc/core-linux-x64-gnu` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.5...v1.15.6)

Updates `@swc/core-linux-x64-musl` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.5...v1.15.6)

Updates `@swc/core-win32-arm64-msvc` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.5...v1.15.6)

Updates `@swc/core-win32-ia32-msvc` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.5...v1.15.6)

Updates `@swc/core-win32-x64-msvc` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: "@swc/core"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-x64"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm-gnueabihf"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-musl"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-musl"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-arm64-msvc"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-ia32-msvc"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-x64-msvc"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-18 13:01:49 +01:00
dependabot[bot]
eec998cc8d lifecycle/aws: bump aws-cdk from 2.1100.0 to 2.1100.1 in /lifecycle/aws (#18922)
Bumps [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) from 2.1100.0 to 2.1100.1.
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1100.1/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  dependency-version: 2.1100.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-18 13:00:30 +01:00
authentik-automation[bot]
d01aa6bebf core, web: update translations (#18920)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-12-18 12:57:02 +01:00
dependabot[bot]
cbbf315662 web: bump knip from 5.74.0 to 5.75.1 in /web (#18924)
Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 5.74.0 to 5.75.1.
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/5.75.1/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 5.75.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-18 12:55:58 +01:00
dependabot[bot]
45ca767fd8 web: bump chromedriver from 143.0.1 to 143.0.2 in /web (#18926)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 143.0.1 to 143.0.2.
- [Commits](https://github.com/giggio/node-chromedriver/compare/143.0.1...143.0.2)

---
updated-dependencies:
- dependency-name: chromedriver
  dependency-version: 143.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-18 12:55:32 +01:00
dependabot[bot]
5d3e2e89e0 core: bump goauthentik/fips-debian from 189345a to 10dadf1 (#18927)
Bumps goauthentik/fips-debian from `189345a` to `10dadf1`.

---
updated-dependencies:
- dependency-name: goauthentik/fips-debian
  dependency-version: trixie-slim-fips
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-18 12:55:06 +01:00
Marcelo Elizeche Landó
5e2f261a0c tests/e2e: handle StaleElementReferenceException in parse_json_content (#18842)
Add retry logic and safer text recover
2025-12-17 20:27:03 -03:00
Tana M Berry
10a421e678 web/admin: add UI copy to RBAC modal (#18917)
add UI copy to RBAC modal
2025-12-17 20:23:20 +00:00
Marc 'risson' Schmitt
668ad3dadf root: fix docker-compose data mount (#18903) 2025-12-17 16:33:50 -03:00
João C. Fernandes
e7903d5391 core/groups: optimize prefetch queries to fetch only required fields (#18448)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-17 17:15:31 +00:00
Jens L.
e38fffc44c web/admin: reword some things on the device view page (#18785)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-17 17:33:36 +01:00
Marc 'risson' Schmitt
4bc2bca448 website/docs: 2025.12: remove superfluous changes (#18910) 2025-12-17 16:24:12 +00:00
Jens L.
48916303d8 api: fix latest version for public schema (#18902) 2025-12-17 15:12:14 +00:00
Marc 'risson' Schmitt
d28109da6a ci/release-tag: checkout correct branch for make test-docker (#18880) 2025-12-17 15:08:05 +00:00
Marc 'risson' Schmitt
3bd299d52a api: fix page_size with invalid query param (#18879) 2025-12-17 15:06:22 +00:00
Tana M Berry
57418582c5 website/docs: added list of Int Guide contributors (also edited frontmatter) (#18888)
* alphabetize

* added frontmatter

* Apply suggestions from code review

Signed-off-by: Dominic R <dominic@sdko.org>

---------

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-17 09:01:05 -06:00
Marc 'risson' Schmitt
f37958bcd0 tasks/middleware: close connections on worker status update database error (#18881) 2025-12-17 15:45:37 +01:00
dependabot[bot]
8931b621b4 core: bump goauthentik.io/api/v3 from 3.2026020.1 to 3.2026020.3 (#18892)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 13:24:57 +00:00
dependabot[bot]
9d3d96bab1 web: bump the eslint group across 1 directory with 5 updates (#18851)
Bumps the eslint group with 5 updates in the /web directory:

| Package | From | To |
| --- | --- | --- |
| [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) | `9.39.1` | `9.39.2` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.49.0` | `8.50.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.49.0` | `8.50.0` |
| [eslint](https://github.com/eslint/eslint) | `9.39.1` | `9.39.2` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.49.0` | `8.50.0` |



Updates `@eslint/js` from 9.39.1 to 9.39.2
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v9.39.2/packages/js)

Updates `@typescript-eslint/eslint-plugin` from 8.49.0 to 8.50.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.50.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.49.0 to 8.50.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.50.0/packages/parser)

Updates `eslint` from 9.39.1 to 9.39.2
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.1...v9.39.2)

Updates `typescript-eslint` from 8.49.0 to 8.50.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.50.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.39.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 9.39.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 13:15:56 +01:00
dependabot[bot]
712f0ed95e web: bump the swc group across 1 directory with 11 updates (#18818)
Bumps the swc group with 1 update in the /web directory: [@swc/core](https://github.com/swc-project/swc).


Updates `@swc/core` from 1.15.3 to 1.15.4
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.3...v1.15.4)

Updates `@swc/core-darwin-arm64` from 1.15.3 to 1.15.4
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.3...v1.15.4)

Updates `@swc/core-darwin-x64` from 1.15.3 to 1.15.4
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.3...v1.15.4)

Updates `@swc/core-linux-arm-gnueabihf` from 1.15.3 to 1.15.4
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.3...v1.15.4)

Updates `@swc/core-linux-arm64-gnu` from 1.15.3 to 1.15.4
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.3...v1.15.4)

Updates `@swc/core-linux-arm64-musl` from 1.15.3 to 1.15.4
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.3...v1.15.4)

Updates `@swc/core-linux-x64-gnu` from 1.15.3 to 1.15.4
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.3...v1.15.4)

Updates `@swc/core-linux-x64-musl` from 1.15.3 to 1.15.4
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.3...v1.15.4)

Updates `@swc/core-win32-arm64-msvc` from 1.15.3 to 1.15.4
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.3...v1.15.4)

Updates `@swc/core-win32-ia32-msvc` from 1.15.3 to 1.15.4
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.3...v1.15.4)

Updates `@swc/core-win32-x64-msvc` from 1.15.3 to 1.15.4
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: "@swc/core"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-x64"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm-gnueabihf"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-musl"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-musl"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-arm64-msvc"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-ia32-msvc"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-x64-msvc"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:32:37 +01:00
dependabot[bot]
1cd9c7bf9d web: bump the goauthentik group across 1 directory with 3 updates (#18819)
Bumps the goauthentik group with 3 updates in the /web directory: [@goauthentik/esbuild-plugin-live-reload](https://github.com/goauthentik/authentik/tree/HEAD/packages/esbuild-plugin-live-reload), [@goauthentik/eslint-config](https://github.com/goauthentik/authentik/tree/HEAD/packages/eslint-config) and [@goauthentik/prettier-config](https://github.com/goauthentik/authentik/tree/HEAD/packages/prettier-config).


Updates `@goauthentik/esbuild-plugin-live-reload` from 1.3.1 to 1.4.0
- [Release notes](https://github.com/goauthentik/authentik/releases)
- [Commits](https://github.com/goauthentik/authentik/commits/HEAD/packages/esbuild-plugin-live-reload)

Updates `@goauthentik/eslint-config` from 1.1.1 to 1.2.0
- [Release notes](https://github.com/goauthentik/authentik/releases)
- [Commits](https://github.com/goauthentik/authentik/commits/HEAD/packages/eslint-config)

Updates `@goauthentik/prettier-config` from 3.2.1 to 3.3.1
- [Release notes](https://github.com/goauthentik/authentik/releases)
- [Commits](https://github.com/goauthentik/authentik/commits/HEAD/packages/prettier-config)

---
updated-dependencies:
- dependency-name: "@goauthentik/esbuild-plugin-live-reload"
  dependency-version: 1.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: goauthentik
- dependency-name: "@goauthentik/eslint-config"
  dependency-version: 1.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: goauthentik
- dependency-name: "@goauthentik/prettier-config"
  dependency-version: 3.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: goauthentik
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:32:22 +01:00
dependabot[bot]
fb23751079 lifecycle/aws: bump aws-cdk from 2.1034.0 to 2.1100.0 in /lifecycle/aws (#18850)
Bumps [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) from 2.1034.0 to 2.1100.0.
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1100.0/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  dependency-version: 2.1100.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:32:01 +01:00
dependabot[bot]
e49aace000 web: bump @sentry/browser from 10.30.0 to 10.31.0 in /web in the sentry group across 1 directory (#18893)
web: bump @sentry/browser in /web in the sentry group across 1 directory

Bumps the sentry group with 1 update in the /web directory: [@sentry/browser](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/browser` from 10.30.0 to 10.31.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/10.30.0...10.31.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 10.31.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:31:27 +01:00
dependabot[bot]
876b299f30 web: bump the bundler group across 1 directory with 7 updates (#18894)
Bumps the bundler group with 2 updates in the /web directory: [@vitest/browser](https://github.com/vitest-dev/vitest/tree/HEAD/packages/browser) and [esbuild](https://github.com/evanw/esbuild).


Updates `@vitest/browser` from 4.0.15 to 4.0.16
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.0.16/packages/browser)

Updates `@vitest/browser-playwright` from 4.0.15 to 4.0.16
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.0.16/packages/browser-playwright)

Updates `esbuild` from 0.27.1 to 0.27.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.27.1...v0.27.2)

Updates `vitest` from 4.0.15 to 4.0.16
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.0.16/packages/vitest)

Updates `@esbuild/darwin-arm64` from 0.27.1 to 0.27.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.27.1...v0.27.2)

Updates `@esbuild/linux-arm64` from 0.27.1 to 0.27.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.27.1...v0.27.2)

Updates `@esbuild/linux-x64` from 0.27.1 to 0.27.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.27.1...v0.27.2)

---
updated-dependencies:
- dependency-name: "@vitest/browser"
  dependency-version: 4.0.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: "@vitest/browser-playwright"
  dependency-version: 4.0.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: esbuild
  dependency-version: 0.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: vitest
  dependency-version: 4.0.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: "@esbuild/darwin-arm64"
  dependency-version: 0.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: "@esbuild/linux-arm64"
  dependency-version: 0.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: "@esbuild/linux-x64"
  dependency-version: 0.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:31:15 +01:00
dependabot[bot]
458439c396 web: bump the rollup group across 1 directory with 4 updates (#18852)
Bumps the rollup group with 4 updates in the /web directory: [@rollup/rollup-darwin-arm64](https://github.com/rollup/rollup), [@rollup/rollup-linux-arm64-gnu](https://github.com/rollup/rollup), [@rollup/rollup-linux-x64-gnu](https://github.com/rollup/rollup) and [rollup](https://github.com/rollup/rollup).


Updates `@rollup/rollup-darwin-arm64` from 4.53.3 to 4.53.4
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.3...v4.53.4)

Updates `@rollup/rollup-linux-arm64-gnu` from 4.53.3 to 4.53.4
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.3...v4.53.4)

Updates `@rollup/rollup-linux-x64-gnu` from 4.53.3 to 4.53.4
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.3...v4.53.4)

Updates `rollup` from 4.53.3 to 4.53.4
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.3...v4.53.4)

---
updated-dependencies:
- dependency-name: "@rollup/rollup-darwin-arm64"
  dependency-version: 4.53.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
- dependency-name: "@rollup/rollup-linux-arm64-gnu"
  dependency-version: 4.53.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
- dependency-name: "@rollup/rollup-linux-x64-gnu"
  dependency-version: 4.53.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
- dependency-name: rollup
  dependency-version: 4.53.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:29:44 +01:00
dependabot[bot]
d3d0effe9d web: bump @types/node from 25.0.0 to 25.0.3 in /web (#18895)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.0.0 to 25.0.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:29:30 +01:00
dependabot[bot]
413b073191 web: bump knip from 5.73.3 to 5.74.0 in /web (#18896)
Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 5.73.3 to 5.74.0.
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/5.74.0/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 5.74.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:28:48 +01:00
dependabot[bot]
46747ae3f2 core: bump goauthentik/fips-debian from 2f19fc1 to 189345a (#18897)
Bumps goauthentik/fips-debian from `2f19fc1` to `189345a`.

---
updated-dependencies:
- dependency-name: goauthentik/fips-debian
  dependency-version: trixie-slim-fips
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:28:39 +01:00
dependabot[bot]
d64a3aab39 core: bump astral-sh/uv from 0.9.17 to 0.9.18 (#18898)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.9.17 to 0.9.18.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/uv/compare/0.9.17...0.9.18)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  dependency-version: 0.9.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:28:28 +01:00
Dominic R
970cddae47 website/integrations: bookstack: fix redir url (#18891) 2025-12-17 08:42:50 +00:00
authentik-automation[bot]
24c4495ac2 core, web: update translations (#18807)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-12-17 00:42:03 +00:00
Dewi Roberts
ff38607fa3 web/admin: endpoint: change wording and add helper text (#18871)
* Change wording and add helper text

* Fix helper text

* Setup text change

* Update web/src/admin/endpoints/connectors/agent/AgentConnectorSetup.ts

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update web/src/admin/endpoints/connectors/agent/AgentConnectorSetup.ts

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* Update web/src/admin/endpoints/connectors/agent/EnrollmentTokenForm.ts

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* Format.

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Co-authored-by: Teffen Ellis <teffen@sister.software>
2025-12-16 23:28:29 +00:00
Dominic R
eef8e57f6c web: fix file upload form (#18808)
* web: fix file upload form name mismatch and modal submit promise handling

Fixes the following error:

FileUploadForm.ts:74  POST http://authentik.localhost:9000/api/v3/admin/file/ 405 (Method Not Allowed)
(anonymous) @ fetch.ts:81
fetchApi @ runtime.ts:206
await in fetchApi
request @ runtime.ts:136
await in request
adminFileCreateRaw @ AdminApi.ts:191
adminFileCreate @ AdminApi.ts:206
send @ FileUploadForm.ts:74
submit @ Form.ts:363
(anonymous) @ ModalForm.ts:54
handleEvent @ lit-html.ts:2109
n @ helpers.ts:117Understand this error
Form.ts:403 authentik/forms: API rejected the form submission due to an invalid field that doesn't appear to be in the form. This is likely a bug in authentik. {detail: 'Response returned an error code'}
(anonymous) @ console.ts:39
(anonymous) @ Form.ts:403
Promise.catch
submit @ Form.ts:376
(anonymous) @ ModalForm.ts:54
handleEvent @ lit-html.ts:2109
n @ helpers.ts:117Understand this error
runtime.ts:140 Uncaught (in promise) ResponseError: Response returned an error code
    at mR.request (runtime.ts:140:15)
    at async mR.adminFileCreateRaw (AdminApi.ts:191:26)
    at async mR.adminFileCreate (AdminApi.ts:206:9)

- align file upload rename field with api name so validation errors map correctly
-improve custom filename extension logic to avoid double or incorrect  extensions
- prevent unhandled promise rejections from modal submit click handler and show  missing-form errors to users

* rev

* wip

* Update ModalForm.ts

Signed-off-by: Dominic R <dominic@sdko.org>

* scope better

* fix what it validates against

---------

Signed-off-by: Dominic R <dominic@sdko.org>
2025-12-16 18:37:22 +00:00
Jens L.
603820854b stages/authenticator_*: fix code input field not string (#18875)
* stages/authenticator_*: fix code input field not string

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Update authentik/stages/authenticator_totp/stage.py

Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens L. <jens@beryju.org>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-16 19:10:48 +01:00
dependabot[bot]
4ad7f8be2a web: bump vite from 7.2.7 to 7.3.0 in /web (#18854)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.2.7 to 7.3.0.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.0/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.0/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 18:44:54 +01:00
Alexander Tereshkin
a605cd1e87 web: fix notification counter (#18781) 2025-12-16 18:41:11 +01:00
Alexander Tereshkin
936789f534 web: add custom message with links for empty data export list (#18830) 2025-12-16 17:36:12 +01:00
Jens L.
2f52d832ab website/docs: 2025.10.3 release notes (#18868)
* website/docs: 2025.10.3 release notes

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* format

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-16 16:56:57 +01:00
Marcelo Elizeche Landó
036514730e website/docs: Add docs for passkey autofill (WebauthN Conditional UI) (#18805)
* Add docs for passkey autofill feature

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* improve configuration section

* remove blank lines

---------

Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-16 15:16:12 +00:00
Simonyi Gergő
d48129ba7b website/docs: adjust RBAC-related details in 2025.12 release notes (#18863)
* website/docs: adjust RBAC-related details in 2025.12 release notes

* adjust wording

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>

---------

Signed-off-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-12-16 15:02:00 +00:00
Jens L.
d219f72ed6 outposts: fix permission errors for related certificates (#18861)
* outposts: fix permission errors for related certificates

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-16 15:01:25 +01:00
Simonyi Gergő
7b19045431 web/admin/rbac: misc object permission fixes (#18859)
* rbac: relabel "Assign role permissions" button

* fix object permissions in permissions table

This should have a backend-based fix in the future.
2025-12-16 14:33:25 +01:00
dependabot[bot]
0027813e4b core: bump library/golang from 5d35fb8 to 8e8f9c8 (#18855)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 13:09:10 +00:00
Simonyi Gergő
a6ebf1074f rbac: alter migrated direct permission roles (#18860)
These should not be marked as managed, because many of these permissions
were created by admins.
2025-12-16 13:40:49 +01:00
Marcelo Elizeche Landó
ea9689c493 core: add skip s3_test_server_available to TestResolveFileUrlS3Backend (#18858)
add skip s3_test_server_available to TestResolveFileUrlS3Backend
2025-12-16 12:59:13 +01:00
Jens L.
06e7335618 ci: replace codecov test-results action (#18862)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-16 12:50:54 +01:00
dependabot[bot]
42c4fee053 core: bump goauthentik/fips-debian from c10cd2c to 2f19fc1 (#18856)
Bumps goauthentik/fips-debian from `c10cd2c` to `2f19fc1`.

---
updated-dependencies:
- dependency-name: goauthentik/fips-debian
  dependency-version: trixie-slim-fips
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 12:33:13 +01:00
Dominic R
26cfbe67f3 admin/files: fix get_objects_for_user queryset argument in FileUsedByView (#18845)
Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-12-16 00:39:13 +00:00
Marcelo Elizeche Landó
2a17024afc core: skip s3 tests if endpoint isn't available (#18841)
skip s3 tests if endpoint isn't available
2025-12-15 20:22:59 -03:00
Connor Peshek
c557b55e0e crypto: Store details parsed from includeDetails in database instead (#18013)
* crypto: Store details parsed from includeDetails in database instead

* fix signal for tests

* Update authentik/crypto/signals.py

Co-authored-by: Jens L. <jens@goauthentik.io>
Signed-off-by: Connor Peshek <connor@connorpeshek.me>

* Update authentik/crypto/apps.py

Co-authored-by: Jens L. <jens@goauthentik.io>
Signed-off-by: Connor Peshek <connor@connorpeshek.me>

* Update authentik/crypto/signals.py

Co-authored-by: Jens L. <jens@goauthentik.io>
Signed-off-by: Connor Peshek <connor@connorpeshek.me>

* Add feedback

* cleanup

* update

* cleanup

* simplify serializer

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Update KID for when updating certificates

* lint

---------

Signed-off-by: Connor Peshek <connor@connorpeshek.me>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: connor peshek <connorpeshek@connors-MacBook-Pro.local>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-12-15 13:50:16 -06:00
Roi Gabay
f56e354e38 website/docs: add jellyseer integration doc (#18812)
* website/docs: add jellyseer integration doc

* Slight tweaks

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/media/jellyseerr/index.md

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: dewi-tik <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-15 17:12:06 +00:00
Marc 'risson' Schmitt
c50c2b0e0c admin/files: revert add check for /media existence (#18636) (#18829) 2025-12-15 15:29:21 +00:00
dependabot[bot]
662124cac9 core: bump goauthentik.io/api/v3 from 3.2025120.26 to 3.2026020.1 (#18815)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 15:20:48 +00:00
Marc 'risson' Schmitt
3d671a901b packages/django-dramatiq-postgres: broker: close django connections on consumer close (#18833)
Co-authored-by: Norman Ziebal <norman.ziebal@mail.schwarz>
2025-12-15 14:59:51 +01:00
Simonyi Gergő
a7fb031b64 core: remove superuser check from Token list (#18684) 2025-12-15 14:29:42 +01:00
Dewi Roberts
2818b0bbdf website/docs: add icon info to style guide (#18832) 2025-12-15 13:27:22 +00:00
Ryan Pesek
60075e39fb core: list applications fix (#18798) 2025-12-15 13:16:07 +01:00
dependabot[bot]
c112f702b3 ci: bump actions/cache from 5.0.0 to 5.0.1 (#18823)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 13:15:05 +01:00
dependabot[bot]
42b3323b3d ci: bump actions/download-artifact from 6.0.0 to 7.0.0 (#18825)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 13:14:22 +01:00
dependabot[bot]
78380831de core: bump goauthentik/fips-debian from 07f41ce to c10cd2c (#18822)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 13:14:09 +01:00
dependabot[bot]
8b5195aeff ci: bump actions/upload-artifact from 5.0.0 to 6.0.0 (#18824)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](330a01c490...b7c566a772)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 12:45:16 +01:00
dependabot[bot]
d762e38027 ci: bump astral-sh/setup-uv from 7.1.5 to 7.1.6 in /.github/actions/setup (#18826)
ci: bump astral-sh/setup-uv in /.github/actions/setup

Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 7.1.5 to 7.1.6.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](ed21f2f24f...681c641aba)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 7.1.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 12:36:47 +01:00
Marcelo Elizeche Landó
e427cb611e root: Add macOS support for sed in Makefile (#18795)
Add macOS support for sed
2025-12-15 12:09:35 +01:00
authentik-automation[bot]
20dbcf2e7b core, web: update translations (#18804)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-12-14 00:55:55 +01:00
dependabot[bot]
d93138f790 lifecycle/aws: bump aws-cdk from 2.1033.0 to 2.1034.0 in /lifecycle/aws (#18771)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-14 00:53:47 +01:00
Jens L.
9ef7f706e9 internal: don't warn on empty outpost for embedded (#18786)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-14 00:50:58 +01:00
dependabot[bot]
627176ab7e web: bump the storybook group across 1 directory with 5 updates (#18774)
Bumps the storybook group with 4 updates in the /web directory: [@storybook/addon-docs](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/docs), [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links), [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) and [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite).


Updates `@storybook/addon-docs` from 10.1.6 to 10.1.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.7/code/addons/docs)

Updates `@storybook/addon-links` from 10.1.6 to 10.1.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.7/code/addons/links)

Updates `@storybook/web-components` from 10.1.6 to 10.1.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.7/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 10.1.6 to 10.1.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.7/code/frameworks/web-components-vite)

Updates `storybook` from 10.1.6 to 10.1.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.7/code/core)

---
updated-dependencies:
- dependency-name: "@storybook/addon-docs"
  dependency-version: 10.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-version: 10.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-version: 10.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-version: 10.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-version: 10.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-13 11:22:11 +01:00
dependabot[bot]
069622aea4 web: bump chromedriver from 143.0.0 to 143.0.1 in /web (#18776)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 143.0.0 to 143.0.1.
- [Commits](https://github.com/giggio/node-chromedriver/compare/143.0.0...143.0.1)

---
updated-dependencies:
- dependency-name: chromedriver
  dependency-version: 143.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-13 11:21:43 +01:00
dependabot[bot]
3da523cbd5 web: bump the react group across 1 directory with 2 updates (#18775)
Bumps the react group with 2 updates in the /web directory: [react](https://github.com/facebook/react/tree/HEAD/packages/react) and [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom).


Updates `react` from 19.2.1 to 19.2.3
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.3/packages/react)

Updates `react-dom` from 19.2.1 to 19.2.3
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.3/packages/react-dom)

---
updated-dependencies:
- dependency-name: react
  dependency-version: 19.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: react
- dependency-name: react-dom
  dependency-version: 19.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: react
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-13 11:18:06 +01:00
Dominic R
126310138d web/admin: fix read-only provider selection for application form (#18768)
web/admin: Add read-only provider selection for application form

One of the ways to "bind" an application to a provider is to click "Create" under "Assigned to application" in the provider view. This PR fixes 2 issues:

* The value is now auto-filled, so the user doesn't need to do that anymore
* The value is now read-only, you don't need to change it since it's well for that provider. If that makes sense
2025-12-12 22:40:55 +00:00
Marcelo Elizeche Landó
9f1e55fbe6 website/release notes: Update v2025.12 release notes (#18797)
* website/release notes: Update v2025.12 release notes

Signed-off-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>

* fix linting

---------

Signed-off-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-12-12 22:25:10 +00:00
Marcelo Elizeche Landó
5997cda48b tests/e2e: retry detached shadow roots (#18796)
tests(e2e): retry detached shadow roots
2025-12-12 22:09:15 +00:00
authentik-automation[bot]
fbe8028b08 root: bump version to 2026.2.0-rc1 (#18794)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-12-12 20:59:47 +00:00
855 changed files with 130141 additions and 19905 deletions

View File

@@ -115,13 +115,20 @@ runs:
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.token }}
PR_NUMBER: ${{ steps.should_run.outputs.pr_number }}
REASON: ${{ steps.should_run.outputs.reason }}
run: |
set -e -o pipefail
PR_NUMBER="${{ steps.should_run.outputs.pr_number }}"
# Get PR details
PR_DATA=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER)
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.user.login')
echo "pr_title=$PR_TITLE" >> $GITHUB_OUTPUT
echo "pr_author=$PR_AUTHOR" >> $GITHUB_OUTPUT
# Determine which labels to process
if [ "${REASON}" = "label_added_to_merged_pr" ]; then
if [ "${{ steps.should_run.outputs.reason }}" = "label_added_to_merged_pr" ]; then
# Only process the specific label that was just added
if [ "${{ github.event_name }}" = "issues" ]; then
LABEL_NAME="${{ github.event.label.name }}"
@@ -145,13 +152,13 @@ runs:
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.token }}
PR_NUMBER: '${{ steps.should_run.outputs.pr_number }}'
COMMIT_SHA: '${{ steps.should_run.outputs.merge_commit_sha }}'
PR_TITLE: ${{ github.event.pull_request.title }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
LABELS: '${{ steps.pr_details.outputs.labels }}'
run: |
set -e -o pipefail
PR_NUMBER='${{ steps.should_run.outputs.pr_number }}'
COMMIT_SHA='${{ steps.should_run.outputs.merge_commit_sha }}'
PR_TITLE='${{ steps.pr_details.outputs.pr_title }}'
PR_AUTHOR='${{ steps.pr_details.outputs.pr_author }}'
LABELS='${{ steps.pr_details.outputs.labels }}'
echo "Processing PR #$PR_NUMBER (reason: ${{ steps.should_run.outputs.reason }})"
echo "Found backport labels: $LABELS"

View File

@@ -8,11 +8,6 @@ inputs:
postgresql_version:
description: "Optional postgresql image tag"
default: "16"
working-directory:
description: |
Optional working directory if this repo isn't in the root of the actions workspace.
When set, needs to contain a trailing slash
default: ""
runs:
using: "composite"
@@ -23,53 +18,50 @@ runs:
run: |
sudo apt-get remove --purge man-db
sudo apt-get update
sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext krb5-multidev libkrb5-dev heimdal-multidev libclang-dev krb5-kdc krb5-user krb5-admin-server
sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext libkrb5-dev krb5-kdc krb5-user krb5-admin-server
sudo rm -rf /usr/local/lib/android
- name: Install uv
if: ${{ contains(inputs.dependencies, 'python') }}
uses: astral-sh/setup-uv@ed21f2f24f8dd64503750218de024bcf64c7250a # v5
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v5
with:
enable-cache: true
- name: Setup python
if: ${{ contains(inputs.dependencies, 'python') }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v5
with:
python-version-file: "${{ inputs.working-directory }}pyproject.toml"
python-version-file: "pyproject.toml"
- name: Install Python deps
if: ${{ contains(inputs.dependencies, 'python') }}
shell: bash
working-directory: ${{ inputs.working-directory }}
run: uv sync --all-extras --dev --frozen
- name: Setup node
if: ${{ contains(inputs.dependencies, 'node') }}
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v4
with:
node-version-file: ${{ inputs.working-directory }}web/package.json
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: ${{ inputs.working-directory }}web/package-lock.json
cache-dependency-path: web/package-lock.json
registry-url: 'https://registry.npmjs.org'
- name: Setup go
if: ${{ contains(inputs.dependencies, 'go') }}
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v5
with:
go-version-file: "${{ inputs.working-directory }}go.mod"
go-version-file: "go.mod"
- name: Setup docker cache
if: ${{ contains(inputs.dependencies, 'runtime') }}
uses: AndreKurait/docker-cache@0fe76702a40db986d9663c24954fc14c6a6031b7
with:
key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/docker-compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }}
key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }}
- name: Setup dependencies
if: ${{ contains(inputs.dependencies, 'runtime') }}
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
export PSQL_TAG=${{ inputs.postgresql_version }}
docker compose -f .github/actions/setup/docker-compose.yml up -d
cd web && npm ci
docker compose -f .github/actions/setup/compose.yml up -d
cd web && npm i
- name: Generate config
if: ${{ contains(inputs.dependencies, 'python') }}
shell: uv run python {0}
working-directory: ${{ inputs.working-directory }}
run: |
from authentik.lib.generators import generate_id
from yaml import safe_dump

View File

@@ -2,7 +2,7 @@ services:
postgresql:
image: docker.io/library/postgres:${PSQL_TAG:-16}
volumes:
- db-data:/var/lib/postgresql
- db-data:/var/lib/postgresql/data
command: "-c log_statement=all"
environment:
POSTGRES_USER: authentik
@@ -11,11 +11,6 @@ services:
ports:
- 5432:5432
restart: always
redis:
image: docker.io/library/redis:7
ports:
- 6379:6379
restart: always
s3:
container_name: s3
image: docker.io/zenko/cloudserver

View File

@@ -12,11 +12,11 @@ runs:
with:
flags: ${{ inputs.flags }}
use_oidc: true
- uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1
- uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
with:
flags: ${{ inputs.flags }}
file: unittest.xml
use_oidc: true
report_type: test_results
- name: PostgreSQL Logs
shell: bash
run: |

View File

@@ -44,7 +44,7 @@ jobs:
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
- uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -78,18 +78,14 @@ jobs:
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6
with:
go-version-file: "go.mod"
- name: Generate API Clients
run: |
make gen-client-ts
make gen-client-go
- name: generate ts client
run: make gen-client-ts
- name: Build Docker Image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
id: push
with:
context: .
file: lifecycle/container/Dockerfile
push: ${{ steps.ev.outputs.shouldPush == 'true' }}
secrets: |
GEOIPUPDATE_ACCOUNT_ID=${{ secrets.GEOIPUPDATE_ACCOUNT_ID }}
@@ -100,7 +96,7 @@ jobs:
platforms: linux/${{ inputs.image_arch }}
cache-from: type=registry,ref=${{ steps.ev.outputs.attestImageNames }}:buildcache-${{ inputs.image_arch }}
cache-to: ${{ steps.ev.outputs.cacheTo }}
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # v3
id: attest
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
with:

View File

@@ -90,14 +90,14 @@ jobs:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: int128/docker-manifest-create-action@b60433fd4312d7a64a56d769b76ebe3f45cf36b4 # v2
- uses: int128/docker-manifest-create-action@6cdd53a8337cd50bc3ef8c7016579d8d460edd94 # v2
id: build
with:
tags: ${{ matrix.tag }}
sources: |
${{ steps.ev.outputs.attestImageNames }}@${{ needs.build-server-amd64.outputs.image-digest }}
${{ steps.ev.outputs.attestImageNames }}@${{ needs.build-server-arm64.outputs.image-digest }}
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # v3
id: attest
with:
subject-name: ${{ steps.ev.outputs.attestImageNames }}

View File

@@ -21,7 +21,7 @@ jobs:
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
with:
token: ${{ steps.generate_token.outputs.token }}

View File

@@ -41,7 +41,7 @@ jobs:
- working-directory: website/
name: Install Dependencies
run: npm ci
- uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v4
- uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v4
with:
path: |
${{ github.workspace }}/website/api/.docusaurus
@@ -55,7 +55,7 @@ jobs:
env:
NODE_ENV: production
run: npm run build -w api
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v4
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v4
with:
name: api-docs
path: website/api/build
@@ -67,7 +67,7 @@ jobs:
- build
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v5
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v5
with:
name: api-docs
path: website/api/build

View File

@@ -75,7 +75,7 @@ jobs:
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -101,7 +101,7 @@ jobs:
context: .
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-docs:buildcache
cache-to: ${{ steps.ev.outputs.shouldPush == 'true' && 'type=registry,ref=ghcr.io/goauthentik/dev-docs:buildcache,mode=max' || '' }}
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # v3
id: attest
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
with:

View File

@@ -24,5 +24,5 @@ jobs:
dir="/tmp/authentik/${{ matrix.version }}"
mkdir -p $dir
cd $dir
wget https://${{ matrix.version }}.goauthentik.io/docker-compose.yml
wget https://${{ matrix.version }}.goauthentik.io/compose.yml
${current}/scripts/test_docker.sh

View File

@@ -84,7 +84,7 @@ jobs:
# Current version family based on
current_version_family=$(cat internal/constants/VERSION | grep -vE -- 'rc[0-9]+$' || true)
if [[ -n $current_version_family ]]; then
prev_stable="version/${current_version_family}"
prev_stable=$current_version_family
fi
echo "::notice::Checking out ${prev_stable} as stable version..."
git checkout ${prev_stable}
@@ -95,10 +95,7 @@ jobs:
with:
postgresql_version: ${{ matrix.psql }}
- name: run migrations to stable
run: |
docker ps
docker logs setup-postgresql-1
uv run python -m lifecycle.migrate
run: uv run python -m lifecycle.migrate
- name: checkout current code
run: |
set -x
@@ -196,15 +193,17 @@ jobs:
glob: tests/e2e/test_source_scim*
- name: flows
glob: tests/e2e/test_flows*
- name: endpoints
glob: tests/e2e/test_endpoints_*
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Setup e2e env (chrome, etc)
run: |
docker compose -f tests/e2e/docker-compose.yml up -d --quiet-pull
docker compose -f tests/e2e/compose.yml up -d --quiet-pull
- id: cache-web
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v4
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v4
with:
path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
@@ -224,11 +223,60 @@ jobs:
if: ${{ always() }}
with:
flags: e2e
test-openid-conformance:
name: test-openid-conformance (${{ matrix.job.name }})
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
job:
- name: basic
glob: tests/openid_conformance/test_basic.py
- name: implicit
glob: tests/openid_conformance/test_implicit.py
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Setup e2e env (chrome, etc)
run: |
docker compose -f tests/e2e/compose.yml up -d --quiet-pull
- name: Setup conformance suite
run: |
docker compose -f tests/openid_conformance/compose.yml up -d --quiet-pull
- id: cache-web
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v4
with:
path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
- name: prepare web ui
if: steps.cache-web.outputs.cache-hit != 'true'
working-directory: web
run: |
npm ci
make -C .. gen-client-ts
npm run build
npm run build:sfe
- name: run conformance
run: |
uv run coverage run manage.py test ${{ matrix.job.glob }}
uv run coverage xml
- uses: ./.github/actions/test-results
if: ${{ always() }}
with:
flags: conformance
- if: ${{ !cancelled() }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: conformance-certification-${{ matrix.job.name }}
path: tests/openid_conformance/exports/
ci-core-mark:
if: always()
needs:
- lint
- test-migrations
- test-migrations-from-stable
- test-unittest
- test-integration
- test-e2e

View File

@@ -92,7 +92,7 @@ jobs:
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -114,7 +114,7 @@ jobs:
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
with:
tags: ${{ steps.ev.outputs.imageTags }}
file: ${{ matrix.type }}.Dockerfile
file: lifecycle/container/${{ matrix.type }}.Dockerfile
push: ${{ steps.ev.outputs.shouldPush == 'true' }}
build-args: |
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}
@@ -122,7 +122,7 @@ jobs:
context: .
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-${{ matrix.type }}:buildcache
cache-to: ${{ steps.ev.outputs.shouldPush == 'true' && format('type=registry,ref=ghcr.io/goauthentik/dev-{0}:buildcache,mode=max', matrix.type) || '' }}
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # v3
id: attest
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
with:

View File

@@ -32,7 +32,7 @@ jobs:
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
with:
token: ${{ steps.generate_token.outputs.token }}

View File

@@ -19,7 +19,7 @@ jobs:
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
with:
token: ${{ steps.generate_token.outputs.token }}

View File

@@ -14,7 +14,7 @@ jobs:
if: ${{ env.GH_APP_ID != '' }}
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
env:
GH_APP_ID: ${{ secrets.GH_APP_ID }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5

View File

@@ -19,7 +19,7 @@ jobs:
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Delete 'dev' containers older than a week
uses: snok/container-retention-policy@3b0972b2276b171b212f8c4efbca59ebba26eceb # v3.0.1
with:

View File

@@ -32,7 +32,7 @@ jobs:
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Checkout main
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
with:
@@ -60,7 +60,7 @@ jobs:
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Checkout main
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
with:

View File

@@ -35,7 +35,7 @@ jobs:
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -58,7 +58,7 @@ jobs:
push: true
platforms: linux/amd64,linux/arm64
context: .
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # v3
id: attest
if: true
with:
@@ -87,15 +87,10 @@ jobs:
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
with:
go-version-file: "go.mod"
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v5
with:
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
@@ -103,10 +98,10 @@ jobs:
DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
with:
image-name: ghcr.io/goauthentik/${{ matrix.type }},authentik/${{ matrix.type }}
- name: Generate API Clients
- name: make empty clients
run: |
make gen-client-ts
make gen-client-go
mkdir -p ./gen-ts-api
mkdir -p ./gen-go-api
- name: Docker Login Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
with:
@@ -126,10 +121,10 @@ jobs:
build-args: |
VERSION=${{ github.ref }}
tags: ${{ steps.ev.outputs.imageTags }}
file: ${{ matrix.type }}.Dockerfile
file: lifecycle/container/${{ matrix.type }}.Dockerfile
platforms: linux/amd64,linux/arm64
context: .
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # v3
id: attest
with:
subject-name: ${{ steps.ev.outputs.attestImageNames }}
@@ -160,21 +155,11 @@ jobs:
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Install web dependencies
working-directory: web/
run: |
npm ci
- name: Generate API Clients
run: |
make gen-client-ts
make gen-client-go
- name: Build web
working-directory: web/
run: |
npm ci
npm run build-proxy
- name: Build API client
run: |
make gen-client-go
- name: Build outpost
run: |
set -x
@@ -247,7 +232,7 @@ jobs:
container=$(docker container create ${{ steps.ev.outputs.imageMainName }})
docker cp ${container}:web/ .
- name: Create a Sentry.io release
uses: getsentry/action-release@128c5058bbbe93c8e02147fe0a9c713f166259a6 # v3
uses: getsentry/action-release@dab6548b3c03c4717878099e43782cf5be654289 # v3
continue-on-error: true
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}

View File

@@ -55,8 +55,6 @@ jobs:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
with:
ref: "version-${{ needs.check-inputs.outputs.major_version }}"
- name: Setup authentik env
uses: ./.github/actions/setup
- run: make test-docker
bump-authentik:
name: Bump authentik version
@@ -70,7 +68,7 @@ jobs:
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- id: get-user-id
name: Get GitHub app user ID
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
@@ -91,7 +89,6 @@ jobs:
# ID from https://api.github.com/users/authentik-automation[bot]
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com'
git pull
git commit -a -m "release: ${{ inputs.version }}" --allow-empty
git tag "version/${{ inputs.version }}" HEAD -m "version/${{ inputs.version }}"
git push --follow-tags
@@ -118,7 +115,7 @@ jobs:
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
repositories: helm
- id: get-user-id
name: Get GitHub app user ID
@@ -160,7 +157,7 @@ jobs:
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
repositories: version
- id: get-user-id
name: Get GitHub app user ID

View File

@@ -18,7 +18,7 @@ jobs:
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10
with:
repo-token: ${{ steps.generate_token.outputs.token }}

View File

@@ -24,7 +24,7 @@ jobs:
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
if: ${{ github.event_name != 'pull_request' }}
with:

3
.gitignore vendored
View File

@@ -211,4 +211,5 @@ source_docs/
/vendor/
### Docker ###
docker-compose.override.yml
tests/openid_conformance/exports/*.zip
compose.override.yml

View File

@@ -16,10 +16,8 @@ go.sum @goauthentik/backend
# Infrastructure
.github/ @goauthentik/infrastructure
lifecycle/aws/ @goauthentik/infrastructure
Dockerfile @goauthentik/infrastructure
*Dockerfile @goauthentik/infrastructure
lifecycle/container/ @goauthentik/infrastructure
.dockerignore @goauthentik/infrastructure
docker-compose.yml @goauthentik/infrastructure
Makefile @goauthentik/infrastructure
.editorconfig @goauthentik/infrastructure
CODEOWNERS @goauthentik/infrastructure

View File

@@ -9,6 +9,13 @@ NPM_VERSION = $(shell python -m scripts.generate_semver)
PY_SOURCES = authentik packages tests scripts lifecycle .github
DOCKER_IMAGE ?= "authentik:test"
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
SED_INPLACE = sed -i ''
else
SED_INPLACE = sed -i
endif
GEN_API_TS = gen-ts-api
GEN_API_PY = gen-py-api
GEN_API_GO = gen-go-api
@@ -119,11 +126,11 @@ bump: ## Bump authentik version. Usage: make bump version=20xx.xx.xx
ifndef version
$(error Usage: make bump version=20xx.xx.xx )
endif
$(eval current_version := $(shell cat ${PWD}/internal/constants/VERSION))
sed -i 's/^version = ".*"/version = "$(version)"/' pyproject.toml
sed -i 's/^VERSION = ".*"/VERSION = "$(version)"/' authentik/__init__.py
$(SED_INPLACE) 's/^version = ".*"/version = "$(version)"/' pyproject.toml
$(SED_INPLACE) 's/^VERSION = ".*"/VERSION = "$(version)"/' authentik/__init__.py
$(MAKE) gen-build gen-compose aws-cfn
sed -i "s/\"${current_version}\"/\"$(version)\"/" ${PWD}/package.json ${PWD}/package-lock.json ${PWD}/web/package.json ${PWD}/web/package-lock.json
npm version --no-git-tag-version --allow-same-version $(version)
cd ${PWD}/web && npm version --no-git-tag-version --allow-same-version $(version)
echo -n $(version) > ${PWD}/internal/constants/VERSION
#########################
@@ -134,14 +141,10 @@ gen-build: ## Extract the schema from the database
AUTHENTIK_DEBUG=true \
AUTHENTIK_TENANTS__ENABLED=true \
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
uv run ak make_blueprint_schema --file blueprints/schema.json
AUTHENTIK_DEBUG=true \
AUTHENTIK_TENANTS__ENABLED=true \
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
uv run ak spectacular --file schema.yml
uv run ak build_schema
gen-compose:
uv run scripts/generate_docker_compose.py
uv run scripts/generate_compose.py
gen-changelog: ## (Release) generate the changelog based from the commits since the last tag
git log --pretty=format:" - %s" $(shell git describe --tags $(shell git rev-list --tags --max-count=1))...$(shell git branch --show-current) | sort > changelog.md
@@ -149,14 +152,14 @@ gen-changelog: ## (Release) generate the changelog based from the commits since
gen-diff: ## (Release) generate the changelog diff between the current schema and the last tag
git show $(shell git describe --tags $(shell git rev-list --tags --max-count=1)):schema.yml > schema-old.yml
docker compose -f scripts/api/docker-compose.yml run --rm --user "${UID}:${GID}" diff \
docker compose -f scripts/api/compose.yml run --rm --user "${UID}:${GID}" diff \
--markdown \
/local/diff.md \
/local/schema-old.yml \
/local/schema.yml
rm schema-old.yml
sed -i 's/{/&#123;/g' diff.md
sed -i 's/}/&#125;/g' diff.md
$(SED_INPLACE) 's/{/&#123;/g' diff.md
$(SED_INPLACE) 's/}/&#125;/g' diff.md
npx prettier --write diff.md
gen-clean-ts: ## Remove generated API client for TypeScript
@@ -172,7 +175,7 @@ gen-clean-go: ## Remove generated API client for Go
gen-clean: gen-clean-ts gen-clean-go gen-clean-py ## Remove generated API clients
gen-client-ts: gen-clean-ts ## Build and install the authentik API for Typescript into the authentik UI Application
docker compose -f scripts/api/docker-compose.yml run --rm --user "${UID}:${GID}" gen \
docker compose -f scripts/api/compose.yml run --rm --user "${UID}:${GID}" gen \
generate \
-i /local/schema.yml \
-g typescript-fetch \
@@ -188,15 +191,24 @@ gen-client-ts: gen-clean-ts ## Build and install the authentik API for Typescri
gen-client-py: gen-clean-py ## Build and install the authentik API for Python
mkdir -p ${PWD}/${GEN_API_PY}
ifeq ($(wildcard ${PWD}/${GEN_API_PY}/.*),)
git clone --depth 1 https://github.com/goauthentik/client-python.git ${PWD}/${GEN_API_PY}
else
cd ${PWD}/${GEN_API_PY} && git pull
endif
cp ${PWD}/schema.yml ${PWD}/${GEN_API_PY}
make -C ${PWD}/${GEN_API_PY} build version=${NPM_VERSION}
gen-client-go: gen-clean-go ## Build and install the authentik API for Golang
gen-client-go: ## Build and install the authentik API for Golang
mkdir -p ${PWD}/${GEN_API_GO}
ifeq ($(wildcard ${PWD}/${GEN_API_GO}/.*),)
git clone --depth 1 https://github.com/goauthentik/client-go.git ${PWD}/${GEN_API_GO}
else
cd ${PWD}/${GEN_API_GO} && git reset --hard
cd ${PWD}/${GEN_API_GO} && git pull
endif
cp ${PWD}/schema.yml ${PWD}/${GEN_API_GO}
make -C ${PWD}/${GEN_API_GO} build version=${NPM_VERSION}
make -C ${PWD}/${GEN_API_GO} build
go mod edit -replace goauthentik.io/api/v3=./${GEN_API_GO}
gen-dev-config: ## Generate a local development config file
@@ -284,7 +296,7 @@ docs-api-clean: ## Clean generated API documentation
docker: ## Build a docker image of the current source tree
mkdir -p ${GEN_API_TS}
DOCKER_BUILDKIT=1 docker build . --progress plain --tag ${DOCKER_IMAGE}
DOCKER_BUILDKIT=1 docker build . -f lifecycle/container/Dockerfile --progress plain --tag ${DOCKER_IMAGE}
test-docker:
BUILD=true ${PWD}/scripts/test_docker.sh

View File

@@ -20,8 +20,8 @@ Even if the issue is not a CVE, we still greatly appreciate your help in hardeni
| Version | Supported |
| ---------- | ---------- |
| 2025.8.x | ✅ |
| 2025.10.x | ✅ |
| 2025.12.x | ✅ |
## Reporting a Vulnerability

View File

@@ -3,7 +3,7 @@
from functools import lru_cache
from os import environ
VERSION = "2025.12.4"
VERSION = "2026.2.0-rc1"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@@ -1,3 +1,5 @@
import mimetypes
from django.db.models import Q
from django.utils.translation import gettext as _
from drf_spectacular.utils import extend_schema
@@ -10,14 +12,13 @@ from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from authentik.admin.files.backends.base import get_content_type
from authentik.admin.files.fields import FileField as AkFileField
from authentik.admin.files.manager import get_file_manager
from authentik.admin.files.usage import FileApiUsage
from authentik.admin.files.validation import validate_upload_file_name
from authentik.api.validation import validate
from authentik.core.api.used_by import DeleteAction, UsedBySerializer
from authentik.core.api.utils import PassiveSerializer, ThemedUrlsSerializer
from authentik.core.api.utils import PassiveSerializer
from authentik.events.models import Event, EventAction
from authentik.lib.utils.reflection import get_apps
from authentik.rbac.permissions import HasPermission
@@ -25,6 +26,11 @@ from authentik.rbac.permissions import HasPermission
MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024 # 25MB
def get_mime_from_filename(filename: str) -> str:
mime_type, _ = mimetypes.guess_type(filename)
return mime_type or "application/octet-stream"
class FileView(APIView):
pagination_class = None
parser_classes = [MultiPartParser]
@@ -47,7 +53,6 @@ class FileView(APIView):
name = CharField()
mime_type = CharField()
url = CharField()
themed_urls = ThemedUrlsSerializer(required=False, allow_null=True)
@extend_schema(
parameters=[FileListParameters],
@@ -75,9 +80,8 @@ class FileView(APIView):
FileView.FileListSerializer(
data={
"name": file,
"url": manager.file_url(file, request),
"mime_type": get_content_type(file),
"themed_urls": manager.themed_urls(file, request),
"url": manager.file_url(file),
"mime_type": get_mime_from_filename(file),
}
)
for file in files
@@ -146,7 +150,7 @@ class FileView(APIView):
"pk": name,
"name": name,
"usage": usage.value,
"mime_type": get_content_type(name),
"mime_type": get_mime_from_filename(name),
},
).from_http(request)

View File

@@ -1,4 +1,3 @@
import mimetypes
from collections.abc import Callable, Generator, Iterator
from typing import cast
@@ -11,32 +10,6 @@ from authentik.admin.files.usage import FileUsage
CACHE_PREFIX = "goauthentik.io/admin/files"
LOGGER = get_logger()
# Theme variable placeholder for theme-specific files like logo-%(theme)s.png
THEME_VARIABLE = "%(theme)s"
def get_content_type(name: str) -> str:
"""Get MIME type for a file based on its extension."""
content_type, _ = mimetypes.guess_type(name)
return content_type or "application/octet-stream"
def get_valid_themes() -> list[str]:
"""Get valid themes that can be substituted for %(theme)s."""
from authentik.brands.api import Themes
return [t.value for t in Themes if t != Themes.AUTOMATIC]
def has_theme_variable(name: str) -> bool:
"""Check if filename contains %(theme)s variable."""
return THEME_VARIABLE in name
def substitute_theme(name: str, theme: str) -> str:
"""Replace %(theme)s with the given theme."""
return name.replace(THEME_VARIABLE, theme)
class Backend:
"""
@@ -102,29 +75,6 @@ class Backend:
"""
raise NotImplementedError
def themed_urls(
self,
name: str,
request: HttpRequest | None = None,
) -> dict[str, str] | None:
"""
Get URLs for each theme variant when filename contains %(theme)s.
Args:
name: File path potentially containing %(theme)s
request: Optional Django HttpRequest for URL building
Returns:
Dict mapping theme to URL if %(theme)s present, None otherwise
"""
if not has_theme_variable(name):
return None
return {
theme: self.file_url(substitute_theme(name, theme), request, use_cache=True)
for theme in get_valid_themes()
}
class ManageableBackend(Backend):
"""

View File

@@ -45,13 +45,8 @@ class FileBackend(ManageableBackend):
@property
def manageable(self) -> bool:
# Check _base_dir (the mount point, e.g. /data) rather than base_path
# (which includes usage/schema subdirs, e.g. /data/media/public).
# The subdirectories are created on first file write via mkdir(parents=True)
# in save_file(), so requiring them to exist beforehand would prevent
# file creation on fresh installs.
return (
self._base_dir.exists()
self.base_path.exists()
and (self._base_dir.is_mount() or (self._base_dir / self.usage.value).is_mount())
or (settings.DEBUG or settings.TEST)
)

View File

@@ -46,25 +46,3 @@ class PassthroughBackend(Backend):
) -> str:
"""Return the URL as-is for passthrough files."""
return name
def themed_urls(
self,
name: str,
request: HttpRequest | None = None,
) -> dict[str, str] | None:
"""Support themed URLs for external URLs with %(theme)s placeholder.
If the external URL contains %(theme)s, substitute it for each theme.
We can't verify that themed variants exist at the external location,
but we trust the user to provide valid URLs.
"""
from authentik.admin.files.backends.base import (
get_valid_themes,
has_theme_variable,
substitute_theme,
)
if not has_theme_variable(name):
return None
return {theme: substitute_theme(name, theme) for theme in get_valid_themes()}

View File

@@ -9,7 +9,7 @@ from botocore.exceptions import ClientError
from django.db import connection
from django.http.request import HttpRequest
from authentik.admin.files.backends.base import ManageableBackend, get_content_type
from authentik.admin.files.backends.base import ManageableBackend
from authentik.admin.files.usage import FileUsage
from authentik.lib.config import CONFIG
from authentik.lib.utils.time import timedelta_from_string
@@ -173,22 +173,7 @@ class S3Backend(ManageableBackend):
if custom_domain:
parsed = urlsplit(url)
scheme = "https" if use_https else "http"
path = parsed.path
# When using path-style addressing, the presigned URL contains the bucket
# name in the path (e.g., /bucket-name/key). Since custom_domain must
# include the bucket name (per docs), strip it from the path to avoid
# duplication. See: https://github.com/goauthentik/authentik/issues/19521
# Check with trailing slash to ensure exact bucket name match
if path.startswith(f"/{self.bucket_name}/"):
path = path.removeprefix(f"/{self.bucket_name}")
# Normalize to avoid double slashes
custom_domain = custom_domain.rstrip("/")
if not path.startswith("/"):
path = f"/{path}"
url = f"{scheme}://{custom_domain}{path}?{parsed.query}"
url = f"{scheme}://{custom_domain}{parsed.path}?{parsed.query}"
return url
@@ -204,7 +189,6 @@ class S3Backend(ManageableBackend):
Key=f"{self.base_path}/{name}",
Body=content,
ACL="private",
ContentType=get_content_type(name),
)
@contextmanager
@@ -220,7 +204,6 @@ class S3Backend(ManageableBackend):
Key=f"{self.base_path}/{name}",
ExtraArgs={
"ACL": "private",
"ContentType": get_content_type(name),
},
)

View File

@@ -165,31 +165,3 @@ class TestFileBackend(FileTestFileBackendMixin, TestCase):
def test_file_exists_false(self):
"""Test file_exists returns False for nonexistent file"""
self.assertFalse(self.backend.file_exists("does_not_exist.txt"))
def test_themed_urls_without_theme_variable(self):
"""Test themed_urls returns None when filename has no %(theme)s"""
file_name = "logo.png"
result = self.backend.themed_urls(file_name)
self.assertIsNone(result)
def test_themed_urls_with_theme_variable(self):
"""Test themed_urls returns dict of URLs for each theme"""
file_name = "logo-%(theme)s.png"
result = self.backend.themed_urls(file_name)
self.assertIsInstance(result, dict)
self.assertIn("light", result)
self.assertIn("dark", result)
# Check URLs contain the substituted theme
self.assertIn("logo-light.png", result["light"])
self.assertIn("logo-dark.png", result["dark"])
def test_themed_urls_multiple_theme_variables(self):
"""Test themed_urls with multiple %(theme)s in path"""
file_name = "%(theme)s/logo-%(theme)s.svg"
result = self.backend.themed_urls(file_name)
self.assertIsInstance(result, dict)
self.assertIn("light/logo-light.svg", result["light"])
self.assertIn("dark/logo-dark.svg", result["dark"])

View File

@@ -110,106 +110,3 @@ class TestS3Backend(FileTestS3BackendMixin, TestCase):
"""Test S3Backend with REPORTS usage"""
self.assertEqual(self.reports_s3_backend.usage, FileUsage.REPORTS)
self.assertEqual(self.reports_s3_backend.base_path, "reports/public")
@CONFIG.patch("storage.s3.secure_urls", True)
@CONFIG.patch("storage.s3.addressing_style", "path")
def test_file_url_custom_domain_with_bucket_no_duplicate(self):
"""Test file_url doesn't duplicate bucket name when custom_domain includes bucket.
Regression test for https://github.com/goauthentik/authentik/issues/19521
When using:
- Path-style addressing (bucket name goes in URL path, not subdomain)
- Custom domain that includes the bucket name (e.g., s3.example.com/bucket-name)
The bucket name should NOT appear twice in the final URL.
Example of the bug:
- custom_domain = "s3.example.com/authentik-media"
- boto3 presigned URL = "http://s3.example.com/authentik-media/media/public/file.png?..."
- Buggy result = "https://s3.example.com/authentik-media/authentik-media/media/public/file.png?..."
"""
bucket_name = self.media_s3_bucket_name
# Custom domain includes the bucket name
custom_domain = f"localhost:8020/{bucket_name}"
with CONFIG.patch("storage.media.s3.custom_domain", custom_domain):
url = self.media_s3_backend.file_url("application-icons/test.svg", use_cache=False)
# The bucket name should appear exactly once in the URL path, not twice
bucket_occurrences = url.count(bucket_name)
self.assertEqual(
bucket_occurrences,
1,
f"Bucket name '{bucket_name}' appears {bucket_occurrences} times in URL, expected 1. "
f"URL: {url}",
)
def test_themed_urls_without_theme_variable(self):
"""Test themed_urls returns None when filename has no %(theme)s"""
result = self.media_s3_backend.themed_urls("logo.png")
self.assertIsNone(result)
def test_themed_urls_with_theme_variable(self):
"""Test themed_urls returns dict of presigned URLs for each theme"""
result = self.media_s3_backend.themed_urls("logo-%(theme)s.png")
self.assertIsInstance(result, dict)
self.assertIn("light", result)
self.assertIn("dark", result)
# Check URLs are valid presigned URLs with correct file paths
self.assertIn("logo-light.png", result["light"])
self.assertIn("logo-dark.png", result["dark"])
self.assertIn("X-Amz-Signature=", result["light"])
self.assertIn("X-Amz-Signature=", result["dark"])
def test_themed_urls_multiple_theme_variables(self):
"""Test themed_urls with multiple %(theme)s in path"""
result = self.media_s3_backend.themed_urls("%(theme)s/logo-%(theme)s.svg")
self.assertIsInstance(result, dict)
self.assertIn("light/logo-light.svg", result["light"])
self.assertIn("dark/logo-dark.svg", result["dark"])
def test_save_file_sets_content_type_svg(self):
"""Test save_file sets correct ContentType for SVG files"""
self.media_s3_backend.save_file("test.svg", b"<svg></svg>")
response = self.media_s3_backend.client.head_object(
Bucket=self.media_s3_bucket_name,
Key="media/public/test.svg",
)
self.assertEqual(response["ContentType"], "image/svg+xml")
def test_save_file_sets_content_type_png(self):
"""Test save_file sets correct ContentType for PNG files"""
self.media_s3_backend.save_file("test.png", b"\x89PNG\r\n\x1a\n")
response = self.media_s3_backend.client.head_object(
Bucket=self.media_s3_bucket_name,
Key="media/public/test.png",
)
self.assertEqual(response["ContentType"], "image/png")
def test_save_file_stream_sets_content_type(self):
"""Test save_file_stream sets correct ContentType"""
with self.media_s3_backend.save_file_stream("test.css") as f:
f.write(b"body { color: red; }")
response = self.media_s3_backend.client.head_object(
Bucket=self.media_s3_bucket_name,
Key="media/public/test.css",
)
self.assertEqual(response["ContentType"], "text/css")
def test_save_file_unknown_extension_octet_stream(self):
"""Test save_file sets octet-stream for unknown extensions"""
self.media_s3_backend.save_file("test.unknownext123", b"data")
response = self.media_s3_backend.client.head_object(
Bucket=self.media_s3_bucket_name,
Key="media/public/test.unknownext123",
)
self.assertEqual(response["ContentType"], "application/octet-stream")

View File

@@ -88,28 +88,6 @@ class FileManager:
LOGGER.warning(f"Could not find file backend for file: {name}")
return ""
def themed_urls(
self,
name: str | None,
request: HttpRequest | Request | None = None,
) -> dict[str, str] | None:
"""
Get URLs for each theme variant when filename contains %(theme)s.
Returns dict mapping theme to URL if %(theme)s present, None otherwise.
"""
if not name:
return None
if isinstance(request, Request):
request = request._request
for backend in self.backends:
if backend.supports_file(name):
return backend.themed_urls(name, request)
return None
def _check_manageable(self) -> None:
if not self.manageable:
raise ImproperlyConfigured("No file management backend configured.")

View File

@@ -5,6 +5,7 @@ from io import BytesIO
from django.test import TestCase
from django.urls import reverse
from authentik.admin.files.api import get_mime_from_filename
from authentik.admin.files.manager import FileManager
from authentik.admin.files.tests.utils import FileTestFileBackendMixin
from authentik.admin.files.usage import FileUsage
@@ -93,9 +94,8 @@ class TestFileAPI(FileTestFileBackendMixin, TestCase):
self.assertIn(
{
"name": "/static/authentik/sources/ldap.png",
"url": "http://testserver/static/authentik/sources/ldap.png",
"url": "/static/authentik/sources/ldap.png",
"mime_type": "image/png",
"themed_urls": None,
},
response.data,
)
@@ -129,9 +129,8 @@ class TestFileAPI(FileTestFileBackendMixin, TestCase):
self.assertIn(
{
"name": "/static/authentik/sources/ldap.png",
"url": "http://testserver/static/authentik/sources/ldap.png",
"url": "/static/authentik/sources/ldap.png",
"mime_type": "image/png",
"themed_urls": None,
},
response.data,
)
@@ -201,64 +200,30 @@ class TestFileAPI(FileTestFileBackendMixin, TestCase):
self.assertEqual(response.status_code, 400)
self.assertIn("field is required", str(response.data))
def test_list_files_includes_themed_urls_none(self):
"""Test listing files includes themed_urls as None for non-themed files"""
manager = FileManager(FileUsage.MEDIA)
file_name = "test-no-theme.png"
manager.save_file(file_name, b"test content")
response = self.client.get(
reverse("authentik_api:files", query={"search": file_name, "manageableOnly": "true"})
)
class TestGetMimeFromFilename(TestCase):
"""Test get_mime_from_filename function"""
self.assertEqual(response.status_code, 200)
file_entry = next((f for f in response.data if f["name"] == file_name), None)
self.assertIsNotNone(file_entry)
self.assertIn("themed_urls", file_entry)
self.assertIsNone(file_entry["themed_urls"])
def test_image_png(self):
"""Test PNG image MIME type"""
self.assertEqual(get_mime_from_filename("test.png"), "image/png")
manager.delete_file(file_name)
def test_image_jpeg(self):
"""Test JPEG image MIME type"""
self.assertEqual(get_mime_from_filename("test.jpg"), "image/jpeg")
def test_list_files_includes_themed_urls_dict(self):
"""Test listing files includes themed_urls as dict for themed files"""
manager = FileManager(FileUsage.MEDIA)
file_name = "logo-%(theme)s.svg"
manager.save_file("logo-light.svg", b"<svg>light</svg>")
manager.save_file("logo-dark.svg", b"<svg>dark</svg>")
manager.save_file(file_name, b"<svg>placeholder</svg>")
def test_image_svg(self):
"""Test SVG image MIME type"""
self.assertEqual(get_mime_from_filename("test.svg"), "image/svg+xml")
response = self.client.get(
reverse("authentik_api:files", query={"search": "%(theme)s", "manageableOnly": "true"})
)
def test_text_plain(self):
"""Test text file MIME type"""
self.assertEqual(get_mime_from_filename("test.txt"), "text/plain")
self.assertEqual(response.status_code, 200)
file_entry = next((f for f in response.data if f["name"] == file_name), None)
self.assertIsNotNone(file_entry)
self.assertIn("themed_urls", file_entry)
self.assertIsInstance(file_entry["themed_urls"], dict)
self.assertIn("light", file_entry["themed_urls"])
self.assertIn("dark", file_entry["themed_urls"])
def test_unknown_extension(self):
"""Test unknown extension returns octet-stream"""
self.assertEqual(get_mime_from_filename("test.unknown"), "application/octet-stream")
manager.delete_file(file_name)
manager.delete_file("logo-light.svg")
manager.delete_file("logo-dark.svg")
def test_upload_file_with_theme_variable(self):
"""Test uploading file with %(theme)s in name"""
manager = FileManager(FileUsage.MEDIA)
file_name = "brand-logo-%(theme)s.svg"
file_content = b"<svg></svg>"
response = self.client.post(
reverse("authentik_api:files"),
{
"file": BytesIO(file_content),
"name": file_name,
"usage": FileUsage.MEDIA.value,
},
format="multipart",
)
self.assertEqual(response.status_code, 200)
self.assertTrue(manager.file_exists(file_name))
manager.delete_file(file_name)
def test_no_extension(self):
"""Test no extension returns octet-stream"""
self.assertEqual(get_mime_from_filename("test"), "application/octet-stream")

View File

@@ -1,7 +1,6 @@
"""Test file service layer"""
from unittest import skipUnless
from urllib.parse import urlparse
from django.http import HttpRequest
from django.test import TestCase
@@ -105,71 +104,3 @@ class TestResolveFileUrlS3Backend(FileTestS3BackendMixin, TestCase):
# S3 URLs should be returned as-is (already absolute)
self.assertTrue(result.startswith("http://s3.test:8080/test"))
class TestThemedUrls(FileTestFileBackendMixin, TestCase):
"""Test FileManager.themed_urls method"""
def test_themed_urls_none_path(self):
"""Test themed_urls returns None for None path"""
manager = FileManager(FileUsage.MEDIA)
result = manager.themed_urls(None)
self.assertIsNone(result)
def test_themed_urls_empty_path(self):
"""Test themed_urls returns None for empty path"""
manager = FileManager(FileUsage.MEDIA)
result = manager.themed_urls("")
self.assertIsNone(result)
def test_themed_urls_no_theme_variable(self):
"""Test themed_urls returns None when no %(theme)s in path"""
manager = FileManager(FileUsage.MEDIA)
result = manager.themed_urls("logo.png")
self.assertIsNone(result)
def test_themed_urls_with_theme_variable(self):
"""Test themed_urls returns dict of URLs for each theme"""
manager = FileManager(FileUsage.MEDIA)
result = manager.themed_urls("logo-%(theme)s.png")
self.assertIsInstance(result, dict)
self.assertIn("light", result)
self.assertIn("dark", result)
self.assertIn("logo-light.png", result["light"])
self.assertIn("logo-dark.png", result["dark"])
def test_themed_urls_with_request(self):
"""Test themed_urls builds absolute URLs with request"""
mock_request = HttpRequest()
mock_request.META = {
"HTTP_HOST": "example.com",
"SERVER_NAME": "example.com",
}
manager = FileManager(FileUsage.MEDIA)
result = manager.themed_urls("logo-%(theme)s.svg", mock_request)
self.assertIsInstance(result, dict)
light_url = urlparse(result["light"])
dark_url = urlparse(result["dark"])
self.assertEqual(light_url.scheme, "http")
self.assertEqual(light_url.netloc, "example.com")
self.assertEqual(dark_url.scheme, "http")
self.assertEqual(dark_url.netloc, "example.com")
def test_themed_urls_passthrough_with_theme_variable(self):
"""Test themed_urls returns dict for passthrough URLs with %(theme)s"""
manager = FileManager(FileUsage.MEDIA)
# External URLs with %(theme)s should return themed URLs
result = manager.themed_urls("https://example.com/logo-%(theme)s.png")
self.assertIsInstance(result, dict)
self.assertEqual(result["light"], "https://example.com/logo-light.png")
self.assertEqual(result["dark"], "https://example.com/logo-dark.png")
def test_themed_urls_passthrough_without_theme_variable(self):
"""Test themed_urls returns None for passthrough URLs without %(theme)s"""
manager = FileManager(FileUsage.MEDIA)
# External URLs without %(theme)s should return None
result = manager.themed_urls("https://example.com/logo.png")
self.assertIsNone(result)

View File

@@ -62,10 +62,10 @@ class TestSanitizeFilePath(TestCase):
"test@file.png", # @
"test#file.png", # #
"test$file.png", # $
"test%file.png", # % (but %(theme)s is allowed)
"test%file.png", # %
"test&file.png", # &
"test*file.png", # *
"test(file).png", # parentheses (but %(theme)s is allowed)
"test(file).png", # parentheses
"test[file].png", # brackets
"test{file}.png", # braces
]
@@ -108,30 +108,3 @@ class TestSanitizeFilePath(TestCase):
with self.assertRaises(ValidationError):
validate_file_name(path)
def test_sanitize_theme_variable_valid(self):
"""Test sanitizing filename with %(theme)s variable"""
# These should all be valid
validate_file_name("logo-%(theme)s.png")
validate_file_name("brand/logo-%(theme)s.svg")
validate_file_name("images/icon-%(theme)s.png")
validate_file_name("%(theme)s/logo.png")
validate_file_name("brand/%(theme)s/logo.png")
def test_sanitize_theme_variable_multiple(self):
"""Test sanitizing filename with multiple %(theme)s variables"""
validate_file_name("%(theme)s/logo-%(theme)s.png")
def test_sanitize_theme_variable_invalid_format(self):
"""Test that partial or malformed theme variables are rejected"""
invalid_paths = [
"test%(theme.png", # missing )s
"test%theme)s.png", # missing (
"test%(themes).png", # wrong variable name
"test%(THEME)s.png", # wrong case
"test%()s.png", # empty variable name
]
for path in invalid_paths:
with self.assertRaises(ValidationError):
validate_file_name(path)

View File

@@ -4,7 +4,6 @@ from pathlib import PurePosixPath
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
from authentik.admin.files.backends.base import THEME_VARIABLE
from authentik.admin.files.backends.passthrough import PassthroughBackend
from authentik.admin.files.backends.static import StaticBackend
from authentik.admin.files.usage import FileUsage
@@ -40,17 +39,12 @@ def validate_upload_file_name(
if not name:
raise ValidationError(_("File name cannot be empty"))
# Allow %(theme)s placeholder for theme-specific files
# Replace with placeholder for validation, then check the result
name_for_validation = name.replace(THEME_VARIABLE, "theme")
# Same regex is used in the frontend as well (with %(theme)s handling)
if not re.match(r"^[a-zA-Z0-9._/-]+$", name_for_validation):
# Same regex is used in the frontend as well
if not re.match(r"^[a-zA-Z0-9._/-]+$", name):
raise ValidationError(
_(
"File name can only contain letters (a-z, A-Z), numbers (0-9), "
"dots (.), hyphens (-), underscores (_), forward slashes (/), "
"and the placeholder %(theme)s for theme-specific files"
"dots (.), hyphens (-), underscores (_), and forward slashes (/)"
)
)

View File

View File

@@ -0,0 +1,45 @@
from json import dumps
from django.core.management.base import BaseCommand, no_translations
from drf_spectacular.drainage import GENERATOR_STATS
from drf_spectacular.generators import SchemaGenerator
from drf_spectacular.renderers import OpenApiYamlRenderer
from drf_spectacular.validation import validate_schema
from structlog.stdlib import get_logger
from authentik.blueprints.v1.schema import SchemaBuilder
class Command(BaseCommand):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.logger = get_logger()
def add_arguments(self, parser):
parser.add_argument("--blueprint-file", type=str, default="blueprints/schema.json")
parser.add_argument("--api-file", type=str, default="schema.yml")
@no_translations
def handle(self, *args, blueprint_file: str, api_file: str, **options):
self.build_blueprint(blueprint_file)
self.build_api(api_file)
def build_blueprint(self, file: str):
self.logger.debug("Building blueprint schema...", file=file)
blueprint_builder = SchemaBuilder()
blueprint_builder.build()
with open(file, "w") as _schema:
_schema.write(
dumps(blueprint_builder.schema, indent=4, default=SchemaBuilder.json_default)
)
def build_api(self, file: str):
self.logger.debug("Building API schema...", file=file)
generator = SchemaGenerator()
schema = generator.get_schema(request=None, public=True)
GENERATOR_STATS.emit_summary()
validate_schema(schema)
output = OpenApiYamlRenderer().render(schema, renderer_context={})
with open(file, "wb") as f:
f.write(output)

View File

@@ -1,9 +1,14 @@
"""Schema generation tests"""
from pathlib import Path
from django.core.management import call_command
from django.urls import reverse
from rest_framework.test import APITestCase
from yaml import safe_load
from authentik.lib.config import CONFIG
class TestSchemaGeneration(APITestCase):
"""Generic admin tests"""
@@ -21,3 +26,18 @@ class TestSchemaGeneration(APITestCase):
reverse("authentik_api:schema-browser"),
)
self.assertEqual(response.status_code, 200)
def test_build_schema(self):
"""Test schema build command"""
blueprint_file = Path("blueprints/schema.json")
api_file = Path("schema.yml")
blueprint_file.unlink()
api_file.unlink()
with (
CONFIG.patch("debug", True),
CONFIG.patch("tenants.enabled", True),
CONFIG.patch("outposts.disable_embedded_outpost", True),
):
call_command("build_schema")
self.assertTrue(blueprint_file.exists())
self.assertTrue(api_file.exists())

View File

@@ -15,7 +15,6 @@ from django.db.models import Model
from django.db.models.query_utils import Q
from django.db.transaction import atomic
from django.db.utils import IntegrityError
from django_channels_postgres.models import GroupChannel, Message
from guardian.models import RoleObjectPermission, UserObjectPermission
from rest_framework.exceptions import ValidationError
from rest_framework.serializers import BaseSerializer, Serializer
@@ -41,55 +40,17 @@ from authentik.core.models import (
User,
UserSourceConnection,
)
from authentik.endpoints.connectors.agent.models import (
AgentDeviceConnection,
AppleNonce,
DeviceAuthenticationToken,
)
from authentik.endpoints.connectors.agent.models import (
DeviceToken as EndpointDeviceToken,
)
from authentik.endpoints.models import Connector, Device, DeviceConnection, DeviceFactSnapshot
from authentik.endpoints.models import Connector
from authentik.enterprise.license import LicenseKey
from authentik.enterprise.models import LicenseUsage
from authentik.enterprise.providers.google_workspace.models import (
GoogleWorkspaceProviderGroup,
GoogleWorkspaceProviderUser,
)
from authentik.enterprise.providers.microsoft_entra.models import (
MicrosoftEntraProviderGroup,
MicrosoftEntraProviderUser,
)
from authentik.enterprise.providers.ssf.models import StreamEvent
from authentik.enterprise.stages.authenticator_endpoint_gdtc.models import (
EndpointDevice,
EndpointDeviceConnection,
)
from authentik.events.logs import LogEvent, capture_logs
from authentik.events.utils import cleanse_dict
from authentik.flows.models import FlowToken, Stage
from authentik.lib.models import SerializerModel
from authentik.flows.models import Stage
from authentik.lib.models import InternallyManagedMixin, SerializerModel
from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.reflection import get_apps
from authentik.outposts.models import OutpostServiceConnection
from authentik.policies.models import Policy, PolicyBindingModel
from authentik.policies.reputation.models import Reputation
from authentik.providers.oauth2.models import (
AccessToken,
AuthorizationCode,
DeviceToken,
RefreshToken,
)
from authentik.providers.proxy.models import ProxySession
from authentik.providers.rac.models import ConnectionToken
from authentik.providers.saml.models import SAMLSession
from authentik.providers.scim.models import SCIMProviderGroup, SCIMProviderUser
from authentik.rbac.models import Role
from authentik.sources.scim.models import SCIMSourceGroup, SCIMSourceUser
from authentik.stages.authenticator_webauthn.models import WebAuthnDeviceType
from authentik.stages.consent.models import UserConsent
from authentik.tasks.models import Task, TaskLog
from authentik.tenants.models import Tenant
# Context set when the serializer is created in a blueprint context
# Update website/docs/customize/blueprints/v1/models.md when used
@@ -125,49 +86,16 @@ def excluded_models() -> list[type[Model]]:
# Classes that have other dependencies
Session,
AuthenticatedSession,
# Classes which are only internally managed
# FIXME: these shouldn't need to be explicitly listed, but rather based off of a mixin
FlowToken,
LicenseUsage,
SCIMProviderGroup,
SCIMProviderUser,
Tenant,
Task,
TaskLog,
ConnectionToken,
AuthorizationCode,
AccessToken,
RefreshToken,
ProxySession,
Reputation,
WebAuthnDeviceType,
SCIMSourceUser,
SCIMSourceGroup,
GoogleWorkspaceProviderUser,
GoogleWorkspaceProviderGroup,
MicrosoftEntraProviderUser,
MicrosoftEntraProviderGroup,
EndpointDevice,
EndpointDeviceConnection,
EndpointDeviceToken,
Device,
DeviceConnection,
DeviceAuthenticationToken,
AppleNonce,
AgentDeviceConnection,
DeviceFactSnapshot,
DeviceToken,
StreamEvent,
UserConsent,
SAMLSession,
Message,
GroupChannel,
)
def is_model_allowed(model: type[Model]) -> bool:
"""Check if model is allowed"""
return model not in excluded_models() and issubclass(model, SerializerModel | BaseMetaModel)
return (
model not in excluded_models()
and issubclass(model, SerializerModel | BaseMetaModel)
and not issubclass(model, InternallyManagedMixin)
)
class DoRollback(SentryIgnoredException):

View File

@@ -1,9 +1,7 @@
"""Generate JSON Schema for blueprints"""
from json import dumps
from typing import Any
from django.core.management.base import BaseCommand, no_translations
from django.db.models import Model, fields
from django.db.models.fields.related import OneToOneField
from drf_jsonschema_serializer.convert import converter, field_to_converter
@@ -40,13 +38,12 @@ class PrimaryKeyRelatedFieldConverter:
return {"type": "integer"}
class Command(BaseCommand):
class SchemaBuilder:
"""Generate JSON Schema for blueprints"""
schema: dict
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __init__(self):
self.schema = {
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json",
@@ -93,16 +90,6 @@ class Command(BaseCommand):
"$defs": {"blueprint_entry": {"oneOf": []}},
}
def add_arguments(self, parser):
parser.add_argument("--file", type=str)
@no_translations
def handle(self, *args, file: str, **options):
"""Generate JSON Schema for blueprints"""
self.build()
with open(file, "w") as _schema:
_schema.write(dumps(self.schema, indent=4, default=Command.json_default))
@staticmethod
def json_default(value: Any) -> Any:
"""Helper that handles gettext_lazy strings that JSON doesn't handle"""
@@ -124,7 +111,7 @@ class Command(BaseCommand):
try:
serializer_class = model_instance.serializer
except NotImplementedError as exc:
raise NotImplementedError(model_instance) from exc
raise ValueError(f"SerializerModel not implemented by {model}") from exc
serializer = serializer_class(
context={
SERIALIZER_CONTEXT_BLUEPRINT: False,

View File

@@ -6,12 +6,7 @@ from django.db import models
from drf_spectacular.utils import extend_schema, extend_schema_field
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.fields import (
CharField,
ChoiceField,
ListField,
SerializerMethodField,
)
from rest_framework.fields import CharField, ChoiceField, ListField, SerializerMethodField
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.permissions import AllowAny
from rest_framework.request import Request
@@ -21,7 +16,7 @@ from rest_framework.viewsets import ModelViewSet
from authentik.brands.models import Brand
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import ModelSerializer, PassiveSerializer, ThemedUrlsSerializer
from authentik.core.api.utils import ModelSerializer, PassiveSerializer
from authentik.rbac.filters import SecretKeyFilter
from authentik.tenants.api.settings import FlagJSONField
from authentik.tenants.flags import Flag
@@ -95,9 +90,7 @@ class CurrentBrandSerializer(PassiveSerializer):
matched_domain = CharField(source="domain")
branding_title = CharField()
branding_logo = CharField(source="branding_logo_url")
branding_logo_themed_urls = ThemedUrlsSerializer(read_only=True, allow_null=True)
branding_favicon = CharField(source="branding_favicon_url")
branding_favicon_themed_urls = ThemedUrlsSerializer(read_only=True, allow_null=True)
branding_custom_css = CharField()
ui_footer_links = ListField(
child=FooterLinkSerializer(),

View File

@@ -89,26 +89,14 @@ class Brand(SerializerModel):
"""Get branding_logo URL"""
return get_file_manager(FileUsage.MEDIA).file_url(self.branding_logo)
def branding_logo_themed_urls(self) -> dict[str, str] | None:
"""Get themed URLs for branding_logo if it contains %(theme)s"""
return get_file_manager(FileUsage.MEDIA).themed_urls(self.branding_logo)
def branding_favicon_url(self) -> str:
"""Get branding_favicon URL"""
return get_file_manager(FileUsage.MEDIA).file_url(self.branding_favicon)
def branding_favicon_themed_urls(self) -> dict[str, str] | None:
"""Get themed URLs for branding_favicon if it contains %(theme)s"""
return get_file_manager(FileUsage.MEDIA).themed_urls(self.branding_favicon)
def branding_default_flow_background_url(self) -> str:
"""Get branding_default_flow_background URL"""
return get_file_manager(FileUsage.MEDIA).file_url(self.branding_default_flow_background)
def branding_default_flow_background_themed_urls(self) -> dict[str, str] | None:
"""Get themed URLs for branding_default_flow_background if it contains %(theme)s"""
return get_file_manager(FileUsage.MEDIA).themed_urls(self.branding_default_flow_background)
@property
def serializer(self) -> type[Serializer]:
from authentik.brands.api import BrandSerializer

View File

@@ -6,6 +6,7 @@ from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.blueprints.tests import apply_blueprint
from authentik.brands.api import Themes
from authentik.brands.models import Brand
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user, create_test_brand
@@ -34,14 +35,12 @@ class TestBrands(APITestCase):
self.client.get(reverse("authentik_api:brand-current")).content.decode(),
{
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_logo_themed_urls": None,
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_favicon_themed_urls": None,
"branding_title": "authentik",
"branding_custom_css": "",
"matched_domain": brand.domain,
"ui_footer_links": [],
"ui_theme": "automatic",
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
"flags": self.default_flags,
},
@@ -56,14 +55,12 @@ class TestBrands(APITestCase):
).content.decode(),
{
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_logo_themed_urls": None,
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_favicon_themed_urls": None,
"branding_title": "custom",
"branding_custom_css": "",
"matched_domain": "bar.baz",
"ui_footer_links": [],
"ui_theme": "automatic",
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
"flags": self.default_flags,
},
@@ -75,14 +72,12 @@ class TestBrands(APITestCase):
self.client.get(reverse("authentik_api:brand-current")).content.decode(),
{
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_logo_themed_urls": None,
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_favicon_themed_urls": None,
"branding_title": "authentik",
"branding_custom_css": "",
"matched_domain": "fallback",
"ui_footer_links": [],
"ui_theme": "automatic",
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
"flags": self.default_flags,
},
@@ -99,14 +94,12 @@ class TestBrands(APITestCase):
response,
{
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_logo_themed_urls": None,
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_favicon_themed_urls": None,
"branding_title": "authentik",
"branding_custom_css": "",
"matched_domain": "authentik-default",
"ui_footer_links": [],
"ui_theme": "automatic",
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
"flags": self.default_flags,
},
@@ -124,14 +117,12 @@ class TestBrands(APITestCase):
response,
{
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_logo_themed_urls": None,
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_favicon_themed_urls": None,
"branding_title": "authentik",
"branding_custom_css": "",
"matched_domain": "authentik-default",
"ui_footer_links": [],
"ui_theme": "automatic",
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
"flags": self.default_flags,
},
@@ -142,14 +133,12 @@ class TestBrands(APITestCase):
).content.decode(),
{
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_logo_themed_urls": None,
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_favicon_themed_urls": None,
"branding_title": "custom",
"branding_custom_css": "",
"matched_domain": "bar.baz",
"ui_footer_links": [],
"ui_theme": "automatic",
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
"flags": self.default_flags,
},
@@ -165,14 +154,12 @@ class TestBrands(APITestCase):
).content.decode(),
{
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_logo_themed_urls": None,
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_favicon_themed_urls": None,
"branding_title": "custom-strong",
"branding_custom_css": "",
"matched_domain": "foo.bar.baz",
"ui_footer_links": [],
"ui_theme": "automatic",
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
"flags": self.default_flags,
},
@@ -188,14 +175,12 @@ class TestBrands(APITestCase):
).content.decode(),
{
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_logo_themed_urls": None,
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_favicon_themed_urls": None,
"branding_title": "custom-weak",
"branding_custom_css": "",
"matched_domain": "bar.baz",
"ui_footer_links": [],
"ui_theme": "automatic",
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
"flags": self.default_flags,
},
@@ -271,14 +256,12 @@ class TestBrands(APITestCase):
self.client.get(reverse("authentik_api:brand-current")).content.decode(),
{
"branding_logo": "https://goauthentik.io/img/icon.png",
"branding_logo_themed_urls": None,
"branding_favicon": "https://goauthentik.io/img/icon.png",
"branding_favicon_themed_urls": None,
"branding_title": "authentik",
"branding_custom_css": "",
"matched_domain": brand.domain,
"ui_footer_links": [],
"ui_theme": "automatic",
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
"flags": self.default_flags,
},

View File

@@ -24,7 +24,7 @@ from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import UserSerializer
from authentik.core.api.utils import ModelSerializer, ThemedUrlsSerializer
from authentik.core.api.utils import ModelSerializer
from authentik.core.models import Application, User
from authentik.events.logs import LogEventSerializer, capture_logs
from authentik.policies.api.exec import PolicyTestResultSerializer
@@ -53,9 +53,6 @@ class ApplicationSerializer(ModelSerializer):
)
meta_icon_url = ReadOnlyField(source="get_meta_icon")
meta_icon_themed_urls = ThemedUrlsSerializer(
source="get_meta_icon_themed_urls", read_only=True, allow_null=True
)
def get_launch_url(self, app: Application) -> str | None:
"""Allow formatting of launch URL"""
@@ -105,7 +102,6 @@ class ApplicationSerializer(ModelSerializer):
"meta_launch_url",
"meta_icon",
"meta_icon_url",
"meta_icon_themed_urls",
"meta_description",
"meta_publisher",
"policy_engine_mode",

View File

@@ -85,7 +85,6 @@ class GroupSerializer(ModelSerializer):
source="roles",
required=False,
)
inherited_roles_obj = SerializerMethodField(allow_null=True)
num_pk = IntegerField(read_only=True)
@property
@@ -109,13 +108,6 @@ class GroupSerializer(ModelSerializer):
return True
return str(request.query_params.get("include_parents", "false")).lower() == "true"
@property
def _should_include_inherited_roles(self) -> bool:
request: Request = self.context.get("request", None)
if not request:
return True
return str(request.query_params.get("include_inherited_roles", "false")).lower() == "true"
@extend_schema_field(PartialUserSerializer(many=True))
def get_users_obj(self, instance: Group) -> list[PartialUserSerializer] | None:
if not self._should_include_users:
@@ -134,15 +126,6 @@ class GroupSerializer(ModelSerializer):
return None
return RelatedGroupSerializer(instance.parents, many=True).data
@extend_schema_field(RoleSerializer(many=True))
def get_inherited_roles_obj(self, instance: Group) -> list | None:
"""Return only inherited roles from ancestor groups (excludes direct roles)"""
if not self._should_include_inherited_roles:
return None
direct_role_pks = instance.roles.values_list("pk", flat=True)
inherited_roles = instance.all_roles().exclude(pk__in=direct_role_pks)
return RoleSerializer(inherited_roles, many=True).data
def validate_is_superuser(self, superuser: bool):
"""Ensure that the user creating this group has permissions to set the superuser flag"""
request: Request = self.context.get("request", None)
@@ -184,7 +167,6 @@ class GroupSerializer(ModelSerializer):
"attributes",
"roles",
"roles_obj",
"inherited_roles_obj",
"children",
"children_obj",
]
@@ -307,7 +289,6 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
OpenApiParameter("include_users", bool, default=True),
OpenApiParameter("include_children", bool, default=False),
OpenApiParameter("include_parents", bool, default=False),
OpenApiParameter("include_inherited_roles", bool, default=False),
]
)
def list(self, request, *args, **kwargs):
@@ -318,7 +299,6 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
OpenApiParameter("include_users", bool, default=True),
OpenApiParameter("include_children", bool, default=False),
OpenApiParameter("include_parents", bool, default=False),
OpenApiParameter("include_inherited_roles", bool, default=False),
]
)
def retrieve(self, request, *args, **kwargs):

View File

@@ -18,14 +18,10 @@ from authentik.core.models import Provider
class ProviderSerializer(ModelSerializer, MetaNameSerializer):
"""Provider Serializer"""
assigned_application_slug = ReadOnlyField(source="application.slug", allow_null=True)
assigned_application_name = ReadOnlyField(source="application.name", allow_null=True)
assigned_backchannel_application_slug = ReadOnlyField(
source="backchannel_application.slug", allow_null=True
)
assigned_backchannel_application_name = ReadOnlyField(
source="backchannel_application.name", allow_null=True
)
assigned_application_slug = ReadOnlyField(source="application.slug")
assigned_application_name = ReadOnlyField(source="application.name")
assigned_backchannel_application_slug = ReadOnlyField(source="backchannel_application.slug")
assigned_backchannel_application_name = ReadOnlyField(source="backchannel_application.name")
component = SerializerMethodField()

View File

@@ -14,7 +14,7 @@ from structlog.stdlib import get_logger
from authentik.core.api.object_types import TypesMixin
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import MetaNameSerializer, ModelSerializer, ThemedUrlsSerializer
from authentik.core.api.utils import MetaNameSerializer, ModelSerializer
from authentik.core.models import GroupSourceConnection, Source, UserSourceConnection
from authentik.core.types import UserSettingSerializer
from authentik.policies.engine import PolicyEngine
@@ -28,7 +28,6 @@ class SourceSerializer(ModelSerializer, MetaNameSerializer):
managed = ReadOnlyField()
component = SerializerMethodField()
icon_url = ReadOnlyField()
icon_themed_urls = ThemedUrlsSerializer(read_only=True, allow_null=True)
def get_component(self, obj: Source) -> str:
"""Get object component so that we know how to edit the object"""
@@ -58,7 +57,6 @@ class SourceSerializer(ModelSerializer, MetaNameSerializer):
"user_path_template",
"icon",
"icon_url",
"icon_themed_urls",
]

View File

@@ -4,7 +4,6 @@ from typing import Any
from django.utils.timezone import now
from drf_spectacular.utils import OpenApiResponse, extend_schema
from guardian.shortcuts import get_anonymous_user
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.fields import CharField
@@ -76,8 +75,7 @@ class TokenSerializer(ManagedSerializer, ModelSerializer):
except ValueError:
pass
expires = attrs.get("expires")
if expires is not None and expires > max_token_lifetime_dt:
if "expires" in attrs and attrs.get("expires") > max_token_lifetime_dt:
raise ValidationError(
{
"expires": (
@@ -146,12 +144,6 @@ class TokenViewSet(UsedByMixin, ModelViewSet):
owner_field = "user"
rbac_allow_create_without_perm = True
def get_queryset(self):
user = self.request.user if self.request else get_anonymous_user()
if user.is_superuser:
return super().get_queryset()
return super().get_queryset().filter(user=user.pk)
def perform_create(self, serializer: TokenSerializer):
if not self.request.user.is_superuser:
instance = serializer.save(

View File

@@ -127,10 +127,3 @@ class LinkSerializer(PassiveSerializer):
"""Returns a single link"""
link = CharField()
class ThemedUrlsSerializer(PassiveSerializer):
"""Themed URLs - maps theme names to URLs for light and dark themes"""
light = CharField(required=False, allow_null=True)
dark = CharField(required=False, allow_null=True)

View File

@@ -8,7 +8,7 @@ from uuid import uuid4
from django.contrib.auth import logout
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest
from django.http import HttpRequest, HttpResponse
from django.utils.deprecation import MiddlewareMixin
from django.utils.functional import SimpleLazyObject
from django.utils.translation import override
@@ -47,7 +47,7 @@ async def aget_user(request):
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request: HttpRequest) -> HttpResponseBadRequest | None:
def process_request(self, request):
if not hasattr(request, "session"):
raise ImproperlyConfigured(
"The Django authentication middleware requires session "
@@ -62,8 +62,7 @@ class AuthenticationMiddleware(MiddlewareMixin):
user = request.user
if user and user.is_authenticated and not user.is_active:
logout(request)
return HttpResponseBadRequest()
return None
raise AssertionError()
class ImpersonateMiddleware:

View File

@@ -713,15 +713,9 @@ class Application(SerializerModel, PolicyBindingModel):
return get_file_manager(FileUsage.MEDIA).file_url(self.meta_icon)
@property
def get_meta_icon_themed_urls(self) -> dict[str, str] | None:
"""Get themed URLs for meta_icon if it contains %(theme)s"""
if not self.meta_icon:
return None
return get_file_manager(FileUsage.MEDIA).themed_urls(self.meta_icon)
def get_launch_url(self, user: User | None = None, user_data: dict | None = None) -> str | None:
def get_launch_url(
self, user: Optional["User"] = None, user_data: dict | None = None
) -> str | None:
"""Get launch URL if set, otherwise attempt to get launch URL based on provider.
Args:
@@ -935,14 +929,6 @@ class Source(ManagedModel, SerializerModel, PolicyBindingModel):
return get_file_manager(FileUsage.MEDIA).file_url(self.icon)
@property
def icon_themed_urls(self) -> dict[str, str] | None:
"""Get themed URLs for icon if it contains %(theme)s"""
if not self.icon:
return None
return get_file_manager(FileUsage.MEDIA).themed_urls(self.icon)
def get_user_path(self) -> str:
"""Get user path, fallback to default for formatting errors"""
try:

View File

@@ -24,8 +24,7 @@ from authentik.root.ws.consumer import build_device_group
# Arguments: user: User, password: str
password_changed = Signal()
# Arguments: credentials: dict[str, any], request: HttpRequest,
# stage: Stage, context: dict[str, any]
# Arguments: credentials: dict[str, any], request: HttpRequest, stage: Stage
login_failed = Signal()
LOGGER = get_logger()

View File

@@ -10,23 +10,15 @@
{% elif ui_theme == "light" %}
<meta name="color-scheme" content="light" />
<meta name="theme-color" content="#ffffff">
{% else %}
{% else %}
<script data-id="theme-script">
"use strict";
(function () {
try {
/* Ignore older theme names */
let locallyStoredTheme = window.localStorage?.getItem("theme") || null;
if (typeof locallyStoredTheme === "string") {
locallyStoredTheme = locallyStoredTheme.trim();
}
if (!(["auto", "light", "dark"].includes(locallyStoredTheme))) {
locallyStoredTheme = null;
}
const initialThemeChoice =
new URLSearchParams(window.location.search).get("theme") || locallyStoredTheme;
new URLSearchParams(window.location.search).get("theme") ||
window.localStorage?.getItem("theme");
const themeChoice =
initialThemeChoice || document.documentElement.dataset.themeChoice || "auto";

View File

@@ -107,8 +107,6 @@ class TestApplicationsAPI(APITestCase):
"provider_obj": {
"assigned_application_name": "allowed",
"assigned_application_slug": "allowed",
"assigned_backchannel_application_name": None,
"assigned_backchannel_application_slug": None,
"authentication_flow": None,
"invalidation_flow": None,
"authorization_flow": str(self.provider.authorization_flow.pk),
@@ -127,7 +125,6 @@ class TestApplicationsAPI(APITestCase):
"open_in_new_tab": True,
"meta_icon": "",
"meta_icon_url": None,
"meta_icon_themed_urls": None,
"meta_description": "",
"meta_publisher": "",
"policy_engine_mode": "any",
@@ -165,8 +162,6 @@ class TestApplicationsAPI(APITestCase):
"provider_obj": {
"assigned_application_name": "allowed",
"assigned_application_slug": "allowed",
"assigned_backchannel_application_name": None,
"assigned_backchannel_application_slug": None,
"authentication_flow": None,
"invalidation_flow": None,
"authorization_flow": str(self.provider.authorization_flow.pk),
@@ -185,7 +180,6 @@ class TestApplicationsAPI(APITestCase):
"open_in_new_tab": True,
"meta_icon": "",
"meta_icon_url": None,
"meta_icon_themed_urls": None,
"meta_description": "",
"meta_publisher": "",
"policy_engine_mode": "any",
@@ -195,7 +189,6 @@ class TestApplicationsAPI(APITestCase):
"meta_description": "",
"meta_icon": "",
"meta_icon_url": None,
"meta_icon_themed_urls": None,
"meta_launch_url": "",
"open_in_new_tab": False,
"meta_publisher": "",

View File

@@ -5,9 +5,10 @@ from django.test import TestCase
from authentik.core.models import Group, PropertyMapping, Source, User
from authentik.core.sources.mapper import SourceMapper
from authentik.lib.generators import generate_id
from authentik.lib.models import InternallyManagedMixin
class ProxySource(Source):
class ProxySource(InternallyManagedMixin, Source):
@property
def property_mapping_type(self):
return PropertyMapping

View File

@@ -183,16 +183,16 @@ class TestTokenAPI(APITestCase):
self.assertEqual(len(body["results"]), 1)
self.assertEqual(body["results"][0]["identifier"], token_should.identifier)
def test_list_admin(self):
"""Test Token List (Test with admin auth)"""
def test_list_with_permission(self):
"""Test Token List (Test with `view_token` permission)"""
Token.objects.all().delete()
self.client.force_login(self.admin)
token_should: Token = Token.objects.create(
identifier="test", expiring=False, user=self.user
)
token_should_not: Token = Token.objects.create(
identifier="test-2", expiring=False, user=get_anonymous_user()
)
self.user.assign_perms_to_managed_role("authentik_core.view_token")
response = self.client.get(reverse("authentik_api:token-list"))
body = loads(response.content)
self.assertEqual(len(body["results"]), 2)

View File

@@ -1,7 +1,5 @@
"""Crypto API Views"""
from datetime import datetime
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509 import load_pem_x509_certificate
@@ -15,14 +13,12 @@ from drf_spectacular.utils import (
OpenApiParameter,
OpenApiResponse,
extend_schema,
extend_schema_field,
)
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.fields import (
CharField,
ChoiceField,
DateTimeField,
IntegerField,
SerializerMethodField,
)
@@ -51,59 +47,15 @@ LOGGER = get_logger()
class CertificateKeyPairSerializer(ModelSerializer):
"""CertificateKeyPair Serializer"""
fingerprint_sha256 = SerializerMethodField()
fingerprint_sha1 = SerializerMethodField()
cert_expiry = SerializerMethodField()
cert_subject = SerializerMethodField()
private_key_available = SerializerMethodField()
key_type = SerializerMethodField()
certificate_download_url = SerializerMethodField()
private_key_download_url = SerializerMethodField()
@property
def _should_include_details(self) -> bool:
request: Request = self.context.get("request", None)
if not request:
return True
return str(request.query_params.get("include_details", "true")).lower() == "true"
def get_fingerprint_sha256(self, instance: CertificateKeyPair) -> str | None:
"Get certificate Hash (SHA256)"
if not self._should_include_details:
return None
return instance.fingerprint_sha256
def get_fingerprint_sha1(self, instance: CertificateKeyPair) -> str | None:
"Get certificate Hash (SHA1)"
if not self._should_include_details:
return None
return instance.fingerprint_sha1
def get_cert_expiry(self, instance: CertificateKeyPair) -> datetime | None:
"Get certificate expiry"
if not self._should_include_details:
return None
return DateTimeField().to_representation(instance.certificate.not_valid_after_utc)
def get_cert_subject(self, instance: CertificateKeyPair) -> str | None:
"""Get certificate subject as full rfc4514"""
if not self._should_include_details:
return None
return instance.certificate.subject.rfc4514_string()
def get_private_key_available(self, instance: CertificateKeyPair) -> bool:
"""Show if this keypair has a private key configured or not"""
return instance.key_data != "" and instance.key_data is not None
@extend_schema_field(ChoiceField(choices=KeyType.choices, allow_null=True))
def get_key_type(self, instance: CertificateKeyPair) -> str | None:
"""Get the key algorithm type from the certificate's public key"""
if not self._should_include_details:
return None
return instance.key_type
def get_certificate_download_url(self, instance: CertificateKeyPair) -> str:
"""Get URL to download certificate"""
return (
@@ -175,6 +127,11 @@ class CertificateKeyPairSerializer(ModelSerializer):
"managed": {"read_only": True},
"key_data": {"write_only": True},
"certificate_data": {"write_only": True},
"fingerprint_sha256": {"read_only": True},
"fingerprint_sha1": {"read_only": True},
"cert_expiry": {"read_only": True},
"cert_subject": {"read_only": True},
"key_type": {"read_only": True},
}
@@ -216,17 +173,12 @@ class CertificateKeyPairFilter(FilterSet):
return queryset.exclude(key_data__exact="")
def filter_key_type(self, queryset, name, value): # pragma: no cover
"""Filter certificates by key type using the public key from the certificate"""
"""Filter certificates by key type using the stored database field"""
if not value:
return queryset
# value is a list of KeyType enum values from MultipleChoiceFilter
filtered_pks = []
for cert in queryset:
if cert.key_type in value:
filtered_pks.append(cert.pk)
return queryset.filter(pk__in=filtered_pks)
return queryset.filter(key_type__in=value)
class Meta:
model = CertificateKeyPair
@@ -263,7 +215,6 @@ class CertificateKeyPairViewSet(UsedByMixin, ModelViewSet):
"Can be specified multiple times (e.g. '?key_type=rsa&key_type=ec')"
),
),
OpenApiParameter("include_details", bool, default=True),
]
)
def list(self, request, *args, **kwargs):

View File

@@ -0,0 +1,120 @@
# Generated by Django 5.2.9 on 2025-12-09 06:22
from hashlib import md5
from cryptography.hazmat.backends import default_backend
from cryptography.x509 import load_pem_x509_certificate
from django.db import migrations, models
from authentik.crypto.signals import extract_certificate_metadata
from authentik.lib.migrations import progress_bar
def backfill_certificate_metadata(apps, schema_editor): # noqa: ARG001
"""Backfill certificate metadata and kid for existing records."""
db_alias = schema_editor.connection.alias
CertificateKeyPair = apps.get_model("authentik_crypto", "CertificateKeyPair")
print("\nStoring extra data about certificates, this might take a couple of minutes...")
for cert in progress_bar(CertificateKeyPair.objects.using(db_alias).all()):
updated_fields = []
if cert.certificate_data:
try:
certificate = load_pem_x509_certificate(
cert.certificate_data.encode("utf-8"), default_backend()
)
metadata = extract_certificate_metadata(certificate)
cert.key_type = metadata["key_type"]
cert.cert_expiry = metadata["cert_expiry"]
cert.cert_subject = metadata["cert_subject"]
cert.fingerprint_sha256 = metadata["fingerprint_sha256"]
cert.fingerprint_sha1 = metadata["fingerprint_sha1"]
updated_fields.extend(
[
"key_type",
"cert_expiry",
"cert_subject",
"fingerprint_sha256",
"fingerprint_sha1",
]
)
except (ValueError, TypeError, AttributeError):
pass
# Backfill kid with MD5 for backwards compatibility
if cert.key_data:
cert.kid = md5(cert.key_data.encode("utf-8"), usedforsecurity=False).hexdigest()
updated_fields.append("kid")
if updated_fields:
cert.save(update_fields=updated_fields, using=db_alias)
class Migration(migrations.Migration):
dependencies = [
("authentik_crypto", "0005_alter_certificatekeypair_options"),
]
operations = [
migrations.AddField(
model_name="certificatekeypair",
name="cert_expiry",
field=models.DateTimeField(blank=True, help_text="Certificate expiry date", null=True),
),
migrations.AddField(
model_name="certificatekeypair",
name="cert_subject",
field=models.TextField(
blank=True, help_text="Certificate subject as RFC4514 string", null=True
),
),
migrations.AddField(
model_name="certificatekeypair",
name="fingerprint_sha1",
field=models.CharField(
blank=True,
help_text="SHA1 fingerprint of the certificate",
max_length=59,
null=True,
),
),
migrations.AddField(
model_name="certificatekeypair",
name="fingerprint_sha256",
field=models.CharField(
blank=True,
help_text="SHA256 fingerprint of the certificate",
max_length=95,
null=True,
),
),
migrations.AddField(
model_name="certificatekeypair",
name="key_type",
field=models.CharField(
blank=True,
choices=[
("rsa", "RSA"),
("ec", "Elliptic Curve"),
("dsa", "DSA"),
("ed25519", "Ed25519"),
("ed448", "Ed448"),
],
help_text="Key algorithm type detected from the certificate's public key",
max_length=16,
null=True,
),
),
migrations.AddField(
model_name="certificatekeypair",
name="kid",
field=models.CharField(
blank=True, help_text="Key ID generated from private key", max_length=128, null=True
),
),
migrations.RunPython(backfill_certificate_metadata, migrations.RunPython.noop),
]

View File

@@ -1,7 +1,8 @@
"""authentik crypto models"""
from base64 import urlsafe_b64encode
from binascii import hexlify
from hashlib import md5
from hashlib import md5, sha512
from ssl import PEM_FOOTER, PEM_HEADER
from textwrap import wrap
from uuid import uuid4
@@ -47,6 +48,39 @@ def fingerprint_sha256(cert: Certificate) -> str:
return hexlify(cert.fingerprint(hashes.SHA256()), ":").decode("utf-8")
def detect_key_type(certificate: Certificate) -> str | None:
"""Detect the key algorithm type by parsing the certificate's public key"""
try:
public_key = certificate.public_key()
if isinstance(public_key, RSAPublicKey):
return KeyType.RSA
if isinstance(public_key, EllipticCurvePublicKey):
return KeyType.EC
if isinstance(public_key, DSAPublicKey):
return KeyType.DSA
if isinstance(public_key, Ed25519PublicKey):
return KeyType.ED25519
if isinstance(public_key, Ed448PublicKey):
return KeyType.ED448
except (ValueError, TypeError, AttributeError) as exc:
LOGGER.warning("Failed to detect key type", exc=exc)
return None
def generate_key_id(key_data: str) -> str:
"""Generate Key ID using SHA512 + urlsafe_b64encode."""
if not key_data:
return ""
return urlsafe_b64encode(sha512(key_data.encode("utf-8")).digest()).decode("utf-8").rstrip("=")
def generate_key_id_legacy(key_data: str) -> str:
"""Generate Key ID using MD5 (legacy format for backwards compatibility)."""
if not key_data:
return ""
return md5(key_data.encode("utf-8")).hexdigest() # nosec
class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
"""CertificateKeyPair that can be used for signing or encrypting if `key_data`
is set, otherwise it can be used to verify remote data."""
@@ -62,6 +96,41 @@ class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
blank=True,
default="",
)
key_type = models.CharField(
max_length=16,
choices=KeyType.choices,
null=True,
blank=True,
help_text=_("Key algorithm type detected from the certificate's public key"),
)
cert_expiry = models.DateTimeField(
null=True,
blank=True,
help_text=_("Certificate expiry date"),
)
cert_subject = models.TextField(
null=True,
blank=True,
help_text=_("Certificate subject as RFC4514 string"),
)
fingerprint_sha256 = models.CharField(
max_length=95,
null=True,
blank=True,
help_text=_("SHA256 fingerprint of the certificate"),
)
fingerprint_sha1 = models.CharField(
max_length=59,
null=True,
blank=True,
help_text=_("SHA1 fingerprint of the certificate"),
)
kid = models.CharField(
max_length=128,
null=True,
blank=True,
help_text=_("Key ID generated from private key"),
)
_cert: Certificate | None = None
_private_key: PrivateKeyTypes | None = None
@@ -106,41 +175,6 @@ class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
return None
return self._private_key
@property
def fingerprint_sha256(self) -> str:
"""Get SHA256 Fingerprint of certificate_data"""
return fingerprint_sha256(self.certificate)
@property
def fingerprint_sha1(self) -> str:
"""Get SHA1 Fingerprint of certificate_data"""
return hexlify(self.certificate.fingerprint(hashes.SHA1()), ":").decode("utf-8") # nosec
@property
def kid(self):
"""Get Key ID used for JWKS"""
return (
md5(self.key_data.encode("utf-8"), usedforsecurity=False).hexdigest()
if self.key_data
else ""
) # nosec
@property
def key_type(self) -> str | None:
"""Get the key algorithm type from the certificate's public key"""
public_key = self.certificate.public_key()
if isinstance(public_key, RSAPublicKey):
return KeyType.RSA
if isinstance(public_key, EllipticCurvePublicKey):
return KeyType.EC
if isinstance(public_key, DSAPublicKey):
return KeyType.DSA
if isinstance(public_key, Ed25519PublicKey):
return KeyType.ED25519
if isinstance(public_key, Ed448PublicKey):
return KeyType.ED448
return None
def __str__(self) -> str:
return f"Certificate-Key Pair {self.name}"

View File

@@ -0,0 +1,70 @@
"""authentik crypto signals"""
from binascii import hexlify
from datetime import datetime
from ssl import CertificateError
from cryptography.hazmat.primitives import hashes
from cryptography.x509 import Certificate
from django.db.models.signals import pre_save
from django.dispatch import receiver
from structlog.stdlib import get_logger
from authentik.crypto.models import (
CertificateKeyPair,
detect_key_type,
fingerprint_sha256,
generate_key_id,
generate_key_id_legacy,
)
LOGGER = get_logger()
def extract_certificate_metadata(certificate: Certificate) -> dict[str, str | datetime]:
"""Extract all metadata fields from a certificate."""
metadata = {}
try:
metadata["key_type"] = detect_key_type(certificate)
metadata["cert_expiry"] = certificate.not_valid_after_utc
metadata["cert_subject"] = certificate.subject.rfc4514_string()
metadata["fingerprint_sha256"] = fingerprint_sha256(certificate)
metadata["fingerprint_sha1"] = hexlify(
certificate.fingerprint(hashes.SHA1()), ":" # nosec
).decode("utf-8")
except (ValueError, TypeError, AttributeError) as exc:
raise CertificateError(f"Invalid certificate metadata: {exc}") from exc
return metadata
@receiver(pre_save, sender="authentik_crypto.CertificateKeyPair")
def certificate_key_pair_pre_save(
sender: type[CertificateKeyPair], instance: CertificateKeyPair, **_
):
"""Automatically populate certificate metadata fields before saving"""
# Only extract metadata if certificate_data is present
if not instance.certificate_data:
return
try:
metadata = extract_certificate_metadata(instance.certificate)
except (CertificateError, ValueError, TypeError, AttributeError) as exc:
LOGGER.warning("Failed to extract certificate metadata", exc=exc)
return
instance.key_type = metadata["key_type"]
instance.cert_expiry = metadata["cert_expiry"]
instance.cert_subject = metadata["cert_subject"]
instance.fingerprint_sha256 = metadata["fingerprint_sha256"]
instance.fingerprint_sha1 = metadata["fingerprint_sha1"]
# Generate kid if not set, or regenerate if key_data has changed
# Preserve existing kid (MD5 or SHA512) if it matches the current key_data
if instance.key_data:
new_kid = generate_key_id(instance.key_data)
legacy_kid = generate_key_id_legacy(instance.key_data)
if instance.kid not in (new_kid, legacy_kid):
instance.kid = new_kid

View File

@@ -20,7 +20,7 @@ from authentik.core.tests.utils import (
)
from authentik.crypto.api import CertificateKeyPairSerializer
from authentik.crypto.builder import CertificateBuilder
from authentik.crypto.models import CertificateKeyPair
from authentik.crypto.models import CertificateKeyPair, generate_key_id, generate_key_id_legacy
from authentik.crypto.tasks import MANAGED_DISCOVERED, certificate_discovery
from authentik.lib.config import CONFIG
from authentik.lib.generators import generate_id, generate_key
@@ -173,21 +173,24 @@ class TestCrypto(APITestCase):
self.assertEqual(api_cert["fingerprint_sha1"], cert.fingerprint_sha1)
self.assertEqual(api_cert["fingerprint_sha256"], cert.fingerprint_sha256)
def test_list_without_details(self):
"""Test API List (no details)"""
def test_list_always_includes_details(self):
"""Test API List always includes certificate details"""
cert = create_test_cert()
self.client.force_login(create_test_admin_user())
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-list",
),
data={"name": cert.name, "include_details": False},
data={"name": cert.name},
)
self.assertEqual(response.status_code, 200)
body = loads(response.content.decode())
api_cert = [x for x in body["results"] if x["name"] == cert.name][0]
self.assertEqual(api_cert["fingerprint_sha1"], None)
self.assertEqual(api_cert["fingerprint_sha256"], None)
# All details should now always be included
self.assertEqual(api_cert["fingerprint_sha1"], cert.fingerprint_sha1)
self.assertEqual(api_cert["fingerprint_sha256"], cert.fingerprint_sha256)
self.assertIsNotNone(api_cert["cert_expiry"])
self.assertIsNotNone(api_cert["cert_subject"])
def test_certificate_download(self):
"""Test certificate export (download)"""
@@ -426,3 +429,114 @@ class TestCrypto(APITestCase):
self.assertEqual(
1, final_count, "Should not create duplicate cert for same private key"
)
def test_metadata_extraction_with_cert_and_key(self):
"""Test that metadata is extracted when creating keypair with certificate and key"""
cert = create_test_cert()
# Verify all metadata fields are populated
self.assertIsNotNone(cert.key_type)
self.assertIsNotNone(cert.cert_expiry)
self.assertIsNotNone(cert.cert_subject)
self.assertIsNotNone(cert.fingerprint_sha256)
self.assertIsNotNone(cert.fingerprint_sha1)
# Verify kid is generated using SHA512 for new records
self.assertIsNotNone(cert.kid)
self.assertEqual(cert.kid, generate_key_id(cert.key_data))
def test_metadata_extraction_without_key(self):
"""Test that metadata is extracted when creating keypair without private key"""
builder = CertificateBuilder(generate_id())
builder.build(subject_alt_names=[], validity_days=3)
# Create keypair with only certificate, no key
cert = CertificateKeyPair.objects.create(
name=generate_id(),
certificate_data=builder.certificate,
key_data="",
)
# Verify certificate metadata fields are populated
self.assertIsNotNone(cert.key_type)
self.assertIsNotNone(cert.cert_expiry)
self.assertIsNotNone(cert.cert_subject)
self.assertIsNotNone(cert.fingerprint_sha256)
self.assertIsNotNone(cert.fingerprint_sha1)
# Verify kid is empty when no key_data
self.assertEqual(cert.kid, None)
def test_metadata_extraction_invalid_cert(self):
"""Test that invalid certificate data doesn't crash, just skips metadata"""
cert = CertificateKeyPair.objects.create(
name=generate_id(),
certificate_data="invalid certificate data",
key_data="",
)
# Verify metadata fields are None for invalid cert
self.assertIsNone(cert.key_type)
self.assertIsNone(cert.cert_expiry)
self.assertIsNone(cert.cert_subject)
self.assertIsNone(cert.fingerprint_sha256)
self.assertIsNone(cert.fingerprint_sha1)
self.assertIsNone(cert.kid)
def test_kid_legacy_preservation(self):
"""Test that legacy MD5 kid is preserved when key_data hasn't changed"""
cert = create_test_cert()
# Simulate a legacy MD5 kid (as if backfilled from old system)
legacy_kid = generate_key_id_legacy(cert.key_data)
CertificateKeyPair.objects.filter(pk=cert.pk).update(kid=legacy_kid)
cert.refresh_from_db()
self.assertEqual(cert.kid, legacy_kid)
# Save the cert again (e.g., name change) - kid should be preserved
cert.name = generate_id()
cert.save()
cert.refresh_from_db()
self.assertEqual(cert.kid, legacy_kid)
def test_kid_regenerated_on_key_change(self):
"""Test that kid is regenerated when key_data changes"""
cert = create_test_cert()
original_kid = cert.kid
# Generate a new key and update the keypair
builder = CertificateBuilder(generate_id())
builder.build(subject_alt_names=[], validity_days=3)
cert.key_data = builder.private_key
cert.certificate_data = builder.certificate
cert.save()
cert.refresh_from_db()
# Kid should be regenerated for the new key
self.assertNotEqual(cert.kid, original_kid)
self.assertEqual(cert.kid, generate_key_id(cert.key_data))
def test_kid_regenerated_on_key_change_from_legacy(self):
"""Test that kid is regenerated from legacy MD5 when key_data changes"""
cert = create_test_cert()
# Simulate a legacy MD5 kid
legacy_kid = generate_key_id_legacy(cert.key_data)
CertificateKeyPair.objects.filter(pk=cert.pk).update(kid=legacy_kid)
cert.refresh_from_db()
self.assertEqual(cert.kid, legacy_kid)
# Generate a new key and update the keypair
builder = CertificateBuilder(generate_id())
builder.build(subject_alt_names=[], validity_days=3)
cert.key_data = builder.private_key
cert.certificate_data = builder.certificate
cert.save()
cert.refresh_from_db()
# Kid should now be SHA512 for the new key
self.assertNotEqual(cert.kid, legacy_kid)
self.assertEqual(cert.kid, generate_key_id(cert.key_data))

View File

@@ -3,10 +3,11 @@ from rest_framework.viewsets import ModelViewSet
from authentik.core.api.used_by import UsedByMixin
from authentik.endpoints.api.connectors import ConnectorSerializer
from authentik.endpoints.models import EndpointStage
from authentik.enterprise.api import EnterpriseRequiredMixin
from authentik.flows.api.stages import StageSerializer
class EndpointStageSerializer(StageSerializer):
class EndpointStageSerializer(EnterpriseRequiredMixin, StageSerializer):
"""EndpointStage Serializer"""
connector_obj = ConnectorSerializer(source="connector", read_only=True)

View File

@@ -1,4 +1,3 @@
from django.db import models
from rest_framework.fields import (
BooleanField,
CharField,
@@ -15,12 +14,6 @@ from authentik.endpoints.models import Device
from authentik.lib.utils.time import timedelta_from_string
from authentik.providers.oauth2.views.jwks import JWKSView
try:
from authentik.enterprise.models import LicenseUsageStatus
except ImportError:
class LicenseUsageStatus(models.TextChoices): ...
class AgentConfigSerializer(PassiveSerializer):
@@ -36,7 +29,6 @@ class AgentConfigSerializer(PassiveSerializer):
auth_terminate_session_on_expiry = BooleanField()
system_config = SerializerMethodField()
license_status = SerializerMethodField(required=False, allow_null=True)
def get_device_id(self, instance: AgentConnector) -> str:
device: Device = self.context["device"]
@@ -62,14 +54,6 @@ class AgentConfigSerializer(PassiveSerializer):
def get_system_config(self, instance: AgentConnector) -> ConfigSerializer:
return ConfigView.get_config(self.context["request"]).data
def get_license_status(self, instance: AgentConnector) -> "LicenseUsageStatus":
try:
from authentik.enterprise.license import LicenseKey
return LicenseKey.cached_summary().status
except ModuleNotFoundError:
return None
class EnrollSerializer(PassiveSerializer):

View File

@@ -16,7 +16,7 @@ from authentik.endpoints.models import (
)
from authentik.flows.stage import StageView
from authentik.lib.generators import generate_key
from authentik.lib.models import SerializerModel
from authentik.lib.models import InternallyManagedMixin, SerializerModel
from authentik.lib.utils.time import timedelta_string_validator
if TYPE_CHECKING:
@@ -97,7 +97,7 @@ class AgentDeviceUserBinding(DeviceUserBinding):
apple_enclave_key_id = models.TextField()
class DeviceToken(ExpiringModel):
class DeviceToken(InternallyManagedMixin, ExpiringModel):
"""Per-device token used for authentication."""
token_uuid = models.UUIDField(primary_key=True, default=uuid4)
@@ -143,7 +143,7 @@ class EnrollmentToken(ExpiringModel, SerializerModel):
]
class DeviceAuthenticationToken(ExpiringModel):
class DeviceAuthenticationToken(InternallyManagedMixin, ExpiringModel):
identifier = models.UUIDField(default=uuid4, primary_key=True)
device = models.ForeignKey(Device, on_delete=models.CASCADE)
@@ -160,7 +160,7 @@ class DeviceAuthenticationToken(ExpiringModel):
verbose_name_plural = _("Device authentication tokens")
class AppleNonce(ExpiringModel):
class AppleNonce(InternallyManagedMixin, ExpiringModel):
nonce = models.TextField()
device_token = models.ForeignKey(DeviceToken, on_delete=models.CASCADE)

View File

@@ -1,5 +1,4 @@
from datetime import timedelta
from hashlib import sha256
from hmac import compare_digest
from django.http import HttpResponse
@@ -9,7 +8,7 @@ from rest_framework.exceptions import ValidationError
from rest_framework.fields import CharField, IntegerField
from authentik.crypto.models import CertificateKeyPair
from authentik.endpoints.connectors.agent.models import DeviceAuthenticationToken, DeviceToken
from authentik.endpoints.connectors.agent.models import DeviceToken
from authentik.endpoints.models import Device, EndpointStage, StageMode
from authentik.flows.challenge import (
Challenge,
@@ -21,7 +20,6 @@ from authentik.lib.generators import generate_id
from authentik.lib.utils.time import timedelta_from_string
from authentik.providers.oauth2.models import JWTAlgorithms
PLAN_CONTEXT_DEVICE_AUTH_TOKEN = "goauthentik.io/endpoints/device_auth_token" # nosec
PLAN_CONTEXT_AGENT_ENDPOINT_CHALLENGE = "goauthentik.io/endpoints/connectors/agent/challenge"
QS_CHALLENGE = "challenge"
QS_CHALLENGE_RESPONSE = "response"
@@ -87,36 +85,12 @@ class AuthenticatorEndpointStageView(ChallengeStageView):
response_class = EndpointAgentChallengeResponse
def get(self, request, *args, **kwargs):
# Check if we're in a device interactive auth flow, in which case we use that
# to prove which device is being used
if response := self.check_device_ia():
return response
stage: EndpointStage = self.executor.current_stage
keypair = CertificateKeyPair.objects.filter(pk=stage.connector.challenge_key_id).first()
if not keypair:
return self.executor.stage_ok()
return super().get(request, *args, **kwargs)
def check_device_ia(self):
"""Check if we're in a device interactive authentication flow, and if so,
there won't be a browser extension to talk to. However we can authenticate
on the DTH header"""
if PLAN_CONTEXT_DEVICE_AUTH_TOKEN not in self.executor.plan.context:
return None
auth_token: DeviceAuthenticationToken = self.executor.plan.context.get(
PLAN_CONTEXT_DEVICE_AUTH_TOKEN
)
device_token_hash = self.request.headers.get("X-Authentik-Platform-Auth-DTH")
if not device_token_hash:
return None
if not compare_digest(
device_token_hash, sha256(auth_token.device_token.key.encode()).hexdigest()
):
return self.executor.stage_invalid("Invalid device token")
self.logger.debug("Setting device based on DTH header")
self.executor.plan.context[PLAN_CONTEXT_DEVICE] = auth_token.device
return self.executor.stage_ok()
def get_challenge(self, *args, **kwargs) -> Challenge:
stage: EndpointStage = self.executor.current_stage
keypair = CertificateKeyPair.objects.get(pk=stage.connector.challenge_key_id)

View File

@@ -1,6 +1,4 @@
from hashlib import sha256
from json import loads
from unittest.mock import PropertyMock, patch
from django.urls import reverse
from jwt import encode
@@ -9,14 +7,10 @@ from authentik.core.tests.utils import create_test_cert, create_test_flow
from authentik.endpoints.connectors.agent.models import (
AgentConnector,
AgentDeviceConnection,
DeviceAuthenticationToken,
DeviceToken,
EnrollmentToken,
)
from authentik.endpoints.connectors.agent.stage import (
PLAN_CONTEXT_AGENT_ENDPOINT_CHALLENGE,
PLAN_CONTEXT_DEVICE_AUTH_TOKEN,
)
from authentik.endpoints.connectors.agent.stage import PLAN_CONTEXT_AGENT_ENDPOINT_CHALLENGE
from authentik.endpoints.models import Device, EndpointStage, StageMode
from authentik.flows.models import FlowStageBinding
from authentik.flows.planner import PLAN_CONTEXT_DEVICE
@@ -41,11 +35,6 @@ class TestEndpointStage(FlowTestCase):
device=self.connection,
key=generate_id(),
)
self.device_auth_token = DeviceAuthenticationToken.objects.create(
device=self.device,
device_token=self.device_token,
connector=self.connector,
)
def test_endpoint_stage(self):
flow = create_test_flow()
@@ -205,71 +194,3 @@ class TestEndpointStage(FlowTestCase):
"response": [{"string": "Invalid challenge response", "code": "invalid"}]
},
)
def test_endpoint_stage_ia_dth(self):
"""Test with DTH"""
flow = create_test_flow()
stage = EndpointStage.objects.create(connector=self.connector)
FlowStageBinding.objects.create(stage=stage, target=flow, order=0)
# Send an "invalid" request first, to populate the flow plan
res = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
plan = self.get_flow_plan()
plan.context[PLAN_CONTEXT_DEVICE_AUTH_TOKEN] = DeviceAuthenticationToken.objects.get(
pk=self.device_auth_token.pk
)
self.set_flow_plan(plan)
with self.assertFlowFinishes() as plan:
res = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
HTTP_X_AUTHENTIK_PLATFORM_AUTH_DTH=sha256(
self.device_token.key.encode()
).hexdigest(),
)
self.assertStageRedirects(res, reverse("authentik_core:root-redirect"))
plan = plan()
self.assertNotIn(PLAN_CONTEXT_AGENT_ENDPOINT_CHALLENGE, plan.context)
self.assertEqual(plan.context[PLAN_CONTEXT_DEVICE], self.device)
def test_endpoint_stage_connector_no_stage_optional(self):
flow = create_test_flow()
stage = EndpointStage.objects.create(connector=self.connector, mode=StageMode.OPTIONAL)
FlowStageBinding.objects.create(stage=stage, target=flow, order=0)
with patch(
"authentik.endpoints.connectors.agent.models.AgentConnector.stage",
PropertyMock(return_value=None),
):
with self.assertFlowFinishes() as plan:
res = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
self.assertStageRedirects(res, reverse("authentik_core:root-redirect"))
plan = plan()
self.assertNotIn(PLAN_CONTEXT_AGENT_ENDPOINT_CHALLENGE, plan.context)
self.assertNotIn(PLAN_CONTEXT_DEVICE, plan.context)
def test_endpoint_stage_connector_no_stage_required(self):
flow = create_test_flow()
stage = EndpointStage.objects.create(connector=self.connector, mode=StageMode.REQUIRED)
FlowStageBinding.objects.create(stage=stage, target=flow, order=0)
with patch(
"authentik.endpoints.connectors.agent.models.AgentConnector.stage",
PropertyMock(return_value=None),
):
with self.assertFlowFinishes() as plan:
res = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
self.assertStageResponse(
res,
component="ak-stage-access-denied",
error_message="Invalid stage configuration",
)
plan = plan()
self.assertNotIn(PLAN_CONTEXT_AGENT_ENDPOINT_CHALLENGE, plan.context)
self.assertNotIn(PLAN_CONTEXT_DEVICE, plan.context)

View File

@@ -15,7 +15,7 @@ from authentik.core.models import AttributesMixin, ExpiringModel
from authentik.flows.models import Stage
from authentik.flows.stage import StageView
from authentik.lib.merge import MERGE_LIST_UNIQUE
from authentik.lib.models import InheritanceForeignKey, SerializerModel
from authentik.lib.models import InheritanceForeignKey, InternallyManagedMixin, SerializerModel
from authentik.lib.utils.time import timedelta_from_string, timedelta_string_validator
from authentik.policies.models import PolicyBinding, PolicyBindingModel
from authentik.tasks.schedules.common import ScheduleSpec
@@ -28,7 +28,7 @@ LOGGER = get_logger()
DEVICE_FACTS_CACHE_TIMEOUT = 3600
class Device(ExpiringModel, AttributesMixin, PolicyBindingModel):
class Device(InternallyManagedMixin, ExpiringModel, AttributesMixin, PolicyBindingModel):
device_uuid = models.UUIDField(default=uuid4, primary_key=True)
name = models.TextField(unique=True)
@@ -86,7 +86,7 @@ class DeviceUserBinding(PolicyBinding):
verbose_name_plural = _("Device User bindings")
class DeviceConnection(SerializerModel):
class DeviceConnection(InternallyManagedMixin, SerializerModel):
device_connection_uuid = models.UUIDField(default=uuid4, primary_key=True)
device = models.ForeignKey("Device", on_delete=models.CASCADE)
connector = models.ForeignKey("Connector", on_delete=models.CASCADE)
@@ -115,7 +115,7 @@ class DeviceConnection(SerializerModel):
verbose_name_plural = _("Device connections")
class DeviceFactSnapshot(ExpiringModel, SerializerModel):
class DeviceFactSnapshot(InternallyManagedMixin, ExpiringModel, SerializerModel):
snapshot_id = models.UUIDField(primary_key=True, default=uuid4)
connection = models.ForeignKey(DeviceConnection, on_delete=models.CASCADE)
data = models.JSONField(default=dict)

View File

@@ -1,4 +1,4 @@
from authentik.endpoints.models import EndpointStage, StageMode
from authentik.endpoints.models import EndpointStage
from authentik.flows.stage import StageView
PLAN_CONTEXT_ENDPOINT_CONNECTOR = "endpoint_connector"
@@ -6,24 +6,15 @@ PLAN_CONTEXT_ENDPOINT_CONNECTOR = "endpoint_connector"
class EndpointStageView(StageView):
def _get_inner(self) -> StageView | None:
def _get_inner(self):
stage: EndpointStage = self.executor.current_stage
inner_stage: type[StageView] | None = stage.connector.stage
if not inner_stage:
return None
return self.executor.stage_ok()
return inner_stage(self.executor, request=self.request)
def dispatch(self, request, *args, **kwargs):
inner = self._get_inner()
if inner is None:
stage: EndpointStage = self.executor.current_stage
if stage.mode == StageMode.OPTIONAL:
return self.executor.stage_ok()
else:
return self.executor.stage_invalid("Invalid stage configuration")
return inner.dispatch(request, *args, **kwargs)
return self._get_inner().dispatch(request, *args, **kwargs)
def cleanup(self):
inner = self._get_inner()
if inner is not None:
return inner.cleanup()
return self._get_inner().cleanup()

View File

@@ -60,18 +60,20 @@ class TestEndpointFacts(APITestCase):
]
}
)
self.assertCountEqual(
device.cached_facts.data["software"],
[
{
"name": "software-a",
"version": "1.2.3.4",
"source": "package",
},
{
"name": "software-b",
"version": "5.6.7.8",
"source": "package",
},
],
self.assertEqual(
device.cached_facts.data,
{
"software": [
{
"name": "software-a",
"version": "1.2.3.4",
"source": "package",
},
{
"name": "software-b",
"version": "5.6.7.8",
"source": "package",
},
]
},
)

View File

@@ -8,7 +8,6 @@ from authentik.endpoints.connectors.agent.auth import (
check_device_policies,
)
from authentik.endpoints.connectors.agent.models import AgentConnector, DeviceAuthenticationToken
from authentik.endpoints.connectors.agent.stage import PLAN_CONTEXT_DEVICE_AUTH_TOKEN
from authentik.endpoints.models import Device
from authentik.enterprise.policy import EnterprisePolicyAccessView
from authentik.flows.exceptions import FlowNonApplicableException
@@ -17,6 +16,8 @@ from authentik.flows.planner import PLAN_CONTEXT_DEVICE, FlowPlanner
from authentik.flows.stage import StageView
from authentik.providers.oauth2.utils import HttpResponseRedirectScheme
PLAN_CONTEXT_DEVICE_AUTH_TOKEN = "goauthentik.io/endpoints/device_auth_token" # nosec
QS_AGENT_IA_TOKEN = "ak-auth-ia-token" # nosec

View File

@@ -115,7 +115,7 @@ class LicenseKey:
decode(
jwt,
our_cert.public_key(),
algorithms=["ES384", "ES512"],
algorithms=["ES512"],
audience=get_license_aud(),
options={"verify_exp": check_expiry, "verify_signature": check_expiry},
),

View File

@@ -11,7 +11,7 @@ from django.utils.translation import gettext as _
from rest_framework.serializers import BaseSerializer
from authentik.core.models import ExpiringModel
from authentik.lib.models import SerializerModel
from authentik.lib.models import InternallyManagedMixin, SerializerModel
if TYPE_CHECKING:
from authentik.enterprise.license import LicenseKey
@@ -81,7 +81,7 @@ class LicenseUsageStatus(models.TextChoices):
return self in [LicenseUsageStatus.VALID, LicenseUsageStatus.EXPIRY_SOON]
class LicenseUsage(ExpiringModel):
class LicenseUsage(InternallyManagedMixin, ExpiringModel):
"""a single license usage record"""
expires = models.DateTimeField(default=usage_expiry)

View File

@@ -44,13 +44,20 @@ class GoogleWorkspaceGroupClient(
email=f"{slugify(obj.name)}@{self.provider.default_group_email_domain}",
)
def delete(self, identifier: str):
def delete(self, obj: Group):
"""Delete group"""
GoogleWorkspaceProviderGroup.objects.filter(
provider=self.provider, google_id=identifier
).delete()
if self.provider.group_delete_action == OutgoingSyncDeleteAction.DELETE:
return self._request(self.directory_service.groups().delete(groupKey=identifier))
google_group = GoogleWorkspaceProviderGroup.objects.filter(
provider=self.provider, group=obj
).first()
if not google_group:
self.logger.debug("Group does not exist in Google, skipping")
return None
with transaction.atomic():
if self.provider.group_delete_action == OutgoingSyncDeleteAction.DELETE:
self._request(
self.directory_service.groups().delete(groupKey=google_group.google_id)
)
google_group.delete()
def create(self, group: Group):
"""Create group from scratch and create a connection object"""

View File

@@ -35,17 +35,28 @@ class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceP
"""Convert authentik user"""
return delete_none_values(super().to_schema(obj, connection, primaryEmail=obj.email))
def delete(self, identifier: str):
def delete(self, obj: User):
"""Delete user"""
GoogleWorkspaceProviderUser.objects.filter(
provider=self.provider, google_id=identifier
).delete()
if self.provider.user_delete_action == OutgoingSyncDeleteAction.DELETE:
return self._request(self.directory_service.users().delete(userKey=identifier))
if self.provider.user_delete_action == OutgoingSyncDeleteAction.SUSPEND:
return self._request(
self.directory_service.users().update(userKey=identifier, body={"suspended": True})
)
google_user = GoogleWorkspaceProviderUser.objects.filter(
provider=self.provider, user=obj
).first()
if not google_user:
self.logger.debug("User does not exist in Google, skipping")
return None
with transaction.atomic():
response = None
if self.provider.user_delete_action == OutgoingSyncDeleteAction.DELETE:
response = self._request(
self.directory_service.users().delete(userKey=google_user.google_id)
)
elif self.provider.user_delete_action == OutgoingSyncDeleteAction.SUSPEND:
response = self._request(
self.directory_service.users().update(
userKey=google_user.google_id, body={"suspended": True}
)
)
google_user.delete()
return response
def create(self, user: User):
"""Create user from scratch and create a connection object"""

View File

@@ -18,7 +18,7 @@ from authentik.core.models import (
User,
UserTypes,
)
from authentik.lib.models import SerializerModel
from authentik.lib.models import InternallyManagedMixin, SerializerModel
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction, OutgoingSyncProvider
@@ -32,7 +32,7 @@ def default_scopes() -> list[str]:
]
class GoogleWorkspaceProviderUser(SerializerModel):
class GoogleWorkspaceProviderUser(InternallyManagedMixin, SerializerModel):
"""Mapping of a user and provider to a Google user ID"""
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@@ -58,7 +58,7 @@ class GoogleWorkspaceProviderUser(SerializerModel):
return f"Google Workspace Provider User {self.user_id} to {self.provider_id}"
class GoogleWorkspaceProviderGroup(SerializerModel):
class GoogleWorkspaceProviderGroup(InternallyManagedMixin, SerializerModel):
"""Mapping of a group and provider to a Google group ID"""
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@@ -152,18 +152,6 @@ class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
return Group.objects.all().order_by("pk")
raise ValueError(f"Invalid type {type}")
@classmethod
def get_object_mappings(cls, obj: User | Group) -> list[tuple[str, str]]:
if isinstance(obj, User):
return list(
obj.googleworkspaceprovideruser_set.values_list("provider__pk", "google_id")
)
if isinstance(obj, Group):
return list(
obj.googleworkspaceprovidergroup_set.values_list("provider__pk", "google_id")
)
raise ValueError(f"Invalid type {type(obj)}")
def google_credentials(self):
return {
"credentials": Credentials.from_service_account_info(

View File

@@ -2,7 +2,6 @@
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProvider
from authentik.enterprise.providers.google_workspace.tasks import (
google_workspace_sync_delete_dispatch,
google_workspace_sync_direct_dispatch,
google_workspace_sync_m2m_dispatch,
)
@@ -11,6 +10,5 @@ from authentik.lib.sync.outgoing.signals import register_signals
register_signals(
GoogleWorkspaceProvider,
task_sync_direct_dispatch=google_workspace_sync_direct_dispatch,
task_sync_delete_dispatch=google_workspace_sync_delete_dispatch,
task_sync_m2m_dispatch=google_workspace_sync_m2m_dispatch,
)

View File

@@ -25,18 +25,6 @@ def google_workspace_sync_direct(*args, **kwargs):
return sync_tasks.sync_signal_direct(*args, **kwargs)
@actor(
description=_("Dispatch deletions for an object (user, group) for Google Workspace providers.")
)
def google_workspace_sync_delete_dispatch(*args, **kwargs):
return sync_tasks.sync_signal_delete_dispatch(google_workspace_sync_delete, *args, **kwargs)
@actor(description=_("Delete an object (user, group) for Google Workspace provider."))
def google_workspace_sync_delete(*args, **kwargs):
return sync_tasks.sync_signal_delete(*args, **kwargs)
@actor(
description=_(
"Dispatch syncs for a direct object (user, group) for Google Workspace providers."

View File

@@ -48,13 +48,18 @@ class MicrosoftEntraGroupClient(
except TypeError as exc:
raise StopSync(exc, obj) from exc
def delete(self, identifier: str):
def delete(self, obj: Group):
"""Delete group"""
MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, microsoft_id=identifier
).delete()
if self.provider.group_delete_action == OutgoingSyncDeleteAction.DELETE:
return self._request(self.client.groups.by_group_id(identifier).delete())
microsoft_group = MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group=obj
).first()
if not microsoft_group:
self.logger.debug("Group does not exist in Microsoft, skipping")
return None
with transaction.atomic():
if self.provider.group_delete_action == OutgoingSyncDeleteAction.DELETE:
self._request(self.client.groups.by_group_id(microsoft_group.microsoft_id).delete())
microsoft_group.delete()
def create(self, group: Group):
"""Create group from scratch and create a connection object"""

View File

@@ -43,17 +43,28 @@ class MicrosoftEntraUserClient(MicrosoftEntraSyncClient[User, MicrosoftEntraProv
except TypeError as exc:
raise StopSync(exc, obj) from exc
def delete(self, identifier: str):
def delete(self, obj: User):
"""Delete user"""
MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, microsoft_id=identifier
).delete()
if self.provider.user_delete_action == OutgoingSyncDeleteAction.DELETE:
return self._request(self.client.users.by_user_id(identifier).delete())
if self.provider.user_delete_action == OutgoingSyncDeleteAction.SUSPEND:
return self._request(
self.client.users.by_user_id(identifier).patch(MSUser(account_enabled=False))
)
microsoft_user = MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, user=obj
).first()
if not microsoft_user:
self.logger.debug("User does not exist in Microsoft, skipping")
return None
with transaction.atomic():
response = None
if self.provider.user_delete_action == OutgoingSyncDeleteAction.DELETE:
response = self._request(
self.client.users.by_user_id(microsoft_user.microsoft_id).delete()
)
elif self.provider.user_delete_action == OutgoingSyncDeleteAction.SUSPEND:
response = self._request(
self.client.users.by_user_id(microsoft_user.microsoft_id).patch(
MSUser(account_enabled=False)
)
)
microsoft_user.delete()
return response
def get_select_fields(self) -> list[str]:
"""All fields that should be selected when we fetch user data."""

View File

@@ -18,12 +18,12 @@ from authentik.core.models import (
User,
UserTypes,
)
from authentik.lib.models import SerializerModel
from authentik.lib.models import InternallyManagedMixin, SerializerModel
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction, OutgoingSyncProvider
class MicrosoftEntraProviderUser(SerializerModel):
class MicrosoftEntraProviderUser(InternallyManagedMixin, SerializerModel):
"""Mapping of a user and provider to a Microsoft user ID"""
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@@ -49,7 +49,7 @@ class MicrosoftEntraProviderUser(SerializerModel):
return f"Microsoft Entra Provider User {self.user_id} to {self.provider_id}"
class MicrosoftEntraProviderGroup(SerializerModel):
class MicrosoftEntraProviderGroup(InternallyManagedMixin, SerializerModel):
"""Mapping of a group and provider to a Microsoft group ID"""
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@@ -141,18 +141,6 @@ class MicrosoftEntraProvider(OutgoingSyncProvider, BackchannelProvider):
return Group.objects.all().order_by("pk")
raise ValueError(f"Invalid type {type}")
@classmethod
def get_object_mappings(cls, obj: User | Group) -> list[tuple[str, str]]:
if isinstance(obj, User):
return list(
obj.microsoftentraprovideruser_set.values_list("provider__pk", "microsoft_id")
)
if isinstance(obj, Group):
return list(
obj.microsoftentraprovidergroup_set.values_list("provider__pk", "microsoft_id")
)
raise ValueError(f"Invalid type {type(obj)}")
def microsoft_credentials(self):
return {
"credentials": ClientSecretCredential(

View File

@@ -2,7 +2,6 @@
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProvider
from authentik.enterprise.providers.microsoft_entra.tasks import (
microsoft_entra_sync_delete_dispatch,
microsoft_entra_sync_direct_dispatch,
microsoft_entra_sync_m2m_dispatch,
)
@@ -11,6 +10,5 @@ from authentik.lib.sync.outgoing.signals import register_signals
register_signals(
MicrosoftEntraProvider,
task_sync_direct_dispatch=microsoft_entra_sync_direct_dispatch,
task_sync_delete_dispatch=microsoft_entra_sync_delete_dispatch,
task_sync_m2m_dispatch=microsoft_entra_sync_m2m_dispatch,
)

View File

@@ -32,18 +32,6 @@ def microsoft_entra_sync_direct_dispatch(*args, **kwargs):
return sync_tasks.sync_signal_direct_dispatch(microsoft_entra_sync_direct, *args, **kwargs)
@actor(description=_("Delete an object (user, group) for Microsoft Entra provider."))
def microsoft_entra_sync_delete(*args, **kwargs):
return sync_tasks.sync_signal_delete(*args, **kwargs)
@actor(
description=_("Dispatch deletions for an object (user, group) for Microsoft Entra providers.")
)
def microsoft_entra_sync_delete_dispatch(*args, **kwargs):
return sync_tasks.sync_signal_delete_dispatch(microsoft_entra_sync_delete, *args, **kwargs)
@actor(description=_("Sync a related object (memberships) for Microsoft Entra provider."))
def microsoft_entra_sync_m2m(*args, **kwargs):
return sync_tasks.sync_signal_m2m(*args, **kwargs)

View File

@@ -14,7 +14,7 @@ from jwt import encode
from authentik.core.models import BackchannelProvider, ExpiringModel, Token
from authentik.crypto.models import CertificateKeyPair
from authentik.lib.models import CreatedUpdatedModel
from authentik.lib.models import CreatedUpdatedModel, InternallyManagedMixin
from authentik.lib.utils.time import timedelta_from_string, timedelta_string_validator
from authentik.providers.oauth2.models import JWTAlgorithms, OAuth2Provider
from authentik.tasks.models import TasksModel
@@ -153,7 +153,7 @@ class Stream(models.Model):
return encode(data, key, algorithm=alg, headers=headers)
class StreamEvent(CreatedUpdatedModel, ExpiringModel):
class StreamEvent(InternallyManagedMixin, CreatedUpdatedModel, ExpiringModel):
"""Single stream event to be sent"""
uuid = models.UUIDField(default=uuid4, primary_key=True, editable=False)

View File

@@ -11,7 +11,7 @@ from rest_framework.serializers import BaseSerializer, Serializer
from authentik.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
from authentik.flows.stage import StageView
from authentik.lib.models import DeprecatedMixin, SerializerModel
from authentik.lib.models import DeprecatedMixin, InternallyManagedMixin, SerializerModel
from authentik.stages.authenticator.models import Device
@@ -63,7 +63,7 @@ class AuthenticatorEndpointGDTCStage(DeprecatedMixin, ConfigurableStage, Friendl
verbose_name_plural = _("Endpoint Authenticator Google Device Trust Connector Stages")
class EndpointDevice(SerializerModel, Device):
class EndpointDevice(InternallyManagedMixin, SerializerModel, Device):
"""Endpoint Device for a single user"""
uuid = models.UUIDField(primary_key=True, default=uuid4)
@@ -91,7 +91,7 @@ class EndpointDevice(SerializerModel, Device):
verbose_name_plural = _("Endpoint Devices")
class EndpointDeviceConnection(models.Model):
class EndpointDeviceConnection(InternallyManagedMixin, models.Model):
device = models.ForeignKey(EndpointDevice, on_delete=models.CASCADE)
stage = models.ForeignKey(AuthenticatorEndpointGDTCStage, on_delete=models.CASCADE)

View File

@@ -19,6 +19,7 @@ from authentik.blueprints.v1.importer import excluded_models
from authentik.core.models import Group, User
from authentik.events.models import Event, EventAction, Notification
from authentik.events.utils import model_to_dict
from authentik.lib.models import InternallyManagedMixin
from authentik.lib.sentry import should_ignore_exception
from authentik.lib.utils.errors import exception_to_dict
from authentik.stages.authenticator_static.models import StaticToken
@@ -40,7 +41,7 @@ _CTX_REQUEST = ContextVar[HttpRequest | None]("authentik_events_log_request", de
def should_log_model(model: Model) -> bool:
"""Return true if operation on `model` should be logged"""
return model.__class__ not in IGNORED_MODELS
return model.__class__ not in IGNORED_MODELS and not isinstance(model, InternallyManagedMixin)
def should_log_m2m(model: Model) -> bool:

View File

@@ -93,13 +93,11 @@ def on_login_failed(
credentials: dict[str, str],
request: HttpRequest,
stage: Stage | None = None,
context: dict[str, Any] | None = None,
**kwargs,
):
"""Failed Login, authentik custom event"""
user = User.objects.filter(username=credentials.get("username")).first()
context = context or {}
Event.new(EventAction.LOGIN_FAILED, **credentials, stage=stage, **context).from_http(
Event.new(EventAction.LOGIN_FAILED, **credentials, stage=stage, **kwargs).from_http(
request, user
)

View File

@@ -22,7 +22,6 @@ from authentik.core.api.utils import (
LinkSerializer,
ModelSerializer,
PassiveSerializer,
ThemedUrlsSerializer,
)
from authentik.events.logs import LogEventSerializer
from authentik.flows.api.flows_diagram import FlowDiagram, FlowDiagramSerializer
@@ -48,7 +47,6 @@ class FlowSerializer(ModelSerializer):
"""Flow Serializer"""
background_url = ReadOnlyField()
background_themed_urls = ThemedUrlsSerializer(read_only=True, allow_null=True)
cache_count = SerializerMethodField()
export_url = SerializerMethodField()
@@ -72,7 +70,6 @@ class FlowSerializer(ModelSerializer):
"designation",
"background",
"background_url",
"background_themed_urls",
"stages",
"policies",
"cache_count",

View File

@@ -29,12 +29,6 @@ class RefreshOtherFlowsAfterAuthentication(Flag[bool], key="flows_refresh_others
visibility = "public"
class ContinuousLogin(Flag[bool], key="flows_continuous_login"):
default = False
visibility = "public"
class AuthentikFlowsConfig(ManagedAppConfig):
"""authentik flows app config"""

18
authentik/flows/auth.py Normal file
View File

@@ -0,0 +1,18 @@
from typing import cast
from django.contrib.auth.models import AnonymousUser
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from authentik.flows.views.executor import SESSION_KEY_PLAN
class FlowActive(BaseAuthentication):
"""Authenticate requests when a flow is currently active"""
def authenticate(self, request: Request):
plan = cast(FlowPlan | None, request.session.get(SESSION_KEY_PLAN))
if not plan:
return None
return (plan.context.get(PLAN_CONTEXT_PENDING_USER, AnonymousUser()), plan)

View File

@@ -11,7 +11,7 @@ from django.http import JsonResponse
from rest_framework.fields import BooleanField, CharField, ChoiceField, DictField
from rest_framework.request import Request
from authentik.core.api.utils import PassiveSerializer, ThemedUrlsSerializer
from authentik.core.api.utils import PassiveSerializer
from authentik.lib.utils.errors import exception_to_string
if TYPE_CHECKING:
@@ -44,7 +44,6 @@ class ContextualFlowInfo(PassiveSerializer):
title = CharField(required=False, allow_blank=True)
background = CharField(required=False)
background_themed_urls = ThemedUrlsSerializer(required=False, allow_null=True)
cancel_url = CharField()
layout = ChoiceField(choices=[(x.value, x.name) for x in FlowLayout])

View File

@@ -20,7 +20,7 @@ from authentik.core.models import Token
from authentik.core.types import UserSettingSerializer
from authentik.flows.challenge import FlowLayout
from authentik.lib.config import CONFIG
from authentik.lib.models import InheritanceForeignKey, SerializerModel
from authentik.lib.models import InheritanceForeignKey, InternallyManagedMixin, SerializerModel
from authentik.lib.utils.reflection import class_to_path
from authentik.policies.models import PolicyBindingModel
@@ -196,15 +196,6 @@ class Flow(SerializerModel, PolicyBindingModel):
return get_file_manager(FileUsage.MEDIA).file_url(self.background, request)
def background_themed_urls(self, request: HttpRequest | None = None) -> dict[str, str] | None:
"""Get themed URLs for background if it contains %(theme)s"""
if not self.background:
if request:
return request.brand.branding_default_flow_background_themed_urls()
return None
return get_file_manager(FileUsage.MEDIA).themed_urls(self.background, request)
stages = models.ManyToManyField(Stage, through="FlowStageBinding", blank=True)
@property
@@ -310,7 +301,7 @@ class FriendlyNamedStage(models.Model):
abstract = True
class FlowToken(Token):
class FlowToken(InternallyManagedMixin, Token):
"""Subclass of a standard Token, stores the currently active flow plan upon creation.
Can be used to later resume a flow."""

View File

@@ -38,7 +38,7 @@ def invalidate_flow_cache(sender, instance, **_):
if isinstance(instance, Flow):
total = delete_cache_prefix(f"{cache_key(instance)}*")
LOGGER.debug("Invalidating Flow cache", flow=instance, len=total)
if isinstance(instance, FlowStageBinding) and instance.target_id:
if isinstance(instance, FlowStageBinding):
total = delete_cache_prefix(f"{cache_key(instance.target)}*")
LOGGER.debug("Invalidating Flow cache from FlowStageBinding", binding=instance, len=total)
if isinstance(instance, Stage):

View File

@@ -197,9 +197,6 @@ class ChallengeStageView(StageView):
data={
"title": self.format_title(),
"background": self.executor.flow.background_url(self.request),
"background_themed_urls": self.executor.flow.background_themed_urls(
self.request
),
"cancel_url": self.cancel_url,
"layout": self.executor.flow.layout,
}

View File

@@ -51,11 +51,6 @@ class FlowTestCase(APITestCase):
def get_flow_plan(self) -> FlowPlan | None:
return self.client.session.get(SESSION_KEY_PLAN)
def set_flow_plan(self, plan: FlowPlan):
session = self.client.session
session[SESSION_KEY_PLAN] = plan
session.save()
def assertStageRedirects(self, response: HttpResponse, to: str) -> dict[str, Any]:
"""Wrapper around assertStageResponse that checks for a redirect"""
return self.assertStageResponse(response, component="xak-flow-redirect", to=to)

View File

@@ -734,7 +734,6 @@ class TestFlowExecutor(FlowTestCase):
flow,
flow_info={
"background": "/static/dist/assets/images/flow_background.jpg",
"background_themed_urls": None,
"cancel_url": "/flows/-/cancel/?next=%2Ffoo",
"layout": "stacked",
"title": flow.title,

View File

@@ -51,7 +51,6 @@ class TestFlowInspector(APITestCase):
"enable_remember_me": False,
"flow_info": {
"background": "/static/dist/assets/images/flow_background.jpg",
"background_themed_urls": None,
"cancel_url": reverse("authentik_flows:cancel"),
"title": flow.title,
"layout": "stacked",

Some files were not shown because too many files have changed in this diff Show More