Compare commits

...

65 Commits

Author SHA1 Message Date
25181d079e ...bandit
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-23 21:07:31 +00:00
5b91cb5ff3 yeah
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-23 20:51:47 +00:00
8ba5fde5ba fix
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-23 20:43:39 +00:00
2802deb497 tests/e2e: don't rely DNS to get host's IP for container access
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-23 19:30:24 +00:00
3eccef88aa web: Fix prop. (#13630) 2025-03-22 17:18:07 +01:00
8f50dfa0c5 core, web: update translations (#13628)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2025-03-22 13:28:36 +00:00
8417d8508f web/admin: reworked sync status card (#13625)
* reworked sync status

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

* update imports

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

* add story and fix import

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-03-21 22:48:28 +00:00
b2c2fc001b core: bump github.com/golang-jwt/jwt/v5 from 5.2.1 to 5.2.2 (#13626)
Bumps [github.com/golang-jwt/jwt/v5](https://github.com/golang-jwt/jwt) from 5.2.1 to 5.2.2.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v5.2.1...v5.2.2)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v5
  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-03-21 22:48:16 +00:00
f60312cbbc translate: Updates for file web/xliff/en.xlf in zh-Hans (#13622)
Translate web/xliff/en.xlf in zh-Hans

100% translated source file: 'web/xliff/en.xlf'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-21 20:16:25 +00:00
7614b17a05 web: bump API Client version (#13623)
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-03-21 20:16:17 +00:00
8947376edb translate: Updates for file web/xliff/en.xlf in zh_CN (#13621)
Translate web/xliff/en.xlf in zh_CN

100% translated source file: 'web/xliff/en.xlf'
on 'zh_CN'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-21 20:16:12 +00:00
ce23209ae8 events: add configurable headers to webhooks (#13602)
* events: add configurable headers to webhooks

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

* make it a full thing

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

* fix migration

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-21 19:37:15 +00:00
0b806b7130 web: Client-side MDX rendering (#13610)
* web: Allow build errors to propagate.

* web: Refactor MDX for client-side rendering.

* Remove override

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

* revert css for links and tables

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

* web: Move Markdown specific styles.

---------

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-03-21 19:32:52 +00:00
9538cf4690 website/docs: revert token_expiry format in example blueprint (#13582)
* revert token_expiry format in example blueprint

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

* Revert blueprint change, use hard link to the previous version in the docs

---------

Signed-off-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-03-21 19:13:35 +01:00
63da458fb3 website: update header to match goauthentik.io (#13616)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-21 17:33:44 +00:00
873dab29a9 translate: Updates for file web/xliff/en.xlf in fr (#13615)
Translate web/xliff/en.xlf in fr

100% translated source file: 'web/xliff/en.xlf'
on 'fr'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-21 16:28:58 +00:00
1e96c80593 core, web: update translations (#13608)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2025-03-21 17:14:08 +01:00
ee4a922234 core: bump astral-sh/uv from 0.6.8 to 0.6.9 (#13612)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.6.8 to 0.6.9.
- [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.6.8...0.6.9)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  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-03-21 17:11:54 +01:00
37a2eff716 website: components: add multilinecodeblock component (#13179)
* wip

* wip

* wip

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

wip

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

wip

* wip

* wip

* move css to same folder

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-03-21 10:08:24 -05:00
50e2f1c474 website/docs: add clarification about formatting URLs to Style Guide (#13601)
* a few additions

* codespell fixes??

* clarify URL formatting

* tweak

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* conflict of course

* bump build

---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.com>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-03-20 20:10:34 -05:00
ab7338b50e core: bump github.com/redis/go-redis/v9 from 9.7.1 to 9.7.3 (#13603)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.7.1 to 9.7.3.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.7.1...v9.7.3)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  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-03-20 19:41:12 +00:00
bcdc6fcd36 lib: set a default HTTP timeout on outgoing requests (#13599)
* lib: set a default HTTP timeout on outgoing requests

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

* add config

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-20 16:44:12 +00:00
98c3e0d68b website/integrations: add miniflux (#13559)
* website/integrations: add miniflux

* fixes authentik capitalisation

* Updated to match suggestions

* Update website/integrations/services/miniflux/index.md

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <chwshka@outlook.com>

* Updated to include <kbd> and <em> tags

* fixed codeblock

* Added explanatory text and removed extra EM tags

* fixed prettier issue

* Update website/integrations/services/miniflux/index.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <chwshka@outlook.com>

* Update website/integrations/services/miniflux/index.md

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/integrations/services/miniflux/index.md

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

---------

Signed-off-by: Dewi Roberts <chwshka@outlook.com>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-03-20 08:52:49 -05:00
a2b82b6448 web: bump API Client version (#13585)
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-03-20 13:08:26 +00:00
0456ace646 core: bump goauthentik.io/api/v3 from 3.2025022.3 to 3.2025022.5 (#13594)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025022.3 to 3.2025022.5.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Changelog](https://github.com/goauthentik/client-go/blob/main/model_version_history.go)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2025022.3...v3.2025022.5)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  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-03-20 13:08:13 +00:00
d3a11ce810 website/docs: style guide & integration template: revamp (#12929)
* website/docs: style guide: revamp

* fix anchor

* Update style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Update website/docs/developer-docs/docs/style-guide.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Tana's suggested format 

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

* lint

* wip

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

* wip

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

* wip

lint

* Update website/docs/developer-docs/docs/style-guide.mdx

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

* tana

* fix

---------

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-03-20 07:47:00 -05:00
bfd1445c69 translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#13592)
Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-03-20 07:58:17 +00:00
c2b3e9b05c translate: Updates for file web/xliff/en.xlf in fr (#13597)
Translate web/xliff/en.xlf in fr

100% translated source file: 'web/xliff/en.xlf'
on 'fr'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 07:57:31 +00:00
2c7d841e4a translate: Updates for file locale/en/LC_MESSAGES/django.po in fr (#13596)
Translate locale/en/LC_MESSAGES/django.po in fr

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fr'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 07:55:47 +00:00
c5d13c4a15 translate: Updates for file web/xliff/en.xlf in zh-Hans (#13593)
Translate web/xliff/en.xlf in zh-Hans

100% translated source file: 'web/xliff/en.xlf'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 08:40:22 +01:00
079ef6e114 translate: Updates for file web/xliff/en.xlf in zh_CN (#13591)
Translate web/xliff/en.xlf in zh_CN

100% translated source file: 'web/xliff/en.xlf'
on 'zh_CN'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 08:40:02 +01:00
98bfca0b4d translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#13590)
Translate locale/en/LC_MESSAGES/django.po in zh_CN

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'zh_CN'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-03-20 08:40:00 +01:00
a247bd5b9f core, web: update translations (#13584)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2025-03-20 01:07:24 +00:00
27856ec301 brands: add option to set global default flow background (#13079)
* brands: add option to set global default flow background

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

* test

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-20 01:07:05 +00:00
e4a8c05d25 web/admin: fix diff showing previous false as "-" (#13580)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-19 23:06:37 +00:00
cb2e0c6d54 web: bump API Client version (#13581)
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-03-19 23:04:21 +00:00
f37e1ca642 brands: migrate custom CSS to brands (#13172)
* brands: migrate custom CSS to brands

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

* fix missing default

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

* fix tests

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

* simpler migration

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

* add css to brand form

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>
2025-03-19 22:52:38 +00:00
70b1f05a84 website/integrations: jellyfin: update OIDC plugin installation (#13544)
Updated OIDC plugin installation

Signed-off-by: Махно Артём Сергеевич <Netoen@users.noreply.github.com>
2025-03-19 20:49:26 +01:00
192ed8f494 root: fix uv lock file on macOS (#13578)
* root: fix uv lock on macOS

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

* re-add make run

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-19 20:38:27 +01:00
b69d77d270 core: bump goauthentik.io/api/v3 from 3.2025022.2 to 3.2025022.3 (#13576)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025022.2 to 3.2025022.3.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Changelog](https://github.com/goauthentik/client-go/blob/main/model_version_history.go)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2025022.2...v3.2025022.3)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  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-03-19 19:23:42 +00:00
35b6801ba0 core: bump astral-sh/uv from 0.6.6 to 0.6.8 (#13577)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.6.6 to 0.6.8.
- [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.6.6...0.6.8)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  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-03-19 19:23:19 +00:00
f9e6f57aad lifecycle/aws: bump aws-cdk from 2.1004.0 to 2.1005.0 in /lifecycle/aws (#13574)
Bumps [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) from 2.1004.0 to 2.1005.0.
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1005.0/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  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-03-19 16:46:35 +00:00
868261c883 root: replace poetry with uv (#13388) 2025-03-19 16:29:18 +00:00
b6442c233d web: Fix inline documentation rendering (#13379)
web: Fix issues surrounding markdown rendering.

- Fix issue where Mermaid diagrams do not render.
- Fix link colors in dark mode.
- Fix anchored links triggering router.
- Fix issue where links occasionally link to missing page.
2025-03-19 17:09:47 +01:00
74292e6c23 web: bump API Client version (#13572)
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-03-19 15:02:17 +00:00
3e2cf4fd30 core: bump msgraph-sdk from 1.24.0 to 1.25.0 (#13570)
Bumps [msgraph-sdk](https://github.com/microsoftgraph/msgraph-sdk-python) from 1.24.0 to 1.25.0.
- [Release notes](https://github.com/microsoftgraph/msgraph-sdk-python/releases)
- [Changelog](https://github.com/microsoftgraph/msgraph-sdk-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/microsoftgraph/msgraph-sdk-python/compare/v1.24.0...v1.25.0)

---
updated-dependencies:
- dependency-name: msgraph-sdk
  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-03-19 15:02:06 +00:00
05cbb4ce0c core: bump goauthentik.io/api/v3 from 3.2025022.1 to 3.2025022.2 (#13569)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025022.1 to 3.2025022.2.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Changelog](https://github.com/goauthentik/client-go/blob/main/model_version_history.go)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2025022.1...v3.2025022.2)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  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-03-19 15:01:54 +00:00
c93d85731c providers/saml: configurable AuthnContextClassRef (#13566)
* providers/saml: make AuthnContextClassRef configurable

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

* providers/saml: fix incorrect AuthInstant

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

* add tests

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-19 14:42:55 +00:00
d163afe87c website/integrations: Beszel (#12905)
* initial release

* Ready for PR

* never changed this?

Used these commands after fresh git pull:

make lint-fix && npx prettier --write ./website/integrations/services/beszel/index.mdx && make website


Signed-off-by: NiceDevil <17103076+nicedevil007@users.noreply.github.com>

* reference to PocketBase

As @4d62 suggestet, here is the much simpler version as it uses 1:1 settings from PocketBase

* index.mdx aktualisieren

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: NiceDevil <17103076+nicedevil007@users.noreply.github.com>

* index.mdx aktualisieren

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: NiceDevil <17103076+nicedevil007@users.noreply.github.com>

* index.mdx aktualisieren

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: NiceDevil <17103076+nicedevil007@users.noreply.github.com>

* add full instructions

* add Beszel config

* Update website/integrations/services/beszel/index.mdx

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* Update website/integrations/services/beszel/index.mdx

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>

* correct redirect URI

Signed-off-by: NiceDevil <17103076+nicedevil007@users.noreply.github.com>

---------

Signed-off-by: NiceDevil <17103076+nicedevil007@users.noreply.github.com>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: nicedevil007 <nicedevil007@users.noreply.github.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.com>
2025-03-18 17:34:28 -05:00
eac2c9a12b website/docs: Apple Business Manager Integration. (#13319)
website/docs: Flesh out Apple Business Manager integration.

- Fix missing required attribute.
- Adjust visible headers in table of contents.
- Fix Docusaurus warning for nested paragraphs.
2025-03-18 19:56:34 +00:00
c10e4a9063 website: package.json: copy over schema for npm run watch (#13543)
* root: Makefile: copy over schema for website-watch

`make website-watch` will fail with the following error if the schema.yml file is not present in the website's static directory. This was tested on a branch up to date with main with the schema file I copied over removed.
```
➜  authentik git:(sdko/integrations-all/migr-to-wizard) ✗ w
cd website && npm run watch

> @goauthentik/website-docs@0.0.0 watch
> docusaurus gen-api-docs all && docusaurus start

(node:102678) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
Loading of api failed for "/home/d/Code/authentik/website/static/schema.yml"

[ERROR] [Error: ENOENT: no such file or directory, lstat '/home/d/Code/authentik/website/static/schema.yml'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'lstat',
  path: '/home/d/Code/authentik/website/static/schema.yml'
}
[INFO] Docusaurus version: 3.7.0
Node version: v23.6.1
make: *** [Makefile:233: website-watch] Error 1
```

We should copy over the schema each time in case it changes. Adding it to this Makefile instruction as [official docs](https://docs.goauthentik.io/docs/developer-docs/setup/website-dev-environment) tell users to run `make website-install` which simply `npm ci`s, then this...

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

* tabs?

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

* Update Makefile

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

* Update package.json

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

* Update package.json

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

---------

Signed-off-by: Dominic R <dominic@sdko.org>
2025-03-18 16:02:52 +00:00
4e4adcc672 web: bump API Client version (#13565)
* web: bump API Client version

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix migration

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

---------

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-03-18 14:48:55 +00:00
bb20576d84 providers/scim: save attributes returned from remote system like google workspace and entra ID (#13459)
providers/scim: save attributes returned from remote system like google workspace and entra

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-18 13:35:56 +00:00
5f315bddbd scripts: postgres: print statements (#13537)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-03-18 13:34:39 +00:00
9e0404646b core: bump goauthentik.io/api/v3 from 3.2025021.4 to 3.2025022.1 (#13556)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025021.4 to 3.2025022.1.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Changelog](https://github.com/goauthentik/client-go/blob/main/model_version_history.go)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2025021.4...v3.2025022.1)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  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-03-18 13:34:20 +00:00
45883ff86b core: bump sentry-sdk from 2.22.0 to 2.23.1 (#13557)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.22.0 to 2.23.1.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.22.0...2.23.1)

---
updated-dependencies:
- dependency-name: sentry-sdk
  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-03-18 13:34:13 +00:00
915f5689c6 website: bump the build group in /website with 6 updates (#13558)
Bumps the build group in /website with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [@swc/core-darwin-arm64](https://github.com/swc-project/swc) | `1.11.9` | `1.11.11` |
| [@swc/core-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.11.9` | `1.11.11` |
| [@swc/core-linux-x64-gnu](https://github.com/swc-project/swc) | `1.11.9` | `1.11.11` |
| [@swc/html-darwin-arm64](https://github.com/swc-project/swc) | `1.11.9` | `1.11.11` |
| [@swc/html-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.11.9` | `1.11.11` |
| [@swc/html-linux-x64-gnu](https://github.com/swc-project/swc) | `1.11.9` | `1.11.11` |


Updates `@swc/core-darwin-arm64` from 1.11.9 to 1.11.11
- [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.11.9...v1.11.11)

Updates `@swc/core-linux-arm64-gnu` from 1.11.9 to 1.11.11
- [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.11.9...v1.11.11)

Updates `@swc/core-linux-x64-gnu` from 1.11.9 to 1.11.11
- [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.11.9...v1.11.11)

Updates `@swc/html-darwin-arm64` from 1.11.9 to 1.11.11
- [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.11.9...v1.11.11)

Updates `@swc/html-linux-arm64-gnu` from 1.11.9 to 1.11.11
- [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.11.9...v1.11.11)

Updates `@swc/html-linux-x64-gnu` from 1.11.9 to 1.11.11
- [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.11.9...v1.11.11)

---
updated-dependencies:
- dependency-name: "@swc/core-darwin-arm64"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-darwin-arm64"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-linux-arm64-gnu"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-linux-x64-gnu"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-18 13:33:39 +00:00
ce1ea926f8 web: bump API Client version (#13555)
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-03-17 21:27:43 +00:00
2e3624ea82 release: 2025.2.2 (#13554) 2025-03-17 22:10:22 +01:00
4e52fb7e52 stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#13541)
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-03-17 20:06:27 +00:00
7e36fb2153 website: Bump the build group in /website with 3 updates (#13549)
Bumps the build group in /website with 3 updates: [lightningcss-darwin-arm64](https://github.com/parcel-bundler/lightningcss), [lightningcss-linux-arm64-gnu](https://github.com/parcel-bundler/lightningcss) and [lightningcss-linux-x64-gnu](https://github.com/parcel-bundler/lightningcss).


Updates `lightningcss-darwin-arm64` from 1.29.2 to 1.29.3
- [Release notes](https://github.com/parcel-bundler/lightningcss/releases)
- [Commits](https://github.com/parcel-bundler/lightningcss/compare/v1.29.2...v1.29.3)

Updates `lightningcss-linux-arm64-gnu` from 1.29.2 to 1.29.3
- [Release notes](https://github.com/parcel-bundler/lightningcss/releases)
- [Commits](https://github.com/parcel-bundler/lightningcss/compare/v1.29.2...v1.29.3)

Updates `lightningcss-linux-x64-gnu` from 1.29.2 to 1.29.3
- [Release notes](https://github.com/parcel-bundler/lightningcss/releases)
- [Commits](https://github.com/parcel-bundler/lightningcss/compare/v1.29.2...v1.29.3)

---
updated-dependencies:
- dependency-name: lightningcss-darwin-arm64
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: lightningcss-linux-arm64-gnu
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: lightningcss-linux-x64-gnu
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-17 20:05:43 +00:00
2b00754324 website/docs: prepare for 2025.2.2 (#13552) 2025-03-17 18:53:39 +01:00
12a73ef306 core: Bump aws-cdk-lib from 2.184.0 to 2.184.1 (#13547)
Bumps [aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.184.0 to 2.184.1.
- [Release notes](https://github.com/aws/aws-cdk/releases)
- [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md)
- [Commits](https://github.com/aws/aws-cdk/compare/v2.184.0...v2.184.1)

---
updated-dependencies:
- dependency-name: aws-cdk-lib
  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-03-17 18:51:58 +01:00
4469db9b23 core: Bump coverage from 7.6.12 to 7.7.0 (#13548)
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.6.12 to 7.7.0.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.6.12...7.7.0)

---
updated-dependencies:
- dependency-name: coverage
  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-03-17 18:50:48 +01:00
b7beac6795 stages/identification: check captcha after checking authentication (#13533) 2025-03-17 17:10:00 +00:00
157 changed files with 10142 additions and 7170 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2025.2.1
current_version = 2025.2.2
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?

View File

@ -9,17 +9,22 @@ inputs:
runs:
using: "composite"
steps:
- name: Install poetry & deps
- name: Install apt deps
shell: bash
run: |
pipx install poetry || true
sudo apt-get update
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
- name: Setup python and restore poetry
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: Setup python
uses: actions/setup-python@v5
with:
python-version-file: "pyproject.toml"
cache: "poetry"
- name: Install Python deps
shell: bash
run: uv sync --all-extras --dev --frozen
- name: Setup node
uses: actions/setup-node@v4
with:
@ -39,10 +44,9 @@ runs:
run: |
export PSQL_TAG=${{ inputs.postgresql_version }}
docker compose -f .github/actions/setup/docker-compose.yml up -d
poetry sync
cd web && npm ci
- name: Generate config
shell: poetry run python {0}
shell: uv run python {0}
run: |
from authentik.lib.generators import generate_id
from yaml import safe_dump

View File

@ -98,7 +98,7 @@ updates:
prefix: "lifecycle/aws:"
labels:
- dependencies
- package-ecosystem: pip
- package-ecosystem: uv
directory: "/"
schedule:
interval: daily

View File

@ -33,7 +33,7 @@ jobs:
npm ci
- name: Check changes have been applied
run: |
poetry run make aws-cfn
uv run make aws-cfn
git diff --exit-code
ci-aws-cfn-mark:
if: always()

View File

@ -34,7 +34,7 @@ jobs:
- name: Setup authentik env
uses: ./.github/actions/setup
- name: run job
run: poetry run make ci-${{ matrix.job }}
run: uv run make ci-${{ matrix.job }}
test-migrations:
runs-on: ubuntu-latest
steps:
@ -42,7 +42,7 @@ jobs:
- name: Setup authentik env
uses: ./.github/actions/setup
- name: run migrations
run: poetry run python -m lifecycle.migrate
run: uv run python -m lifecycle.migrate
test-make-seed:
runs-on: ubuntu-latest
steps:
@ -69,19 +69,21 @@ jobs:
fetch-depth: 0
- name: checkout stable
run: |
# Delete all poetry envs
rm -rf /home/runner/.cache/pypoetry
# Copy current, latest config to local
# Temporarly comment the .github backup while migrating to uv
cp authentik/lib/default.yml local.env.yml
cp -R .github ..
# cp -R .github ..
cp -R scripts ..
git checkout $(git tag --sort=version:refname | grep '^version/' | grep -vE -- '-rc[0-9]+$' | tail -n1)
rm -rf .github/ scripts/
mv ../.github ../scripts .
# rm -rf .github/ scripts/
# mv ../.github ../scripts .
rm -rf scripts/
mv ../scripts .
- name: Setup authentik env (stable)
uses: ./.github/actions/setup
with:
postgresql_version: ${{ matrix.psql }}
continue-on-error: true
- name: run migrations to stable
run: poetry run python -m lifecycle.migrate
- name: checkout current code
@ -91,15 +93,13 @@ jobs:
git reset --hard HEAD
git clean -d -fx .
git checkout $GITHUB_SHA
# Delete previous poetry env
rm -rf /home/runner/.cache/pypoetry/virtualenvs/*
- name: Setup authentik env (ensure latest deps are installed)
uses: ./.github/actions/setup
with:
postgresql_version: ${{ matrix.psql }}
- name: migrate to latest
run: |
poetry run python -m lifecycle.migrate
uv run python -m lifecycle.migrate
- name: run tests
env:
# Test in the main database that we just migrated from the previous stable version
@ -108,7 +108,7 @@ jobs:
CI_RUN_ID: ${{ matrix.run_id }}
CI_TOTAL_RUNS: "5"
run: |
poetry run make ci-test
uv run make ci-test
test-unittest:
name: test-unittest - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5
runs-on: ubuntu-latest
@ -133,7 +133,7 @@ jobs:
CI_RUN_ID: ${{ matrix.run_id }}
CI_TOTAL_RUNS: "5"
run: |
poetry run make ci-test
uv run make ci-test
- if: ${{ always() }}
uses: codecov/codecov-action@v5
with:
@ -156,8 +156,8 @@ jobs:
uses: helm/kind-action@v1.12.0
- name: run integration
run: |
poetry run coverage run manage.py test tests/integration
poetry run coverage xml
uv run coverage run manage.py test tests/integration
uv run coverage xml
- if: ${{ always() }}
uses: codecov/codecov-action@v5
with:
@ -214,8 +214,8 @@ jobs:
npm run build
- name: run e2e
run: |
poetry run coverage run manage.py test ${{ matrix.job.glob }}
poetry run coverage xml
uv run coverage run manage.py test ${{ matrix.job.glob }}
uv run coverage xml
- if: ${{ always() }}
uses: codecov/codecov-action@v5
with:

View File

@ -2,7 +2,7 @@ name: authentik-gen-update-webauthn-mds
on:
workflow_dispatch:
schedule:
- cron: '30 1 1,15 * *'
- cron: "30 1 1,15 * *"
env:
POSTGRES_DB: authentik
@ -24,7 +24,7 @@ jobs:
token: ${{ steps.generate_token.outputs.token }}
- name: Setup authentik env
uses: ./.github/actions/setup
- run: poetry run ak update_webauthn_mds
- run: uv run ak update_webauthn_mds
- uses: peter-evans/create-pull-request@v7
id: cpr
with:

View File

@ -21,8 +21,8 @@ jobs:
uses: ./.github/actions/setup
- name: generate docs
run: |
poetry run make migrate
poetry run ak build_source_docs
uv run make migrate
uv run ak build_source_docs
- name: Publish
uses: netlify/actions/cli@master
with:

View File

@ -36,10 +36,10 @@ jobs:
run: make gen-client-ts
- name: run extract
run: |
poetry run make i18n-extract
uv run make i18n-extract
- name: run compile
run: |
poetry run ak compilemessages
uv run ak compilemessages
make web-check-compile
- name: Create Pull Request
if: ${{ github.event_name != 'pull_request' }}

46
.vscode/tasks.json vendored
View File

@ -3,8 +3,13 @@
"tasks": [
{
"label": "authentik/core: make",
"command": "poetry",
"args": ["run", "make", "lint-fix", "lint"],
"command": "uv",
"args": [
"run",
"make",
"lint-fix",
"lint"
],
"presentation": {
"panel": "new"
},
@ -12,8 +17,12 @@
},
{
"label": "authentik/core: run",
"command": "poetry",
"args": ["run", "ak", "server"],
"command": "uv",
"args": [
"run",
"ak",
"server"
],
"group": "build",
"presentation": {
"panel": "dedicated",
@ -23,13 +32,17 @@
{
"label": "authentik/web: make",
"command": "make",
"args": ["web"],
"args": [
"web"
],
"group": "build"
},
{
"label": "authentik/web: watch",
"command": "make",
"args": ["web-watch"],
"args": [
"web-watch"
],
"group": "build",
"presentation": {
"panel": "dedicated",
@ -39,19 +52,26 @@
{
"label": "authentik: install",
"command": "make",
"args": ["install", "-j4"],
"args": [
"install",
"-j4"
],
"group": "build"
},
{
"label": "authentik/website: make",
"command": "make",
"args": ["website"],
"args": [
"website"
],
"group": "build"
},
{
"label": "authentik/website: watch",
"command": "make",
"args": ["website-watch"],
"args": [
"website-watch"
],
"group": "build",
"presentation": {
"panel": "dedicated",
@ -60,8 +80,12 @@
},
{
"label": "authentik/api: generate",
"command": "poetry",
"args": ["run", "make", "gen"],
"command": "uv",
"args": [
"run",
"make",
"gen"
],
"group": "build"
}
]

View File

@ -10,7 +10,7 @@ schemas/ @goauthentik/backend
scripts/ @goauthentik/backend
tests/ @goauthentik/backend
pyproject.toml @goauthentik/backend
poetry.lock @goauthentik/backend
uv.lock @goauthentik/backend
go.mod @goauthentik/backend
go.sum @goauthentik/backend
# Infrastructure

View File

@ -3,8 +3,7 @@
# Stage 1: Build website
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 AS website-builder
ENV NODE_ENV=production \
GIT_UNAVAILABLE=true
ENV NODE_ENV=production
WORKDIR /work/website
@ -94,53 +93,59 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
mkdir -p /usr/share/GeoIP && \
/bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 5: Python dependencies
FROM ghcr.io/goauthentik/fips-python:3.12.8-slim-bookworm-fips AS python-deps
# Stage 5: Download uv
FROM ghcr.io/astral-sh/uv:0.6.9 AS uv
# Stage 6: Base python image
FROM ghcr.io/goauthentik/fips-python:3.12.8-slim-bookworm-fips AS python-base
ENV VENV_PATH="/ak-root/.venv" \
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \
UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
UV_NATIVE_TLS=1 \
UV_PYTHON_DOWNLOADS=0
WORKDIR /ak-root/
COPY --from=uv /uv /uvx /bin/
# Stage 7: Python dependencies
FROM python-base AS python-deps
ARG TARGETARCH
ARG TARGETVARIANT
WORKDIR /ak-root/poetry
ENV VENV_PATH="/ak-root/venv" \
POETRY_VIRTUALENVS_CREATE=false \
PATH="/ak-root/venv/bin:$PATH"
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
ENV PATH="/root/.cargo/bin:$PATH"
RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/var/cache/apt \
apt-get update && \
# Required for installing pip packages
apt-get install -y --no-install-recommends build-essential pkg-config libpq-dev libkrb5-dev
RUN --mount=type=bind,target=./pyproject.toml,src=./pyproject.toml \
--mount=type=bind,target=./poetry.lock,src=./poetry.lock \
--mount=type=cache,target=/root/.cache/pip \
--mount=type=cache,target=/root/.cache/pypoetry \
pip install --no-cache cffi && \
apt-get update && \
apt-get install -y --no-install-recommends \
build-essential libffi-dev \
# Required for cryptography
curl pkg-config \
# Required for lxml
libxslt-dev zlib1g-dev \
# Required for xmlsec
libltdl-dev \
# Required for kadmin
sccache clang && \
curl https://sh.rustup.rs -sSf | sh -s -- -y && \
. "$HOME/.cargo/env" && \
python -m venv /ak-root/venv/ && \
bash -c "source ${VENV_PATH}/bin/activate && \
pip3 install --upgrade pip poetry && \
poetry config --local installer.no-binary cryptography,xmlsec,lxml,python-kadmin-rs && \
poetry install --only=main --no-ansi --no-interaction --no-root && \
pip uninstall cryptography -y && \
poetry install --only=main --no-ansi --no-interaction --no-root"
# Build essentials
build-essential pkg-config libffi-dev git \
# cryptography
curl \
# libxml
libxslt-dev zlib1g-dev \
# postgresql
libpq-dev \
# python-kadmin-rs
clang libkrb5-dev sccache \
# xmlsec
libltdl-dev && \
curl https://sh.rustup.rs -sSf | sh -s -- -y
# Stage 6: Run
FROM ghcr.io/goauthentik/fips-python:3.12.8-slim-bookworm-fips AS final-image
ENV UV_NO_BINARY_PACKAGE="cryptography lxml python-kadmin-rs xmlsec"
RUN --mount=type=bind,target=pyproject.toml,src=pyproject.toml \
--mount=type=bind,target=uv.lock,src=uv.lock \
--mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-install-project --no-dev
# Stage 8: Run
FROM python-base AS final-image
ARG VERSION
ARG GIT_BUILD_HASH
@ -172,7 +177,7 @@ RUN apt-get update && \
COPY ./authentik/ /authentik
COPY ./pyproject.toml /
COPY ./poetry.lock /
COPY ./uv.lock /
COPY ./schemas /schemas
COPY ./locale /locale
COPY ./tests /tests
@ -181,7 +186,7 @@ COPY ./blueprints /blueprints
COPY ./lifecycle/ /lifecycle
COPY ./authentik/sources/kerberos/krb5.conf /etc/krb5.conf
COPY --from=go-builder /go/authentik /bin/authentik
COPY --from=python-deps /ak-root/venv /ak-root/venv
COPY --from=python-deps /ak-root/.venv /ak-root/.venv
COPY --from=web-builder /work/web/dist/ /web/dist/
COPY --from=web-builder /work/web/authentik/ /web/authentik/
COPY --from=website-builder /work/website/build/ /website/help/
@ -192,9 +197,6 @@ USER 1000
ENV TMPDIR=/dev/shm/ \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PATH="/ak-root/venv/bin:/lifecycle:$PATH" \
VENV_PATH="/ak-root/venv" \
POETRY_VIRTUALENVS_CREATE=false \
GOFIPS=1
HEALTHCHECK --interval=30s --timeout=30s --start-period=60s --retries=3 CMD [ "ak", "healthcheck" ]

View File

@ -12,9 +12,9 @@ GEN_API_TS = "gen-ts-api"
GEN_API_PY = "gen-py-api"
GEN_API_GO = "gen-go-api"
pg_user := $(shell poetry run python -m authentik.lib.config postgresql.user 2>/dev/null)
pg_host := $(shell poetry run python -m authentik.lib.config postgresql.host 2>/dev/null)
pg_name := $(shell poetry run python -m authentik.lib.config postgresql.name 2>/dev/null)
pg_user := $(shell uv run python -m authentik.lib.config postgresql.user 2>/dev/null)
pg_host := $(shell uv run python -m authentik.lib.config postgresql.host 2>/dev/null)
pg_name := $(shell uv run python -m authentik.lib.config postgresql.name 2>/dev/null)
all: lint-fix lint test gen web ## Lint, build, and test everything
@ -32,34 +32,37 @@ go-test:
go test -timeout 0 -v -race -cover ./...
test: ## Run the server tests and produce a coverage report (locally)
poetry run coverage run manage.py test --keepdb authentik
poetry run coverage html
poetry run coverage report
uv run coverage run manage.py test --keepdb authentik
uv run coverage html
uv run coverage report
lint-fix: lint-codespell ## Lint and automatically fix errors in the python source code. Reports spelling errors.
poetry run black $(PY_SOURCES)
poetry run ruff check --fix $(PY_SOURCES)
uv run black $(PY_SOURCES)
uv run ruff check --fix $(PY_SOURCES)
lint-codespell: ## Reports spelling errors.
poetry run codespell -w
uv run codespell -w
lint: ## Lint the python and golang sources
poetry run bandit -c pyproject.toml -r $(PY_SOURCES)
uv run bandit -c pyproject.toml -r $(PY_SOURCES)
golangci-lint run -v
core-install:
poetry install
uv sync --frozen
migrate: ## Run the Authentik Django server's migrations
poetry run python -m lifecycle.migrate
uv run python -m lifecycle.migrate
i18n-extract: core-i18n-extract web-i18n-extract ## Extract strings that require translation into files to send to a translation service
aws-cfn:
cd lifecycle/aws && npm run aws-cfn
run: ## Run the main authentik server process
uv run ak server
core-i18n-extract:
poetry run ak makemessages \
uv run ak makemessages \
--add-location file \
--no-obsolete \
--ignore web \
@ -90,11 +93,11 @@ gen-build: ## Extract the schema from the database
AUTHENTIK_DEBUG=true \
AUTHENTIK_TENANTS__ENABLED=true \
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
poetry run ak make_blueprint_schema > blueprints/schema.json
uv run ak make_blueprint_schema > blueprints/schema.json
AUTHENTIK_DEBUG=true \
AUTHENTIK_TENANTS__ENABLED=true \
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
poetry run ak spectacular --file schema.yml
uv run ak spectacular --file schema.yml
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
@ -173,7 +176,7 @@ gen-client-go: gen-clean-go ## Build and install the authentik API for Golang
rm -rf ./${GEN_API_GO}/config.yaml ./${GEN_API_GO}/templates/
gen-dev-config: ## Generate a local development config file
poetry run scripts/generate_config.py
uv run scripts/generate_config.py
gen: gen-build gen-client-ts
@ -254,21 +257,21 @@ ci--meta-debug:
node --version
ci-black: ci--meta-debug
poetry run black --check $(PY_SOURCES)
uv run black --check $(PY_SOURCES)
ci-ruff: ci--meta-debug
poetry run ruff check $(PY_SOURCES)
uv run ruff check $(PY_SOURCES)
ci-codespell: ci--meta-debug
poetry run codespell -s
uv run codespell -s
ci-bandit: ci--meta-debug
poetry run bandit -r $(PY_SOURCES)
uv run bandit -r $(PY_SOURCES)
ci-pending-migrations: ci--meta-debug
poetry run ak makemigrations --check
uv run ak makemigrations --check
ci-test: ci--meta-debug
poetry run coverage run manage.py test --keepdb --randomly-seed ${CI_TEST_SEED} authentik
poetry run coverage report
poetry run coverage xml
uv run coverage run manage.py test --keepdb --randomly-seed ${CI_TEST_SEED} authentik
uv run coverage report
uv run coverage xml

View File

@ -2,7 +2,7 @@
from os import environ
__version__ = "2025.2.1"
__version__ = "2025.2.2"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -49,6 +49,8 @@ class BrandSerializer(ModelSerializer):
"branding_title",
"branding_logo",
"branding_favicon",
"branding_custom_css",
"branding_default_flow_background",
"flow_authentication",
"flow_invalidation",
"flow_recovery",
@ -86,6 +88,7 @@ class CurrentBrandSerializer(PassiveSerializer):
branding_title = CharField()
branding_logo = CharField(source="branding_logo_url")
branding_favicon = CharField(source="branding_favicon_url")
branding_custom_css = CharField()
ui_footer_links = ListField(
child=FooterLinkSerializer(),
read_only=True,
@ -125,6 +128,7 @@ class BrandViewSet(UsedByMixin, ModelViewSet):
"branding_title",
"branding_logo",
"branding_favicon",
"branding_default_flow_background",
"flow_authentication",
"flow_invalidation",
"flow_recovery",

View File

@ -0,0 +1,35 @@
# Generated by Django 5.0.12 on 2025-02-22 01:51
from pathlib import Path
from django.db import migrations, models
from django.apps.registry import Apps
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def migrate_custom_css(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
Brand = apps.get_model("authentik_brands", "brand")
db_alias = schema_editor.connection.alias
path = Path("/web/dist/custom.css")
if not path.exists():
return
with path.read_text() as css:
Brand.objects.using(db_alias).update(branding_custom_css=css)
class Migration(migrations.Migration):
dependencies = [
("authentik_brands", "0007_brand_default_application"),
]
operations = [
migrations.AddField(
model_name="brand",
name="branding_custom_css",
field=models.TextField(blank=True, default=""),
),
migrations.RunPython(migrate_custom_css),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.13 on 2025-03-19 22:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_brands", "0008_brand_branding_custom_css"),
]
operations = [
migrations.AddField(
model_name="brand",
name="branding_default_flow_background",
field=models.TextField(default="/static/dist/assets/images/flow_background.jpg"),
),
]

View File

@ -33,6 +33,10 @@ class Brand(SerializerModel):
branding_logo = models.TextField(default="/static/dist/assets/icons/icon_left_brand.svg")
branding_favicon = models.TextField(default="/static/dist/assets/icons/icon.png")
branding_custom_css = models.TextField(default="", blank=True)
branding_default_flow_background = models.TextField(
default="/static/dist/assets/images/flow_background.jpg"
)
flow_authentication = models.ForeignKey(
Flow, null=True, on_delete=models.SET_NULL, related_name="brand_authentication"
@ -84,6 +88,12 @@ class Brand(SerializerModel):
return CONFIG.get("web.path", "/")[:-1] + self.branding_favicon
return self.branding_favicon
def branding_default_flow_background_url(self) -> str:
"""Get branding_default_flow_background with the correct prefix"""
if self.branding_default_flow_background.startswith("/static"):
return CONFIG.get("web.path", "/")[:-1] + self.branding_default_flow_background
return self.branding_default_flow_background
@property
def serializer(self) -> Serializer:
from authentik.brands.api import BrandSerializer

View File

@ -24,6 +24,7 @@ class TestBrands(APITestCase):
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_title": "authentik",
"branding_custom_css": "",
"matched_domain": brand.domain,
"ui_footer_links": [],
"ui_theme": Themes.AUTOMATIC,
@ -43,6 +44,7 @@ class TestBrands(APITestCase):
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_title": "custom",
"branding_custom_css": "",
"matched_domain": "bar.baz",
"ui_footer_links": [],
"ui_theme": Themes.AUTOMATIC,
@ -59,6 +61,7 @@ class TestBrands(APITestCase):
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_title": "authentik",
"branding_custom_css": "",
"matched_domain": "fallback",
"ui_footer_links": [],
"ui_theme": Themes.AUTOMATIC,
@ -121,3 +124,27 @@ class TestBrands(APITestCase):
"subject": None,
},
)
def test_branding_url(self):
"""Test branding attributes return correct values"""
brand = create_test_brand()
brand.branding_default_flow_background = "https://goauthentik.io/img/icon.png"
brand.branding_favicon = "https://goauthentik.io/img/icon.png"
brand.branding_logo = "https://goauthentik.io/img/icon.png"
brand.save()
self.assertEqual(
brand.branding_default_flow_background_url(), "https://goauthentik.io/img/icon.png"
)
self.assertJSONEqual(
self.client.get(reverse("authentik_api:brand-current")).content.decode(),
{
"branding_logo": "https://goauthentik.io/img/icon.png",
"branding_favicon": "https://goauthentik.io/img/icon.png",
"branding_title": "authentik",
"branding_custom_css": "",
"matched_domain": brand.domain,
"ui_footer_links": [],
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
},
)

View File

@ -16,7 +16,7 @@
{% block head_before %}
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/custom.css' %}" data-inject>
<style>{{ brand.branding_custom_css }}</style>
<script src="{% versioned_script 'dist/poly-%v.js' %}" type="module"></script>
<script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script>
{% block head %}

View File

@ -4,7 +4,7 @@
{% load i18n %}
{% block head_before %}
<link rel="prefetch" href="{% static 'dist/assets/images/flow_background.jpg' %}" />
<link rel="prefetch" href="{{ request.brand.branding_default_flow_background_url }}" />
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}" media="(prefers-color-scheme: dark)">
{% include "base/header_js.html" %}
@ -13,7 +13,7 @@
{% block head %}
<style>
:root {
--ak-flow-background: url("{% static 'dist/assets/images/flow_background.jpg' %}");
--ak-flow-background: url("{{ request.brand.branding_default_flow_background_url }}");
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--sm: var(--ak-flow-background);

View File

@ -50,7 +50,8 @@ class NotificationTransportSerializer(ModelSerializer):
"mode",
"mode_verbose",
"webhook_url",
"webhook_mapping",
"webhook_mapping_body",
"webhook_mapping_headers",
"send_once",
]

View File

@ -0,0 +1,43 @@
# Generated by Django 5.0.13 on 2025-03-20 19:54
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_events", "0008_event_authentik_e_expires_8c73a8_idx_and_more"),
]
operations = [
migrations.RenameField(
model_name="notificationtransport",
old_name="webhook_mapping",
new_name="webhook_mapping_body",
),
migrations.AlterField(
model_name="notificationtransport",
name="webhook_mapping_body",
field=models.ForeignKey(
default=None,
help_text="Customize the body of the request. Mapping should return data that is JSON-serializable.",
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
related_name="+",
to="authentik_events.notificationwebhookmapping",
),
),
migrations.AddField(
model_name="notificationtransport",
name="webhook_mapping_headers",
field=models.ForeignKey(
default=None,
help_text="Configure additional headers to be sent. Mapping should return a dictionary of key-value pairs",
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
related_name="+",
to="authentik_events.notificationwebhookmapping",
),
),
]

View File

@ -336,8 +336,27 @@ class NotificationTransport(SerializerModel):
mode = models.TextField(choices=TransportMode.choices, default=TransportMode.LOCAL)
webhook_url = models.TextField(blank=True, validators=[DomainlessURLValidator()])
webhook_mapping = models.ForeignKey(
"NotificationWebhookMapping", on_delete=models.SET_DEFAULT, null=True, default=None
webhook_mapping_body = models.ForeignKey(
"NotificationWebhookMapping",
on_delete=models.SET_DEFAULT,
null=True,
default=None,
related_name="+",
help_text=_(
"Customize the body of the request. "
"Mapping should return data that is JSON-serializable."
),
)
webhook_mapping_headers = models.ForeignKey(
"NotificationWebhookMapping",
on_delete=models.SET_DEFAULT,
null=True,
default=None,
related_name="+",
help_text=_(
"Configure additional headers to be sent. "
"Mapping should return a dictionary of key-value pairs"
),
)
send_once = models.BooleanField(
default=False,
@ -360,8 +379,8 @@ class NotificationTransport(SerializerModel):
def send_local(self, notification: "Notification") -> list[str]:
"""Local notification delivery"""
if self.webhook_mapping:
self.webhook_mapping.evaluate(
if self.webhook_mapping_body:
self.webhook_mapping_body.evaluate(
user=notification.user,
request=None,
notification=notification,
@ -380,9 +399,18 @@ class NotificationTransport(SerializerModel):
if notification.event and notification.event.user:
default_body["event_user_email"] = notification.event.user.get("email", None)
default_body["event_user_username"] = notification.event.user.get("username", None)
if self.webhook_mapping:
headers = {}
if self.webhook_mapping_body:
default_body = sanitize_item(
self.webhook_mapping.evaluate(
self.webhook_mapping_body.evaluate(
user=notification.user,
request=None,
notification=notification,
)
)
if self.webhook_mapping_headers:
headers = sanitize_item(
self.webhook_mapping_headers.evaluate(
user=notification.user,
request=None,
notification=notification,
@ -392,6 +420,7 @@ class NotificationTransport(SerializerModel):
response = get_http_session().post(
self.webhook_url,
json=default_body,
headers=headers,
)
response.raise_for_status()
except RequestException as exc:

View File

@ -120,7 +120,7 @@ class TestEventsNotifications(APITestCase):
)
transport = NotificationTransport.objects.create(
name=generate_id(), webhook_mapping=mapping, mode=TransportMode.LOCAL
name=generate_id(), webhook_mapping_body=mapping, mode=TransportMode.LOCAL
)
NotificationRule.objects.filter(name__startswith="default").delete()
trigger = NotificationRule.objects.create(name=generate_id(), group=self.group)

View File

@ -60,20 +60,25 @@ class TestEventTransports(TestCase):
def test_transport_webhook_mapping(self):
"""Test webhook transport with custom mapping"""
mapping = NotificationWebhookMapping.objects.create(
mapping_body = NotificationWebhookMapping.objects.create(
name=generate_id(), expression="return request.user"
)
mapping_headers = NotificationWebhookMapping.objects.create(
name=generate_id(), expression="""return {"foo": "bar"}"""
)
transport: NotificationTransport = NotificationTransport.objects.create(
name=generate_id(),
mode=TransportMode.WEBHOOK,
webhook_url="http://localhost:1234/test",
webhook_mapping=mapping,
webhook_mapping_body=mapping_body,
webhook_mapping_headers=mapping_headers,
)
with Mocker() as mocker:
mocker.post("http://localhost:1234/test")
transport.send(self.notification)
self.assertEqual(mocker.call_count, 1)
self.assertEqual(mocker.request_history[0].method, "POST")
self.assertEqual(mocker.request_history[0].headers["foo"], "bar")
self.assertJSONEqual(
mocker.request_history[0].body.decode(),
{"email": self.user.email, "pk": self.user.pk, "username": self.user.username},

View File

@ -6,6 +6,7 @@ from typing import TYPE_CHECKING
from uuid import uuid4
from django.db import models
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _
from model_utils.managers import InheritanceManager
from rest_framework.serializers import BaseSerializer
@ -178,14 +179,11 @@ class Flow(SerializerModel, PolicyBindingModel):
help_text=_("Required level of authentication and authorization to access a flow."),
)
@property
def background_url(self) -> str:
def background_url(self, request: HttpRequest) -> str:
"""Get the URL to the background image. If the name is /static or starts with http
it is returned as-is"""
if not self.background:
return (
CONFIG.get("web.path", "/")[:-1] + "/static/dist/assets/images/flow_background.jpg"
)
return request.brand.branding_default_flow_background_url()
if self.background.name.startswith("http"):
return self.background.name
if self.background.name.startswith("/static"):

View File

@ -184,7 +184,7 @@ class ChallengeStageView(StageView):
flow_info = ContextualFlowInfo(
data={
"title": self.format_title(),
"background": self.executor.flow.background_url,
"background": self.executor.flow.background_url(self.request),
"cancel_url": reverse("authentik_flows:cancel"),
"layout": self.executor.flow.layout,
}

View File

@ -27,7 +27,6 @@ class FlowTestCase(APITestCase):
self.assertIsNotNone(raw_response["component"])
if flow:
self.assertIn("flow_info", raw_response)
self.assertEqual(raw_response["flow_info"]["background"], flow.background_url)
self.assertEqual(
raw_response["flow_info"]["cancel_url"], reverse("authentik_flows:cancel")
)

View File

@ -49,7 +49,7 @@ class TestFlowInspector(APITestCase):
"captcha_stage": None,
"component": "ak-stage-identification",
"flow_info": {
"background": flow.background_url,
"background": "/static/dist/assets/images/flow_background.jpg",
"cancel_url": reverse("authentik_flows:cancel"),
"title": flow.title,
"layout": "stacked",

View File

@ -45,6 +45,8 @@ redis:
# url: ""
# transport_options: ""
http_timeout: 30
cache:
# url: ""
timeout: 300

View File

@ -16,7 +16,40 @@ def authentik_user_agent() -> str:
return f"authentik@{get_full_version()}"
class DebugSession(Session):
class TimeoutSession(Session):
"""Always set a default HTTP request timeout"""
def __init__(self, default_timeout=None):
super().__init__()
self.timeout = default_timeout
def send(
self,
request,
*,
stream=...,
verify=...,
proxies=...,
cert=...,
timeout=...,
allow_redirects=...,
**kwargs,
):
if not timeout and self.timeout:
timeout = self.timeout
return super().send(
request,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout,
allow_redirects=allow_redirects,
**kwargs,
)
class DebugSession(TimeoutSession):
"""requests session which logs http requests and responses"""
def send(self, req: PreparedRequest, *args, **kwargs):
@ -42,8 +75,9 @@ class DebugSession(Session):
def get_http_session() -> Session:
"""Get a requests session with common headers"""
session = Session()
session = TimeoutSession()
if CONFIG.get_bool("debug") or CONFIG.get("log_level") == "trace":
session = DebugSession()
session.headers["User-Agent"] = authentik_user_agent()
session.timeout = CONFIG.get_optional_int("http_timeout")
return session

View File

@ -180,6 +180,7 @@ class SAMLProviderSerializer(ProviderSerializer):
"session_valid_not_on_or_after",
"property_mappings",
"name_id_mapping",
"authn_context_class_ref_mapping",
"digest_algorithm",
"signature_algorithm",
"signing_kp",

View File

@ -0,0 +1,28 @@
# Generated by Django 5.0.13 on 2025-03-18 17:41
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_saml", "0016_samlprovider_encryption_kp_and_more"),
]
operations = [
migrations.AddField(
model_name="samlprovider",
name="authn_context_class_ref_mapping",
field=models.ForeignKey(
blank=True,
default=None,
help_text="Configure how the AuthnContextClassRef value will be created. When left empty, the AuthnContextClassRef will be set based on which authentication methods the user used to authenticate.",
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
related_name="+",
to="authentik_providers_saml.samlpropertymapping",
verbose_name="AuthnContextClassRef Property Mapping",
),
),
]

View File

@ -71,6 +71,20 @@ class SAMLProvider(Provider):
"the NameIDPolicy of the incoming request will be considered"
),
)
authn_context_class_ref_mapping = models.ForeignKey(
"SAMLPropertyMapping",
default=None,
blank=True,
null=True,
on_delete=models.SET_DEFAULT,
verbose_name=_("AuthnContextClassRef Property Mapping"),
related_name="+",
help_text=_(
"Configure how the AuthnContextClassRef value will be created. When left empty, "
"the AuthnContextClassRef will be set based on which authentication methods the user "
"used to authenticate."
),
)
assertion_valid_not_before = models.TextField(
default="minutes=-5",
@ -170,7 +184,6 @@ class SAMLProvider(Provider):
def launch_url(self) -> str | None:
"""Use IDP-Initiated SAML flow as launch URL"""
try:
return reverse(
"authentik_providers_saml:sso-init",
kwargs={"application_slug": self.application.slug},

View File

@ -1,5 +1,6 @@
"""SAML Assertion generator"""
from datetime import datetime
from hashlib import sha256
from types import GeneratorType
@ -52,6 +53,7 @@ class AssertionProcessor:
_assertion_id: str
_response_id: str
_auth_instant: str
_valid_not_before: str
_session_not_on_or_after: str
_valid_not_on_or_after: str
@ -65,6 +67,11 @@ class AssertionProcessor:
self._assertion_id = get_random_id()
self._response_id = get_random_id()
_login_event = get_login_event(self.http_request)
_login_time = datetime.now()
if _login_event:
_login_time = _login_event.created
self._auth_instant = get_time_string(_login_time)
self._valid_not_before = get_time_string(
timedelta_from_string(self.provider.assertion_valid_not_before)
)
@ -131,7 +138,7 @@ class AssertionProcessor:
def get_assertion_auth_n_statement(self) -> Element:
"""Generate AuthnStatement with AuthnContext and ContextClassRef Elements."""
auth_n_statement = Element(f"{{{NS_SAML_ASSERTION}}}AuthnStatement")
auth_n_statement.attrib["AuthnInstant"] = self._valid_not_before
auth_n_statement.attrib["AuthnInstant"] = self._auth_instant
auth_n_statement.attrib["SessionIndex"] = sha256(
self.http_request.session.session_key.encode("ascii")
).hexdigest()
@ -158,6 +165,28 @@ class AssertionProcessor:
auth_n_context_class_ref.text = (
"urn:oasis:names:tc:SAML:2.0:ac:classes:MobileOneFactorContract"
)
if self.provider.authn_context_class_ref_mapping:
try:
value = self.provider.authn_context_class_ref_mapping.evaluate(
user=self.http_request.user,
request=self.http_request,
provider=self.provider,
)
if value is not None:
auth_n_context_class_ref.text = str(value)
return auth_n_statement
except PropertyMappingExpressionException as exc:
Event.new(
EventAction.CONFIGURATION_ERROR,
message=(
"Failed to evaluate property-mapping: "
f"'{self.provider.authn_context_class_ref_mapping.name}'"
),
provider=self.provider,
mapping=self.provider.authn_context_class_ref_mapping,
).from_http(self.http_request)
LOGGER.warning("Failed to evaluate property mapping", exc=exc)
return auth_n_statement
return auth_n_statement
def get_assertion_conditions(self) -> Element:

View File

@ -294,6 +294,61 @@ class TestAuthNRequest(TestCase):
self.assertEqual(parsed_request.id, "aws_LDxLGeubpc5lx12gxCgS6uPbix1yd5re")
self.assertEqual(parsed_request.name_id_policy, SAML_NAME_ID_FORMAT_EMAIL)
def test_authn_context_class_ref_mapping(self):
"""Test custom authn_context_class_ref"""
authn_context_class_ref = generate_id()
mapping = SAMLPropertyMapping.objects.create(
name=generate_id(), expression=f"""return '{authn_context_class_ref}'"""
)
self.provider.authn_context_class_ref_mapping = mapping
self.provider.save()
user = create_test_admin_user()
http_request = get_request("/", user=user)
# First create an AuthNRequest
request_proc = RequestProcessor(self.source, http_request, "test_state")
request = request_proc.build_auth_n()
# To get an assertion we need a parsed request (parsed by provider)
parsed_request = AuthNRequestParser(self.provider).parse(
b64encode(request.encode()).decode(), "test_state"
)
# Now create a response and convert it to string (provider)
response_proc = AssertionProcessor(self.provider, http_request, parsed_request)
response = response_proc.build_response()
self.assertIn(user.username, response)
self.assertIn(authn_context_class_ref, response)
def test_authn_context_class_ref_mapping_invalid(self):
"""Test custom authn_context_class_ref (invalid)"""
mapping = SAMLPropertyMapping.objects.create(name=generate_id(), expression="q")
self.provider.authn_context_class_ref_mapping = mapping
self.provider.save()
user = create_test_admin_user()
http_request = get_request("/", user=user)
# First create an AuthNRequest
request_proc = RequestProcessor(self.source, http_request, "test_state")
request = request_proc.build_auth_n()
# To get an assertion we need a parsed request (parsed by provider)
parsed_request = AuthNRequestParser(self.provider).parse(
b64encode(request.encode()).decode(), "test_state"
)
# Now create a response and convert it to string (provider)
response_proc = AssertionProcessor(self.provider, http_request, parsed_request)
response = response_proc.build_response()
self.assertIn(user.username, response)
events = Event.objects.filter(
action=EventAction.CONFIGURATION_ERROR,
)
self.assertTrue(events.exists())
self.assertEqual(
events.first().context["message"],
f"Failed to evaluate property-mapping: '{mapping.name}'",
)
def test_request_attributes(self):
"""Test full SAML Request/Response flow, fully signed"""
user = create_test_admin_user()
@ -321,8 +376,10 @@ class TestAuthNRequest(TestCase):
request = request_proc.build_auth_n()
# Create invalid PropertyMapping
scope = SAMLPropertyMapping.objects.create(name="test", saml_name="test", expression="q")
self.provider.property_mappings.add(scope)
mapping = SAMLPropertyMapping.objects.create(
name=generate_id(), saml_name="test", expression="q"
)
self.provider.property_mappings.add(mapping)
# To get an assertion we need a parsed request (parsed by provider)
parsed_request = AuthNRequestParser(self.provider).parse(
@ -338,7 +395,7 @@ class TestAuthNRequest(TestCase):
self.assertTrue(events.exists())
self.assertEqual(
events.first().context["message"],
"Failed to evaluate property-mapping: 'test'",
f"Failed to evaluate property-mapping: '{mapping.name}'",
)
def test_idp_initiated(self):

View File

@ -1,12 +1,16 @@
"""Time utilities"""
import datetime
from datetime import datetime, timedelta
from django.utils.timezone import now
def get_time_string(delta: datetime.timedelta | None = None) -> str:
def get_time_string(delta: timedelta | datetime | None = None) -> str:
"""Get Data formatted in SAML format"""
if delta is None:
delta = datetime.timedelta()
now = datetime.datetime.now()
final = now + delta
delta = timedelta()
if isinstance(delta, timedelta):
final = now() + delta
else:
final = delta
return final.strftime("%Y-%m-%dT%H:%M:%SZ")

View File

@ -24,7 +24,9 @@ class SCIMProviderGroupSerializer(ModelSerializer):
"group",
"group_obj",
"provider",
"attributes",
]
extra_kwargs = {"attributes": {"read_only": True}}
class SCIMProviderGroupViewSet(

View File

@ -24,7 +24,9 @@ class SCIMProviderUserSerializer(ModelSerializer):
"user",
"user_obj",
"provider",
"attributes",
]
extra_kwargs = {"attributes": {"read_only": True}}
class SCIMProviderUserViewSet(

View File

@ -102,7 +102,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
if not scim_id or scim_id == "":
raise StopSync("SCIM Response with missing or invalid `id`")
connection = SCIMProviderGroup.objects.create(
provider=self.provider, group=group, scim_id=scim_id
provider=self.provider, group=group, scim_id=scim_id, attributes=response
)
users = list(group.users.order_by("id").values_list("id", flat=True))
self._patch_add_users(connection, users)

View File

@ -77,21 +77,24 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
if len(users_res) < 1:
raise exc
return SCIMProviderUser.objects.create(
provider=self.provider, user=user, scim_id=users_res[0]["id"]
provider=self.provider,
user=user,
scim_id=users_res[0]["id"],
attributes=users_res[0],
)
else:
scim_id = response.get("id")
if not scim_id or scim_id == "":
raise StopSync("SCIM Response with missing or invalid `id`")
return SCIMProviderUser.objects.create(
provider=self.provider, user=user, scim_id=scim_id
provider=self.provider, user=user, scim_id=scim_id, attributes=response
)
def update(self, user: User, connection: SCIMProviderUser):
"""Update existing user"""
scim_user = self.to_schema(user, connection)
scim_user.id = connection.scim_id
self._request(
response = self._request(
"PUT",
f"/Users/{connection.scim_id}",
json=scim_user.model_dump(
@ -99,3 +102,5 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
exclude_unset=True,
),
)
connection.attributes = response
connection.save()

View File

@ -0,0 +1,23 @@
# Generated by Django 5.0.13 on 2025-03-18 13:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_scim", "0012_scimprovider_compatibility_mode"),
]
operations = [
migrations.AddField(
model_name="scimprovidergroup",
name="attributes",
field=models.JSONField(default=dict),
),
migrations.AddField(
model_name="scimprovideruser",
name="attributes",
field=models.JSONField(default=dict),
),
]

View File

@ -22,6 +22,7 @@ class SCIMProviderUser(SerializerModel):
scim_id = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)
@property
def serializer(self) -> type[Serializer]:
@ -43,6 +44,7 @@ class SCIMProviderGroup(SerializerModel):
scim_id = models.TextField()
group = models.ForeignKey(Group, on_delete=models.CASCADE)
provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)
@property
def serializer(self) -> type[Serializer]:

File diff suppressed because one or more lines are too long

View File

@ -142,38 +142,35 @@ class IdentificationChallengeResponse(ChallengeResponse):
raise ValidationError("Failed to authenticate.")
self.pre_user = pre_user
# Password check
if current_stage.password_stage:
password = attrs.get("password", None)
if not password:
self.stage.logger.warning("Password not set for ident+auth attempt")
try:
with start_span(
op="authentik.stages.identification.authenticate",
name="User authenticate call (combo stage)",
):
user = authenticate(
self.stage.request,
current_stage.password_stage.backends,
current_stage,
username=self.pre_user.username,
password=password,
)
if not user:
raise ValidationError("Failed to authenticate.")
self.pre_user = user
except PermissionDenied as exc:
raise ValidationError(str(exc)) from exc
# Captcha check
if captcha_stage := current_stage.captcha_stage:
captcha_token = attrs.get("captcha_token", None)
if not captcha_token:
self.stage.logger.warning("Token not set for captcha attempt")
verify_captcha_token(captcha_stage, captcha_token, client_ip)
# Password check
if not current_stage.password_stage:
# No password stage select, don't validate the password
return attrs
password = attrs.get("password", None)
if not password:
self.stage.logger.warning("Password not set for ident+auth attempt")
try:
with start_span(
op="authentik.stages.identification.authenticate",
name="User authenticate call (combo stage)",
):
user = authenticate(
self.stage.request,
current_stage.password_stage.backends,
current_stage,
username=self.pre_user.username,
password=password,
)
if not user:
raise ValidationError("Failed to authenticate.")
self.pre_user = user
except PermissionDenied as exc:
raise ValidationError(str(exc)) from exc
return attrs

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json",
"type": "object",
"title": "authentik 2025.2.1 Blueprint schema",
"title": "authentik 2025.2.2 Blueprint schema",
"required": [
"version",
"entries"
@ -6462,6 +6462,11 @@
"title": "NameID Property Mapping",
"description": "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be considered"
},
"authn_context_class_ref_mapping": {
"type": "integer",
"title": "AuthnContextClassRef Property Mapping",
"description": "Configure how the AuthnContextClassRef value will be created. When left empty, the AuthnContextClassRef will be set based on which authentication methods the user used to authenticate."
},
"digest_algorithm": {
"type": "string",
"enum": [
@ -13011,6 +13016,15 @@
"minLength": 1,
"title": "Branding favicon"
},
"branding_custom_css": {
"type": "string",
"title": "Branding custom css"
},
"branding_default_flow_background": {
"type": "string",
"minLength": 1,
"title": "Branding default flow background"
},
"flow_authentication": {
"type": "string",
"format": "uuid",
@ -14892,9 +14906,15 @@
"type": "string",
"title": "Webhook url"
},
"webhook_mapping": {
"webhook_mapping_body": {
"type": "integer",
"title": "Webhook mapping"
"title": "Webhook mapping body",
"description": "Customize the body of the request. Mapping should return data that is JSON-serializable."
},
"webhook_mapping_headers": {
"type": "integer",
"title": "Webhook mapping headers",
"description": "Configure additional headers to be sent. Mapping should return a dictionary of key-value pairs"
},
"send_once": {
"type": "boolean",

View File

@ -31,7 +31,7 @@ services:
volumes:
- redis:/data
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.2}
restart: unless-stopped
command: server
environment:
@ -54,7 +54,7 @@ services:
redis:
condition: service_healthy
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.2}
restart: unless-stopped
command: worker
environment:

6
go.mod
View File

@ -11,7 +11,7 @@ require (
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.10
github.com/go-openapi/runtime v0.28.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.2
github.com/gorilla/mux v1.8.1
@ -23,13 +23,13 @@ require (
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.8.0
github.com/prometheus/client_golang v1.21.1
github.com/redis/go-redis/v9 v9.7.1
github.com/redis/go-redis/v9 v9.7.3
github.com/sethvargo/go-envconfig v1.1.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2025021.4
goauthentik.io/api/v3 v3.2025022.5
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.28.0
golang.org/x/sync v0.12.0

12
go.sum
View File

@ -113,8 +113,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -248,8 +248,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.7.1 h1:4LhKRCIduqXqtvCUlaq9c8bdHOkICjDMrr1+Zb3osAc=
github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
@ -299,8 +299,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
goauthentik.io/api/v3 v3.2025021.4 h1:KFap2KW+8CwhOxjBkRnRB4flvuHEMw24+fZei9dOhzw=
goauthentik.io/api/v3 v3.2025021.4/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
goauthentik.io/api/v3 v3.2025022.5 h1:+yvpWu7BNvd+EXAbv6PCgjEv1UwEm9yYVIIc7d3fIlM=
goauthentik.io/api/v3 v3.2025022.5/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=

View File

@ -29,4 +29,4 @@ func UserAgent() string {
return fmt.Sprintf("authentik@%s", FullVersion())
}
const VERSION = "2025.2.1"
const VERSION = "2025.2.2"

View File

@ -8,7 +8,6 @@
<link rel="shortcut icon" type="image/png" href="/outpost.goauthentik.io/static/dist/assets/icons/icon.png">
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/patternfly.min.css">
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/authentik.css">
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/custom.css">
<link rel="prefetch" href="/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg" />
<style>
.pf-c-background-image::before {

View File

@ -62,12 +62,12 @@ function prepare_debug {
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends krb5-kdc krb5-user krb5-admin-server libkrb5-dev gcc
VIRTUAL_ENV=/ak-root/venv poetry install --no-ansi --no-interaction
VIRTUAL_ENV=/ak-root/.venv uv sync --frozen
touch /unittest.xml
chown authentik:authentik /unittest.xml
}
if [[ "$(python -m authentik.lib.config debugger 2> /dev/null)" == "True" ]]; then
if [[ "$(python -m authentik.lib.config debugger 2>/dev/null)" == "True" ]]; then
prepare_debug
fi

View File

@ -1,4 +1,4 @@
"""Wrapper for lifecycle/ak, to be installed by poetry"""
"""Wrapper for lifecycle/ak, to be installed by uv"""
from os import system, waitstatus_to_exitcode
from pathlib import Path

View File

@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1004.0",
"aws-cdk": "^2.1005.0",
"cross-env": "^7.0.3"
},
"engines": {
@ -17,9 +17,9 @@
}
},
"node_modules/aws-cdk": {
"version": "2.1004.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1004.0.tgz",
"integrity": "sha512-3E5ICmSc7ZCZCwLX7NY+HFmmdUYgRaL+67h/BDoDQmkhx9StC8wG4xgzHFY9k8WQS0+ib/MP28f2d9yzHtQLlQ==",
"version": "2.1005.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1005.0.tgz",
"integrity": "sha512-4ejfGGrGCEl0pg1xcqkxK0lpBEZqNI48wtrXhk6dYOFYPYMZtqn1kdla29ONN+eO2unewkNF4nLP1lPYhlf9Pg==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@ -10,7 +10,7 @@
"node": ">=20"
},
"devDependencies": {
"aws-cdk": "^2.1004.0",
"aws-cdk": "^2.1005.0",
"cross-env": "^7.0.3"
}
}

View File

@ -26,7 +26,7 @@ Parameters:
Description: authentik Docker image
AuthentikVersion:
Type: String
Default: 2025.2.1
Default: 2025.2.2
Description: authentik Docker image tag
AuthentikServerCPU:
Type: Number

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
"POT-Creation-Date: 2025-03-22 00:10+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -616,6 +616,18 @@ msgstr ""
msgid "Email"
msgstr ""
#: authentik/events/models.py
msgid ""
"Customize the body of the request. Mapping should return data that is JSON-"
"serializable."
msgstr ""
#: authentik/events/models.py
msgid ""
"Configure additional headers to be sent. Mapping should return a dictionary "
"of key-value pairs"
msgstr ""
#: authentik/events/models.py
msgid ""
"Only send notification once, for example when sending a webhook into a chat "
@ -1756,6 +1768,17 @@ msgid ""
"NameIDPolicy of the incoming request will be considered"
msgstr ""
#: authentik/providers/saml/models.py
msgid "AuthnContextClassRef Property Mapping"
msgstr ""
#: authentik/providers/saml/models.py
msgid ""
"Configure how the AuthnContextClassRef value will be created. When left "
"empty, the AuthnContextClassRef will be set based on which authentication "
"methods the user used to authenticate."
msgstr ""
#: authentik/providers/saml/models.py
msgid ""
"Assertion valid not before current time + this value (Format: hours=-1;"

View File

@ -19,7 +19,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
"POT-Creation-Date: 2025-03-20 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Marc Schmitt, 2025\n"
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
@ -1956,6 +1956,20 @@ msgstr ""
"Configure la manière dont la valeur NameID sera créée. Si laissé vide, la "
"NameIDPolicy de la requête entrante sera prise en compte"
#: authentik/providers/saml/models.py
msgid "AuthnContextClassRef Property Mapping"
msgstr "Mappage de propriété AuthnContextClassRef"
#: authentik/providers/saml/models.py
msgid ""
"Configure how the AuthnContextClassRef value will be created. When left "
"empty, the AuthnContextClassRef will be set based on which authentication "
"methods the user used to authenticate."
msgstr ""
"Configure comment la valeur AuthnContextClassRef sera créée. Lorsque non "
"sélectionné, AuthnContextClassRef sera défini en fonction de quelle méthode "
"d'authentification l'utilisateur a utilisé pour s'authentifier."
#: authentik/providers/saml/models.py
msgid ""
"Assertion valid not before current time + this value (Format: "

Binary file not shown.

View File

@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
"POT-Creation-Date: 2025-03-20 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: deluxghost, 2025\n"
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
@ -1782,6 +1782,18 @@ msgid ""
"NameIDPolicy of the incoming request will be considered"
msgstr "配置如何创建 NameID 值。如果留空,将考虑传入请求的 NameIDPolicy"
#: authentik/providers/saml/models.py
msgid "AuthnContextClassRef Property Mapping"
msgstr "AuthnContextClassRef 属性映射"
#: authentik/providers/saml/models.py
msgid ""
"Configure how the AuthnContextClassRef value will be created. When left "
"empty, the AuthnContextClassRef will be set based on which authentication "
"methods the user used to authenticate."
msgstr ""
"配置如何创建 AuthnContextClassRef 值。留空时AuthnContextClassRef 会基于用户使用的身份验证方式设置。"
#: authentik/providers/saml/models.py
msgid ""
"Assertion valid not before current time + this value (Format: "

Binary file not shown.

View File

@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
"POT-Creation-Date: 2025-03-20 00:10+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: deluxghost, 2025\n"
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
@ -1781,6 +1781,18 @@ msgid ""
"NameIDPolicy of the incoming request will be considered"
msgstr "配置如何创建 NameID 值。如果留空,将考虑传入请求的 NameIDPolicy"
#: authentik/providers/saml/models.py
msgid "AuthnContextClassRef Property Mapping"
msgstr "AuthnContextClassRef 属性映射"
#: authentik/providers/saml/models.py
msgid ""
"Configure how the AuthnContextClassRef value will be created. When left "
"empty, the AuthnContextClassRef will be set based on which authentication "
"methods the user used to authenticate."
msgstr ""
"配置如何创建 AuthnContextClassRef 值。留空时AuthnContextClassRef 会基于用户使用的身份验证方式设置。"
#: authentik/providers/saml/models.py
msgid ""
"Assertion valid not before current time + this value (Format: "

12
package-lock.json generated Normal file
View File

@ -0,0 +1,12 @@
{
"name": "@goauthentik/authentik",
"version": "2025.2.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/authentik",
"version": "2025.2.1"
}
}
}

View File

@ -1,5 +1,5 @@
{
"name": "@goauthentik/authentik",
"version": "2025.2.1",
"version": "2025.2.2",
"private": true
}

6120
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,116 @@
[tool.poetry]
[project]
name = "authentik"
version = "2025.2.1"
version = "2025.2.2"
description = ""
authors = ["authentik Team <hello@goauthentik.io>"]
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
requires-python = "==3.12.*"
dependencies = [
"argon2-cffi",
"celery",
"channels",
"channels-redis",
"cryptography",
"dacite",
"deepmerge",
"defusedxml",
"django",
"django-countries",
"django-cte",
"django-filter",
"django-guardian",
"django-model-utils",
"django-pglock",
"django-prometheus",
"django-redis",
"django-storages[s3]",
"django-tenants",
"djangorestframework ==3.14.0",
"djangorestframework-guardian",
"docker",
"drf-orjson-renderer",
"drf-spectacular",
"dumb-init",
"duo-client",
"fido2",
"flower",
"geoip2",
"geopy",
"google-api-python-client",
"gssapi",
"gunicorn",
"jsonpatch",
"jwcrypto",
"kubernetes",
"ldap3",
"lxml",
"msgraph-sdk",
"opencontainers",
"packaging",
"paramiko",
"psycopg[c]",
"pydantic",
"pydantic-scim",
"pyjwt",
"pyrad",
"python-kadmin-rs ==0.5.3",
"pyyaml",
"requests-oauthlib",
"scim2-filter-parser",
"sentry-sdk",
"service_identity",
"setproctitle",
"structlog",
"swagger-spec-validator",
"tenant-schemas-celery",
"twilio",
"ua-parser",
"unidecode",
"urllib3 <3",
"uvicorn[standard]",
"watchdog",
"webauthn",
"wsproto",
"xmlsec <= 1.3.14",
"zxcvbn",
]
[dependency-groups]
dev = [
"aws-cdk-lib",
"bandit",
"black",
"bump2version",
"channels[daphne]",
"codespell",
"colorama",
"constructs",
"coverage[toml]",
"debugpy",
"drf-jsonschema-serializer",
"freezegun",
"importlib-metadata",
"k5test",
"pdoc",
"pytest",
"pytest-django",
"pytest-github-actions-annotate-failures",
"pytest-randomly",
"pytest-timeout",
"requests-mock",
"ruff",
"selenium",
]
[tool.uv.sources]
django-tenants = { git = "https://github.com/rissson/django-tenants.git", branch = "authentik-fixes" }
opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "20d69d9cc50a0fef31605b46f06da0c94f1ec3cf" }
[project.scripts]
ak = "lifecycle.ak:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.bandit]
exclude_dirs = ["**/node_modules/**"]
@ -30,6 +138,7 @@ skip = [
]
dictionary = ".github/codespell-dictionary.txt,-"
ignore-words = ".github/codespell-words.txt"
[tool.black]
line-length = 100
target-version = ['py312']
@ -60,6 +169,7 @@ select = [
ignore = [
"DJ001", # Avoid using `null=True` on string-based fields,
]
[tool.ruff.lint.pylint]
max-args = 7
max-branches = 18
@ -107,109 +217,3 @@ filterwarnings = [
"ignore:defusedxml.lxml is no longer supported and will be removed in a future release.:DeprecationWarning",
"ignore:SelectableGroups dict interface is deprecated. Use select.:DeprecationWarning",
]
[tool.poetry.dependencies]
argon2-cffi = "*"
celery = "*"
channels = "*"
channels-redis = "*"
cryptography = "*"
dacite = "*"
deepmerge = "*"
defusedxml = "*"
django = "*"
django-countries = "*"
django-cte = "*"
django-filter = "*"
django-guardian = "*"
django-model-utils = "*"
django-pglock = "*"
django-prometheus = "*"
django-redis = "*"
django-storages = { extras = ["s3"], version = "*" }
# See https://github.com/django-tenants/django-tenants/pull/997
django-tenants = { git = "https://github.com/rissson/django-tenants.git", branch = "authentik-fixes" }
djangorestframework = "3.14.0"
djangorestframework-guardian = "*"
docker = "*"
drf-orjson-renderer = "*"
drf-spectacular = "*"
dumb-init = "*"
duo-client = "*"
fido2 = "*"
flower = "*"
geoip2 = "*"
geopy = "*"
google-api-python-client = "*"
gunicorn = "*"
gssapi = "*"
jsonpatch = "*"
jwcrypto = "*"
kubernetes = "*"
ldap3 = "*"
lxml = "*"
msgraph-sdk = "*"
opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "20d69d9cc50a0fef31605b46f06da0c94f1ec3cf", extras = [
"reggie",
] }
packaging = "*"
paramiko = "*"
psycopg = { extras = ["c"], version = "*" }
pydantic = "*"
pydantic-scim = "*"
pyjwt = "*"
pyrad = "*"
python = "~3.12"
python-kadmin-rs = "0.5.3"
pyyaml = "*"
requests-oauthlib = "*"
scim2-filter-parser = "*"
sentry-sdk = "*"
service_identity = "*"
setproctitle = "*"
structlog = "*"
swagger-spec-validator = "*"
tenant-schemas-celery = "*"
twilio = "*"
ua-parser = "*"
unidecode = "*"
# Pinned because of botocore https://github.com/orgs/python-poetry/discussions/7937
urllib3 = { extras = ["secure"], version = "<3" }
uvicorn = { extras = ["standard"], version = "*" }
watchdog = "*"
webauthn = "*"
wsproto = "*"
xmlsec = "*"
zxcvbn = "*"
[tool.poetry.group.dev.dependencies]
aws-cdk-lib = "*"
bandit = "*"
black = "*"
bump2version = "*"
channels = { version = "*", extras = ["daphne"] }
codespell = "*"
colorama = "*"
constructs = "*"
coverage = { extras = ["toml"], version = "*" }
debugpy = "*"
drf-jsonschema-serializer = "*"
freezegun = "*"
importlib-metadata = "*"
k5test = "*"
pdoc = "*"
pytest = "*"
pytest-django = "*"
pytest-github-actions-annotate-failures = "*"
pytest-randomly = "*"
pytest-timeout = "*"
requests-mock = "*"
ruff = "*"
selenium = "*"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
ak = "lifecycle.ak:main"

View File

@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2025.2.1
version: 2025.2.2
description: Making authentication simple.
contact:
email: hello@goauthentik.io
@ -4447,6 +4447,10 @@ paths:
schema:
type: string
format: uuid
- in: query
name: branding_default_flow_background
schema:
type: string
- in: query
name: branding_favicon
schema:
@ -22191,6 +22195,11 @@ paths:
schema:
type: string
format: uuid
- in: query
name: authn_context_class_ref_mapping
schema:
type: string
format: uuid
- in: query
name: authorization_flow
schema:
@ -25745,7 +25754,7 @@ paths:
description: ''
delete:
operationId: sources_all_destroy
description: Source Viewset
description: Prevent deletion of built-in sources
parameters:
- in: path
name: slug
@ -41140,6 +41149,10 @@ components:
type: string
branding_favicon:
type: string
branding_custom_css:
type: string
branding_default_flow_background:
type: string
flow_authentication:
type: string
format: uuid
@ -41199,6 +41212,11 @@ components:
branding_favicon:
type: string
minLength: 1
branding_custom_css:
type: string
branding_default_flow_background:
type: string
minLength: 1
flow_authentication:
type: string
format: uuid
@ -42091,6 +42109,8 @@ components:
type: string
branding_favicon:
type: string
branding_custom_css:
type: string
ui_footer_links:
type: array
items:
@ -42117,6 +42137,7 @@ components:
type: string
readOnly: true
required:
- branding_custom_css
- branding_favicon
- branding_logo
- branding_title
@ -46869,10 +46890,18 @@ components:
webhook_url:
type: string
format: uri
webhook_mapping:
webhook_mapping_body:
type: string
format: uuid
nullable: true
description: Customize the body of the request. Mapping should return data
that is JSON-serializable.
webhook_mapping_headers:
type: string
format: uuid
nullable: true
description: Configure additional headers to be sent. Mapping should return
a dictionary of key-value pairs
send_once:
type: boolean
description: Only send notification once, for example when sending a webhook
@ -46900,10 +46929,18 @@ components:
webhook_url:
type: string
format: uri
webhook_mapping:
webhook_mapping_body:
type: string
format: uuid
nullable: true
description: Customize the body of the request. Mapping should return data
that is JSON-serializable.
webhook_mapping_headers:
type: string
format: uuid
nullable: true
description: Configure additional headers to be sent. Mapping should return
a dictionary of key-value pairs
send_once:
type: boolean
description: Only send notification once, for example when sending a webhook
@ -50120,6 +50157,11 @@ components:
branding_favicon:
type: string
minLength: 1
branding_custom_css:
type: string
branding_default_flow_background:
type: string
minLength: 1
flow_authentication:
type: string
format: uuid
@ -51332,10 +51374,18 @@ components:
webhook_url:
type: string
format: uri
webhook_mapping:
webhook_mapping_body:
type: string
format: uuid
nullable: true
description: Customize the body of the request. Mapping should return data
that is JSON-serializable.
webhook_mapping_headers:
type: string
format: uuid
nullable: true
description: Configure additional headers to be sent. Mapping should return
a dictionary of key-value pairs
send_once:
type: boolean
description: Only send notification once, for example when sending a webhook
@ -52228,6 +52278,14 @@ components:
title: NameID Property Mapping
description: Configure how the NameID value will be created. When left empty,
the NameIDPolicy of the incoming request will be considered
authn_context_class_ref_mapping:
type: string
format: uuid
nullable: true
title: AuthnContextClassRef Property Mapping
description: Configure how the AuthnContextClassRef value will be created.
When left empty, the AuthnContextClassRef will be set based on which authentication
methods the user used to authenticate.
digest_algorithm:
$ref: '#/components/schemas/DigestAlgorithmEnum'
signature_algorithm:
@ -55183,6 +55241,14 @@ components:
title: NameID Property Mapping
description: Configure how the NameID value will be created. When left empty,
the NameIDPolicy of the incoming request will be considered
authn_context_class_ref_mapping:
type: string
format: uuid
nullable: true
title: AuthnContextClassRef Property Mapping
description: Configure how the AuthnContextClassRef value will be created.
When left empty, the AuthnContextClassRef will be set based on which authentication
methods the user used to authenticate.
digest_algorithm:
$ref: '#/components/schemas/DigestAlgorithmEnum'
signature_algorithm:
@ -55348,6 +55414,14 @@ components:
title: NameID Property Mapping
description: Configure how the NameID value will be created. When left empty,
the NameIDPolicy of the incoming request will be considered
authn_context_class_ref_mapping:
type: string
format: uuid
nullable: true
title: AuthnContextClassRef Property Mapping
description: Configure how the AuthnContextClassRef value will be created.
When left empty, the AuthnContextClassRef will be set based on which authentication
methods the user used to authenticate.
digest_algorithm:
$ref: '#/components/schemas/DigestAlgorithmEnum'
signature_algorithm:
@ -55897,7 +55971,10 @@ components:
readOnly: true
provider:
type: integer
attributes:
readOnly: true
required:
- attributes
- group
- group_obj
- id
@ -55984,7 +56061,10 @@ components:
readOnly: true
provider:
type: integer
attributes:
readOnly: true
required:
- attributes
- id
- provider
- scim_id

View File

@ -7,6 +7,8 @@ services:
environment:
POSTGRES_HOST_AUTH_METHOD: trust
POSTGRES_DB: authentik
command:
["postgres", "-c", "log_statement=all", "-c", "log_destination=stderr"]
ports:
- 127.0.0.1:5432:5432
restart: always

View File

@ -47,10 +47,12 @@ class TestProviderOAuth2Github(SeleniumTestCase):
"GF_AUTH_GITHUB_AUTH_URL": self.url(
"authentik_providers_oauth2_root:github-authorize"
),
"GF_AUTH_GITHUB_TOKEN_URL": self.url(
"GF_AUTH_GITHUB_TOKEN_URL": self.host_url(
"authentik_providers_oauth2_root:github-access-token"
),
"GF_AUTH_GITHUB_API_URL": self.url("authentik_providers_oauth2_root:github-user"),
"GF_AUTH_GITHUB_API_URL": self.host_url(
"authentik_providers_oauth2_root:github-user"
),
"GF_LOG_LEVEL": "debug",
},
)

View File

@ -53,8 +53,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
"GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET": self.client_secret,
"GF_AUTH_GENERIC_OAUTH_SCOPES": "openid email profile",
"GF_AUTH_GENERIC_OAUTH_AUTH_URL": self.url("authentik_providers_oauth2:authorize"),
"GF_AUTH_GENERIC_OAUTH_TOKEN_URL": self.url("authentik_providers_oauth2:token"),
"GF_AUTH_GENERIC_OAUTH_API_URL": self.url("authentik_providers_oauth2:userinfo"),
"GF_AUTH_GENERIC_OAUTH_TOKEN_URL": self.host_url(
"authentik_providers_oauth2:token"
),
"GF_AUTH_GENERIC_OAUTH_API_URL": self.host_url(
"authentik_providers_oauth2:userinfo"
),
"GF_AUTH_SIGNOUT_REDIRECT_URL": self.url(
"authentik_providers_oauth2:end-session",
application_slug=self.app_slug,

View File

@ -42,7 +42,9 @@ class TestSourceSCIM(SeleniumTestCase):
test_launch = session.post(
"http://localhost:8080/test/run",
data={
"endPoint": self.live_server_url + f"/source/scim/{source.slug}/v2",
"endPoint": self.host_url(
"authentik_sources_scim:v2-root", source_slug=source.slug
),
"username": "foo",
"password": source.token.key,
"jwtToken": None,

View File

@ -2,7 +2,6 @@
import json
import os
import socket
from collections.abc import Callable
from functools import lru_cache, wraps
from os import environ
@ -36,8 +35,8 @@ from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.generators import generate_id
RETRIES = int(environ.get("RETRIES", "3"))
IS_CI = "CI" in environ
RETRIES = int(environ.get("RETRIES", "3")) if IS_CI else 1
def get_docker_tag() -> str:
@ -51,13 +50,6 @@ def get_docker_tag() -> str:
return f"gh-{branch_name}"
def get_local_ip() -> str:
"""Get the local machine's IP"""
hostname = socket.gethostname()
ip_addr = socket.gethostbyname(hostname)
return ip_addr
class DockerTestCase(TestCase):
"""Mixin for dealing with containers"""
@ -113,6 +105,9 @@ class DockerTestCase(TestCase):
specs["network"] = self.__network.name
specs["labels"] = self.docker_labels
specs["detach"] = True
specs["extra_hosts"] = {
"host.docker.internal": "host-gateway",
}
if hasattr(self, "live_server_url"):
specs.setdefault("environment", {})
specs["environment"]["AUTHENTIK_HOST"] = self.live_server_url
@ -155,7 +150,7 @@ class DockerTestCase(TestCase):
class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
"""StaticLiveServerTestCase which automatically creates a Webdriver instance"""
host = get_local_ip()
host = "0.0.0.0" # nosec Required for containers to reach us directly on the host
wait_timeout: int
user: User
@ -210,6 +205,15 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
f"URL {self.driver.current_url} doesn't match expected URL {desired_url}",
)
def host_url(self, view, query: dict | None = None, **kwargs) -> str:
"""reverse `view` with `**kwargs` into full URL using live_server_url"""
url = f"http://host.docker.internal:{self.server_thread.port}" + reverse(
view, kwargs=kwargs
)
if query:
return url + "?" + urlencode(query)
return url
def url(self, view, query: dict | None = None, **kwargs) -> str:
"""reverse `view` with `**kwargs` into full URL using live_server_url"""
url = self.live_server_url + reverse(view, kwargs=kwargs)

3418
uv.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -19,5 +19,5 @@
"importOrder": ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true,
"importOrderParserPlugins": ["typescript", "classProperties", "decorators-legacy"]
"importOrderParserPlugins": ["typescript", "jsx", "classProperties", "decorators-legacy"]
}

View File

@ -13,6 +13,7 @@ const importInlinePatterns = [
'import AKGlobal from "@goauthentik/common/styles/authentik\\.css',
'import PF.+ from "@patternfly/patternfly/\\S+\\.css',
'import ThemeDark from "@goauthentik/common/styles/theme-dark\\.css',
'import OneDark from "@goauthentik/common/styles/one-dark\\.css',
'import styles from "\\./LibraryPageImpl\\.css',
];
@ -39,6 +40,10 @@ const config: StorybookConfig = {
from: "../src/common/styles/theme-dark.css",
to: "@goauthentik/common/styles/theme-dark.css",
},
{
from: "../src/common/styles/one-dark.css",
to: "@goauthentik/common/styles/one-dark.css",
},
],
framework: {
name: "@storybook/web-components-vite",

View File

@ -71,7 +71,7 @@ export default [
...globals.node,
},
},
files: ["scripts/*.mjs", "*.ts", "*.mjs"],
files: ["scripts/**/*.mjs", "*.ts", "*.mjs"],
rules: {
"no-unused-vars": "off",
// We WANT our scripts to output to the console!

2772
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
"name": "@goauthentik/web",
"version": "0.0.0",
"dependencies": {
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-python": "^6.1.6",
@ -11,12 +12,13 @@
"@floating-ui/dom": "^1.6.11",
"@formatjs/intl-listformat": "^7.5.7",
"@fortawesome/fontawesome-free": "^6.6.0",
"@goauthentik/api": "^2025.2.1-1741798605",
"@goauthentik/api": "^2025.2.2-1742585853",
"@lit-labs/ssr": "^3.2.2",
"@lit/context": "^1.1.2",
"@lit/localize": "^0.12.2",
"@lit/reactive-element": "^2.0.4",
"@lit/task": "^1.0.1",
"@mdx-js/mdx": "^3.1.0",
"@open-wc/lit-helpers": "^0.7.0",
"@patternfly/elements": "^4.0.2",
"@patternfly/patternfly": "^4.224.2",
@ -24,6 +26,7 @@
"@spotlightjs/spotlight": "^2.4.2",
"@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1",
"change-case": "^5.4.4",
"chart.js": "^4.4.4",
"chartjs-adapter-date-fns": "^3.0.0",
"codemirror": "^6.0.1",
@ -31,16 +34,28 @@
"core-js": "^3.38.1",
"country-flag-icons": "^1.5.13",
"date-fns": "^4.1.0",
"deepmerge-ts": "^7.1.5",
"dompurify": "^3.2.4",
"fuse.js": "^7.0.0",
"guacamole-common-js": "^1.5.0",
"hastscript": "^9.0.1",
"lit": "^3.2.0",
"md-front-matter": "^1.0.4",
"mermaid": "^11.4.1",
"rapidoc": "^9.3.7",
"showdown": "^2.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"rehype-highlight": "^7.0.2",
"rehype-mermaid": "^3.0.0",
"rehype-parse": "^9.0.1",
"rehype-stringify": "^10.0.1",
"remark-directive": "^4.0.0",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.1",
"remark-mdx-frontmatter": "^5.0.0",
"style-mod": "^4.1.2",
"ts-pattern": "^5.4.0",
"unist-util-visit": "^5.0.0",
"webcomponent-qr-code": "^1.2.0",
"yaml": "^2.5.1"
},
@ -66,15 +81,17 @@
"@types/guacamole-common-js": "^1.5.2",
"@types/mocha": "^10.0.8",
"@types/node": "^22.7.4",
"@types/showdown": "^2.0.6",
"@types/react": "^18.3.13",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^8.8.0",
"@typescript-eslint/parser": "^8.8.0",
"@wdio/browser-runner": "9.4",
"@wdio/cli": "9.4",
"@wdio/spec-reporter": "^9.1.2",
"chokidar": "^4.0.1",
"chromedriver": "^131.0.1",
"esbuild": "^0.25.0",
"esbuild-plugin-polyfill-node": "^0.3.0",
"esbuild-plugins-node-modules-polyfill": "^1.7.0",
"eslint": "^9.11.1",
"eslint-plugin-lit": "^1.15.0",
"eslint-plugin-wc": "^2.1.1",
@ -118,7 +135,7 @@
"build-locales:build": "wireit",
"build-proxy": "wireit",
"build:sfe": "wireit",
"esbuild:watch": "node build.mjs --watch",
"esbuild:watch": "node scripts/build-web.mjs --watch",
"extract-locales": "wireit",
"format": "wireit",
"lint": "wireit",
@ -153,7 +170,7 @@
"instead of `npm run watch`. The former is more comprehensive, but ",
"the latter is faster."
],
"command": "${NODE_RUNNER} build.mjs",
"command": "${NODE_RUNNER} scripts/build-web.mjs",
"files": [
"src/**/*.{css,jpg,png,ts,js,json}",
"!src/**/*.stories.ts",
@ -171,8 +188,8 @@
"./dist/enterprise/**",
"./dist/poly-*.js",
"./dist/poly-*.js.map",
"./dist/custom.css",
"./dist/theme-dark.css",
"./dist/one-dark.css",
"./dist/patternfly.min.css"
],
"dependencies": [
@ -195,7 +212,7 @@
]
},
"build-proxy": {
"command": "node build.mjs --proxy",
"command": "node scripts/build-web.mjs --proxy",
"dependencies": [
"build-locales"
]

View File

@ -1,17 +1,20 @@
import { execFileSync } from "child_process";
import { deepmerge } from "deepmerge-ts";
import esbuild from "esbuild";
import { polyfillNode } from "esbuild-plugin-polyfill-node";
import findFreePorts from "find-free-ports";
import { copyFileSync, mkdirSync, readFileSync, statSync } from "fs";
import { globSync } from "glob";
import path from "path";
import * as path from "path";
import { cwd } from "process";
import process from "process";
import { fileURLToPath } from "url";
import { buildObserverPlugin } from "./build-observer-plugin.mjs";
import { mdxPlugin } from "./esbuild/build-mdx-plugin.mjs";
import { buildObserverPlugin } from "./esbuild/build-observer-plugin.mjs";
const __dirname = fileURLToPath(new URL(".", import.meta.url));
let authentikProjectRoot = __dirname + "../";
let authentikProjectRoot = path.join(__dirname, "..", "..");
try {
// Use the package.json file in the root folder, as it has the current version information.
@ -51,7 +54,6 @@ const definitions = Object.fromEntries(
const assetsFileMappings = [
["node_modules/@patternfly/patternfly/patternfly.min.css", "."],
["node_modules/@patternfly/patternfly/assets/**", ".", "node_modules/@patternfly/patternfly/"],
["src/custom.css", "."],
["src/common/styles/**", "."],
["src/assets/images/**", "./assets/images"],
["./icons/*", "./assets/icons"],
@ -108,7 +110,7 @@ const entryPoints = [
];
/**
* @satisfies {import("esbuild").BuildOptions}
* @type {import("esbuild").BuildOptions}
*/
const BASE_ESBUILD_OPTIONS = {
bundle: true,
@ -121,12 +123,19 @@ const BASE_ESBUILD_OPTIONS = {
tsconfig: "./tsconfig.json",
loader: {
".css": "text",
".md": "text",
".mdx": "text",
},
plugins: [
polyfillNode({
polyfills: {
path: true,
},
}),
mdxPlugin({
root: authentikProjectRoot,
}),
],
define: definitions,
format: "esm",
plugins: [],
logOverride: {
/**
* HACK: Silences issue originating in ESBuild.
@ -161,24 +170,35 @@ function composeVersionID() {
* @throws {Error} on build failure
*/
function createEntryPointOptions([source, dest], overrides = {}) {
const outdir = path.join(__dirname, "./dist", dest);
const outdir = path.join(__dirname, "..", "dist", dest);
return {
...BASE_ESBUILD_OPTIONS,
/**
* @type {esbuild.BuildOptions}
*/
const entryPointConfig = {
entryPoints: [`./src/${source}`],
entryNames: `[dir]/[name]-${composeVersionID()}`,
publicPath: path.join("/static", "dist", dest),
outdir,
...overrides,
};
/**
* @type {esbuild.BuildOptions}
*/
const mergedConfig = deepmerge(BASE_ESBUILD_OPTIONS, entryPointConfig, overrides);
return mergedConfig;
}
/**
* Build all entry points in parallel.
*
* @param {EntryPoint[]} entryPoints
* @returns {Promise<esbuild.BuildResult[]>}
*/
async function buildParallel(entryPoints) {
await Promise.allSettled(
return Promise.all(
entryPoints.map((entryPoint) => {
return esbuild.build(createEntryPointOptions(entryPoint));
}),
@ -210,11 +230,10 @@ async function doWatch() {
return esbuild.context(
createEntryPointOptions(entryPoint, {
plugins: [
...BASE_ESBUILD_OPTIONS.plugins,
buildObserverPlugin({
serverURL,
logPrefix: entryPoint[1],
relativeRoot: __dirname,
relativeRoot: path.join(__dirname, ".."),
}),
],
define: {

View File

@ -0,0 +1,81 @@
/**
* @import {
OnLoadArgs,
OnLoadResult,
Plugin,
PluginBuild
* } from 'esbuild'
*/
import * as fs from "node:fs/promises";
import * as path from "node:path";
/**
* @typedef {Omit<OnLoadArgs, 'pluginData'> & LoadDataFields} LoadData
* Data passed to `onload`.
*
* @typedef LoadDataFields
* Extra fields given in `data` to `onload`.
* @property {PluginData | null | undefined} [pluginData]
* Plugin data.
*
*
* @typedef PluginData
* Extra data passed.
* @property {Buffer | string | null | undefined} [contents]
* File contents.
*/
const name = "mdx-plugin";
/**
* @typedef MDXPluginOptions
*
* @property {string} root Root directory.
*/
/**
* Bundle MDX into JSON modules.
*
* @param {MDXPluginOptions} options Options.
* @returns {Plugin} Plugin.
*/
export function mdxPlugin({ root }) {
return { name, setup };
/**
* @param {PluginBuild} build
* Build.
* @returns {undefined}
* Nothing.
*/
function setup(build) {
build.onLoad({ filter: /\.mdx?$/ }, onload);
/**
* @param {LoadData} data
* Data.
* @returns {Promise<OnLoadResult>}
* Result.
*/
async function onload(data) {
const content = String(
data.pluginData &&
data.pluginData.contents !== null &&
data.pluginData.contents !== undefined
? data.pluginData.contents
: await fs.readFile(data.path),
);
const publicPath = path.resolve(
"/",
path.relative(path.join(root, "website"), data.path),
);
const publicDirectory = path.dirname(publicPath);
return {
contents: JSON.stringify({ content, publicPath, publicDirectory }),
loader: "file",
};
}
}
}

View File

@ -8,7 +8,7 @@ import path from "path";
* @returns {string}
*/
export function serializeCustomEventToStream(event) {
// @ts-ignore
// @ts-expect-error - TS doesn't know about the detail property
const data = event.detail ?? {};
const eventContent = [`event: ${event.type}`, `data: ${JSON.stringify(data)}`];

View File

@ -5,7 +5,6 @@ import "@goauthentik/components/events/ObjectChangelog";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/PageHeader";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/ModalButton";

View File

@ -3,7 +3,7 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import MDApplication from "@goauthentik/docs/add-secure-apps/applications/index.md";
import "@goauthentik/elements/AppIcon.js";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/ak-mdx";
import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/forms/DeleteBulkForm";
import "@goauthentik/elements/forms/ModalForm";
@ -89,7 +89,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
return html`<div class="pf-c-sidebar__panel pf-m-width-25">
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-markdown .md=${MDApplication} meta="applications/index.md"></ak-markdown>
<ak-mdx .url=${MDApplication}></ak-mdx>
</div>
</div>
</div>`;

View File

@ -94,7 +94,7 @@ export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
}
renderExpanded(item: ApplicationEntitlement): TemplateResult {
return html` <td></td>
return html`<td></td>
<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<div class="pf-c-content">

View File

@ -51,11 +51,7 @@ export class BrandForm extends ModelForm<Brand, string> {
}
renderForm(): TemplateResult {
return html` <ak-form-element-horizontal
label=${msg("Domain")}
?required=${true}
name="domain"
>
return html` <ak-form-element-horizontal label=${msg("Domain")} required name="domain">
<input
type="text"
value="${first(this.instance?.domain, window.location.host)}"
@ -90,14 +86,10 @@ export class BrandForm extends ModelForm<Brand, string> {
</p>
</ak-form-element-horizontal>
<ak-form-group .expanded=${true}>
<ak-form-group>
<span slot="header"> ${msg("Branding settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal
label=${msg("Title")}
?required=${true}
name="brandingTitle"
>
<ak-form-element-horizontal label=${msg("Title")} required name="brandingTitle">
<input
type="text"
value="${first(
@ -111,11 +103,7 @@ export class BrandForm extends ModelForm<Brand, string> {
${msg("Branding shown in page title and several other places.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Logo")}
?required=${true}
name="brandingLogo"
>
<ak-form-element-horizontal label=${msg("Logo")} required name="brandingLogo">
<input
type="text"
value="${first(this.instance?.brandingLogo, DefaultBrand.brandingLogo)}"
@ -130,7 +118,7 @@ export class BrandForm extends ModelForm<Brand, string> {
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Favicon")}
?required=${true}
required
name="brandingFavicon"
>
<input
@ -148,6 +136,45 @@ export class BrandForm extends ModelForm<Brand, string> {
${msg("Icon shown in the browser tab.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Default flow background")}
?required=${true}
name="brandingDefaultFlowBackground"
>
<input
type="text"
value="${first(
this.instance?.brandingDefaultFlowBackground,
"/static/dist/assets/images/flow_background.jpg",
)}"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">
${msg(
"Default background used during flow execution. Can be overridden per flow.",
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Custom CSS")}
required
name="brandingCustomCss"
>
<ak-codemirror
mode=${CodeMirrorMode.CSS}
value="${first(
this.instance?.brandingCustomCss,
DefaultBrand.brandingCustomCss,
)}"
>
</ak-codemirror>
<p class="pf-c-form__helper-text">
${msg("Custom CSS to apply to pages when this brand is active.")}
</p>
</ak-form-element-horizontal>
</div>
</ak-form-group>

View File

@ -66,7 +66,7 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
}
renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
<input
type="text"
value="${ifDefined(this.instance?.name)}"
@ -74,7 +74,7 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Mode")} ?required=${true} name="mode">
<ak-form-element-horizontal label=${msg("Mode")} required name="mode">
<ak-radio
@change=${(ev: CustomEvent<{ value: NotificationTransportModeEnum }>) => {
this.onModeChange(ev.detail.value);
@ -106,7 +106,7 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
?hidden=${!this.showWebhook}
label=${msg("Webhook URL")}
name="webhookUrl"
?required=${true}
required
>
<input
type="text"
@ -116,8 +116,8 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
</ak-form-element-horizontal>
<ak-form-element-horizontal
?hidden=${!this.showWebhook}
label=${msg("Webhook Mapping")}
name="webhookMapping"
label=${msg("Webhook Body Mapping")}
name="webhookMappingBody"
>
<ak-search-select
.fetchObjects=${async (
@ -141,9 +141,42 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
return item?.pk;
}}
.selected=${(item: NotificationWebhookMapping): boolean => {
return this.instance?.webhookMapping === item.pk;
return this.instance?.webhookMappingBody === item.pk;
}}
?blankable=${true}
blankable
>
</ak-search-select>
</ak-form-element-horizontal>
<ak-form-element-horizontal
?hidden=${!this.showWebhook}
label=${msg("Webhook Header Mapping")}
name="webhookMappingHeaders"
>
<ak-search-select
.fetchObjects=${async (
query?: string,
): Promise<NotificationWebhookMapping[]> => {
const args: PropertymappingsNotificationListRequest = {
ordering: "name",
};
if (query !== undefined) {
args.search = query;
}
const items = await new PropertymappingsApi(
DEFAULT_CONFIG,
).propertymappingsNotificationList(args);
return items.results;
}}
.renderElement=${(item: NotificationWebhookMapping): string => {
return item.name;
}}
.value=${(item: NotificationWebhookMapping | undefined): string | undefined => {
return item?.pk;
}}
.selected=${(item: NotificationWebhookMapping): boolean => {
return this.instance?.webhookMappingHeaders === item.pk;
}}
blankable
>
</ak-search-select>
</ak-form-element-horizontal>

View File

@ -7,11 +7,10 @@ import { EVENT_REFRESH } from "@goauthentik/common/constants";
import "@goauthentik/components/ak-status-label";
import "@goauthentik/components/events/ObjectChangelog";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/SyncStatusCard";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/sync/SyncStatusCard";
import { msg } from "@lit/localize";
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";

View File

@ -6,7 +6,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import "@goauthentik/components/events/ObjectChangelog";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/buttons/ModalButton";

View File

@ -8,8 +8,8 @@ import MDProviderOAuth2 from "@goauthentik/docs/add-secure-apps/providers/oauth2
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/ak-mdx";
import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/buttons/SpinnerButton";
@ -221,7 +221,7 @@ export class OAuth2ProviderViewPage extends AKElement {
>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
<div class="pf-c-description-list__text pf-m-monospace">
${this.provider.clientId}
</div>
</dd>
@ -236,7 +236,9 @@ export class OAuth2ProviderViewPage extends AKElement {
<div class="pf-c-description-list__text">
<ul>
${this.provider.redirectUris.map((ru) => {
return html`<li>${ru.matchingMode}: ${ru.url}</li>`;
return html`<li class="pf-m-monospace">
${ru.matchingMode}: ${ru.url}
</li>`;
})}
</ul>
</div>
@ -355,21 +357,20 @@ export class OAuth2ProviderViewPage extends AKElement {
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-12-col-on-xl pf-m-12-col-on-2xl"
>
<div class="pf-c-card__body">
<ak-markdown
<ak-mdx
.url=${MDProviderOAuth2}
.replacers=${[
(input: string) => {
if (!this.provider) {
return input;
}
return input.replaceAll(
"&lt;application slug&gt;",
"<application slug>",
this.provider.assignedApplicationSlug,
);
},
]}
.md=${MDProviderOAuth2}
meta="providers/oauth2/index.md"
></ak-markdown>
></ak-mdx>
</div>
</div>
</div>`;

View File

@ -16,10 +16,9 @@ import MDTraefikStandalone from "@goauthentik/docs/add-secure-apps/providers/pro
import MDHeaderAuthentication from "@goauthentik/docs/add-secure-apps/providers/proxy/header_authentication.mdx";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/Markdown";
import { Replacer } from "@goauthentik/elements/Markdown";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/ak-mdx";
import type { Replacer } from "@goauthentik/elements/ak-mdx";
import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/buttons/SpinnerButton";
import { getURLParam } from "@goauthentik/elements/router/RouteMatch";
@ -127,37 +126,30 @@ export class ProxyProviderViewPage extends AKElement {
{
label: msg("Nginx (Ingress)"),
md: MDNginxIngress,
meta: "providers/proxy/_nginx_ingress.md",
},
{
label: msg("Nginx (Proxy Manager)"),
md: MDNginxPM,
meta: "providers/proxy/_nginx_proxy_manager.md",
},
{
label: msg("Nginx (standalone)"),
md: MDNginxStandalone,
meta: "providers/proxy/_nginx_standalone.md",
},
{
label: msg("Traefik (Ingress)"),
md: MDTraefikIngress,
meta: "providers/proxy/_traefik_ingress.md",
},
{
label: msg("Traefik (Compose)"),
md: MDTraefikCompose,
meta: "providers/proxy/_traefik_compose.md",
},
{
label: msg("Traefik (Standalone)"),
md: MDTraefikStandalone,
meta: "providers/proxy/_traefik_standalone.md",
},
{
label: msg("Caddy (Standalone)"),
md: MDCaddyStandalone,
meta: "providers/proxy/_caddy_standalone.md",
},
];
const replacers: Replacer[] = [
@ -195,11 +187,7 @@ export class ProxyProviderViewPage extends AKElement {
data-tab-title="${server.label}"
class="pf-c-page__main-section pf-m-no-padding-mobile ak-markdown-section"
>
<ak-markdown
.replacers=${replacers}
.md=${server.md}
meta=${server.meta}
></ak-markdown>
<ak-mdx .url=${server.md} .replacers=${replacers}></ak-mdx>
</section>`;
})}</ak-tabs
>`;
@ -265,10 +253,7 @@ export class ProxyProviderViewPage extends AKElement {
</div>
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
<div class="pf-c-card__body">
<ak-markdown
.md=${MDHeaderAuthentication}
meta="proxy/header_authentication.md"
></ak-markdown>
<ak-mdx .url=${MDHeaderAuthentication}></ak-mdx>
</div>
</div>
</div>`;

View File

@ -245,6 +245,41 @@ export function renderForm(
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("AuthnContextClassRef Property Mapping")}
name="authnContextClassRefMapping"
>
<ak-search-select
.fetchObjects=${async (query?: string): Promise<SAMLPropertyMapping[]> => {
const args: PropertymappingsProviderSamlListRequest = {
ordering: "saml_name",
};
if (query !== undefined) {
args.search = query;
}
const items = await new PropertymappingsApi(
DEFAULT_CONFIG,
).propertymappingsProviderSamlList(args);
return items.results;
}}
.renderElement=${(item: SAMLPropertyMapping): string => {
return item.name;
}}
.value=${(item: SAMLPropertyMapping | undefined): string | undefined => {
return item?.pk;
}}
.selected=${(item: SAMLPropertyMapping): boolean => {
return provider?.authnContextClassRefMapping === item.pk;
}}
?blankable=${true}
>
</ak-search-select>
<p class="pf-c-form__helper-text">
${msg(
"Configure how the AuthnContextClassRef value will be created. When left empty, the AuthnContextClassRef will be set based on which authentication methods the user used to authenticate.",
)}
</p>
</ak-form-element-horizontal>
<ak-text-input
name="assertionValidNotBefore"

View File

@ -24,6 +24,7 @@ export class SCIMProviderGroupList extends Table<SCIMProviderGroup> {
return true;
}
expandable = true;
checkbox = true;
clearOnRefresh = true;
@ -81,6 +82,13 @@ export class SCIMProviderGroupList extends Table<SCIMProviderGroup> {
html`${item.id}`,
];
}
renderExpanded(item: SCIMProviderGroup): TemplateResult {
return html`<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<pre>${JSON.stringify(item.attributes, null, 4)}</pre>
</div>
</td>`;
}
}
declare global {

View File

@ -24,6 +24,7 @@ export class SCIMProviderUserList extends Table<SCIMProviderUser> {
return true;
}
expandable = true;
checkbox = true;
clearOnRefresh = true;
@ -82,6 +83,13 @@ export class SCIMProviderUserList extends Table<SCIMProviderUser> {
html`${item.id}`,
];
}
renderExpanded(item: SCIMProviderUser): TemplateResult {
return html`<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<pre>${JSON.stringify(item.attributes, null, 4)}</pre>
</div>
</td>`;
}
}
declare global {

View File

@ -9,11 +9,11 @@ import "@goauthentik/components/ak-status-label";
import "@goauthentik/components/events/ObjectChangelog";
import MDSCIMProvider from "@goauthentik/docs/add-secure-apps/providers/scim/index.md";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/SyncStatusCard";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/ak-mdx";
import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/sync/SyncStatusCard";
import { msg } from "@lit/localize";
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
@ -243,10 +243,7 @@ export class SCIMProviderViewPage extends AKElement {
</div>
<div class="pf-c-card pf-l-grid__item pf-m-5-col">
<div class="pf-c-card__body">
<ak-markdown
.md=${MDSCIMProvider}
meta="providers/scim/index.md"
></ak-markdown>
<ak-mdx .url=${MDSCIMProvider}></ak-mdx>
</div>
</div>
</div>`;

View File

@ -62,7 +62,11 @@ export class SSFProviderFormPage extends BaseProviderForm<SSFProvider> {
<ak-form-group expanded>
<span slot="header"> ${msg("Protocol settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal label=${msg("Signing Key")} name="signingKey">
<ak-form-element-horizontal
label=${msg("Signing Key")}
name="signingKey"
required
>
<!-- NOTE: 'null' cast to 'undefined' on signingKey to satisfy Lit requirements -->
<ak-crypto-certificate-search
certificate=${ifDefined(provider?.signingKey ?? undefined)}

View File

@ -7,7 +7,6 @@ import "@goauthentik/components/events/ObjectChangelog";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/buttons/SpinnerButton";
@ -137,10 +136,13 @@ export class SSFProviderViewPage extends AKElement {
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
<input
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
readonly
type="text"
value=${this.provider.ssfUrl || ""}
placeholder=${this.provider.ssfUrl
? msg("SSF URL")
: msg("No assigned application")}
/>
</div>
</dd>

View File

@ -7,12 +7,12 @@ import "@goauthentik/components/events/ObjectChangelog";
import MDSourceKerberosBrowser from "@goauthentik/docs/users-sources/sources/protocols/kerberos/browser.md";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/SyncStatusCard";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/ak-mdx";
import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/forms/ModalForm";
import "@goauthentik/elements/sync/SyncStatusCard";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
@ -186,11 +186,7 @@ export class KerberosSourceViewPage extends AKElement {
${this.renderSyncCards()}
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
<div class="pf-c-card__body">
<ak-markdown
.md=${MDSourceKerberosBrowser}
meta="users-sources/protocols/kerberos/browser.md"
;
></ak-markdown>
<ak-mdx .url=${MDSourceKerberosBrowser}></ak-mdx>
</div>
</div>
</div>

View File

@ -6,11 +6,11 @@ import { EVENT_REFRESH } from "@goauthentik/common/constants";
import "@goauthentik/components/events/ObjectChangelog";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/SyncStatusCard";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/forms/ModalForm";
import "@goauthentik/elements/sync/SyncStatusCard";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";

View File

@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2025.2.1";
export const VERSION = "2025.2.2";
export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";";

View File

@ -18,6 +18,7 @@ export const TAG_SENTRY_CAPABILITIES = "authentik.capabilities";
export async function configureSentry(canDoPpi = false): Promise<Config> {
const cfg = await config();
if (cfg.errorReporting.enabled) {
init({
dsn: cfg.errorReporting.sentryDsn,

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