Compare commits

..

36 Commits

Author SHA1 Message Date
faf8bf591f providers/saml: configuration for default NameID Policy
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-18 11:39:28 +02:00
52115f9345 core, web: update translations (#15102)
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-06-18 11:15:00 +02:00
b476551f13 web: bump @sentry/browser from 9.29.0 to 9.30.0 in /web in the sentry group across 1 directory (#15104)
web: bump @sentry/browser in /web in the sentry group across 1 directory

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


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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-18 11:14:24 +02:00
f9563c25cd core: bump github.com/jellydator/ttlcache/v3 from 3.3.0 to 3.4.0 (#15105)
Bumps [github.com/jellydator/ttlcache/v3](https://github.com/jellydator/ttlcache) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/jellydator/ttlcache/releases)
- [Commits](https://github.com/jellydator/ttlcache/compare/v3.3.0...v3.4.0)

---
updated-dependencies:
- dependency-name: github.com/jellydator/ttlcache/v3
  dependency-version: 3.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-18 11:14:15 +02:00
0067e6e155 website: bump @types/lodash from 4.17.17 to 4.17.18 in /website (#15106)
Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.17.17 to 4.17.18.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash)

---
updated-dependencies:
- dependency-name: "@types/lodash"
  dependency-version: 4.17.18
  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-06-18 11:14:08 +02:00
ce183929d4 website/integrations: zipline: add offline_access scope (#15099)
Closes https://github.com/goauthentik/authentik/issues/15098

Signed-off-by: Dominic R <dominic@sdko.org>
2025-06-17 16:15:07 -05:00
2fdf345271 website/docs: Add steps to troubleshoot /initial-setup/ (#15011)
* Add steps to troubleshoot /initial-setup/

* fix linting

* Apply suggestions from code review

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* Apply suggestions from code review

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* add email part

* Apply suggestions from code review

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* Apply suggestions from code review

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* change wording

---------

Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-06-17 08:40:14 -05:00
bbcf8418b4 core, web: update translations (#15084)
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-06-17 13:50:05 +02:00
dc57be46f4 website: bump the eslint group in /website with 3 updates (#15085)
Bumps the eslint group in /website with 3 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


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

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-17 13:31:27 +02:00
d68b3ba516 website: bump @types/node from 24.0.1 to 24.0.3 in /website (#15086)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.0.1 to 24.0.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.0.3
  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-06-17 13:31:20 +02:00
a9c46cfcbd website: bump postcss from 8.5.5 to 8.5.6 in /website (#15087)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.5 to 8.5.6.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.5...8.5.6)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-17 13:31:12 +02:00
c50353ebf6 core: bump webauthn from 2.5.2 to 2.6.0 (#15089)
Bumps [webauthn](https://github.com/duo-labs/py_webauthn) from 2.5.2 to 2.6.0.
- [Release notes](https://github.com/duo-labs/py_webauthn/releases)
- [Changelog](https://github.com/duo-labs/py_webauthn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/duo-labs/py_webauthn/compare/v2.5.2...v2.6.0)

---
updated-dependencies:
- dependency-name: webauthn
  dependency-version: 2.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-17 13:30:57 +02:00
db6be9e1b6 core: bump goauthentik.io/api/v3 from 3.2025061.2 to 3.2025062.1 (#15090)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025061.2 to 3.2025062.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.2025061.2...v3.2025062.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-17 13:30:50 +02:00
a74892886d web: bump the eslint group across 2 directories with 3 updates (#15091)
Bumps the eslint group with 1 update in the /packages/eslint-config directory: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).
Bumps the eslint group with 1 update in the /web directory: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


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

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

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

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

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-17 13:30:42 +02:00
74cd4c2236 translate: Updates for file web/xliff/en.xlf in zh_CN (#15074)
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-06-17 13:28:56 +02:00
ef3bd7e77b translate: Updates for file web/xliff/en.xlf in zh-Hans (#15075)
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-06-17 13:28:42 +02:00
3f5ad2baa4 ci: fix post-release e2e builds failing (#15082)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-17 09:10:26 +02:00
24805f087b web: bump API Client version (#15079)
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-06-17 01:51:07 +02:00
9464b422a3 web/common: fix uiConfig not merged correctly (#15080)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-17 01:36:39 +02:00
da6d4ede51 root: backport version bump 2025.6.2 (#15078)
release: 2025.6.2
2025-06-17 00:21:39 +02:00
cecad5bfd3 website/integrations: add note to nextcloud OIDC config (#15073)
Add note to OIDC config
2025-06-16 16:47:16 +00:00
bc4b07d57b web/admin: remove all special cases of slug handling, replace with a "smart slug" component (#14983)
* web: Add InvalidationFlow to Radius Provider dialogues

## What

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

## Note

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

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

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

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

This reverts commit dddde09be5.

* web/components: Remove all special cases of slug handling, replace with a "smart slug" component

This commit removes all special handling for the `slug` attribute in our text. A variant of the text
input control that can handle formatting-as-slugs has replaced all the slugificiation code; simply
drop it onto a page and tell it the (must be unique) selector from which to get the data to be
slugified. It only looks up one tier of the DOM so be careful that both the text input and its slug
accessory occupy the same DOM context.

## Details

### The Component

Now that we know a (lot) more about Lit, this component has been slightly updated to meet our
current standards.

- web/src/components/ak-slug-input.ts

Changes made:

- The "listen for the source object" has been moved to the `firstUpdated`, so that it no longer has
  to wait for the end of a render.
 - The `dirtyFlag` handler now uses the `@input` syntax.
- Updated the slug formatter to permit trailing dashes.
- Uses the `@bound` decorator, eliminating the need to do binding in the constructor (and so
  eliminating the constructor completely).

### Component uses:

The following components were revised to use `ak-slug-input` instead of a plain text input with the
slug-handling added by our forms manager.

- applications/ApplicationForm.ts
- flows/FlowForm.ts
- sources/kerberos/KerberosSourceForm.ts
- sources/ldap/LDAPSourceForm.ts
- sources/oauth/OAuthSourceForm.ts
- sources/plex/PlexSourceForm.ts
- sources/saml/SAMLSourceForm.ts
- sources/scim/SCIMSourceForm.ts

### Remove the redundant special slug handling code

- web/src/elements/forms/Form.ts
- web/src/elements/forms/HorizontalFormElement.ts

### A special case among special cases

- web/src/admin/stages/invitation/InvitationForm.ts

This form is our one case where we have a slug input field with no corresponding text source. Adding
a simple event handler to validate the value whenever it changed and write back a "clean" slug was
the most straightforward solution. I added a help line; it seemed "surprising" to ask someone for a
name and not follow the same rules as "names" everywhere else in our UI without explanation.

* After writing the commit message, I realized some of the comments I made MUST be added to the component.

* The `source` attribute needed its own comment to indicate that a `query()` compatible selector is expected.

* Added public/private/protected/# indicators to all fields.  Trying to balance between getting it 'right' and leaving an opening for harmonizing style-sharing and state-sharing between (text / textarea), slug, password and (visible / hidden / secret).

* Removed the ids as requested; the default "look for this" matches the original behavior without requiring it be hard-coded and unchangable.
2025-06-16 09:04:00 -07:00
e85d2d0096 Web/cleanup/empty state better slot handling (#14289)
* web: Add InvalidationFlow to Radius Provider dialogues

## What

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

## Note

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

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

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

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

This reverts commit dddde09be5.

* web: remove Lit syntax from always true attributes

## What

Replaces instances of `?loading=${true}` and `?loading="${true}"` with `loading`

## Why

The Lit syntax is completely unnecessary when the attribute's state is constant, and it's a few
(just a few) extra CPU cycles for Lit to process that.

More to the point, it annoys me.

## How

```
$ perl -pi.bak -e 's/\?loading=\$\{true\}/loading/' $(rg -l '\?loading=\$\{true\}')
$ find . -name '*.bak' -exec rm {} \;
$ perl -pi.bak -e 's/\?loading="\$\{true\}"/loading/' $(rg -l '\?loading="\$\{true\}"')
$ find . -name '*.bak' -exec rm {} \;
```

* Prettier had opinions

* web: move optional textual information out of attributes and into slots

## What

Replaces instances of:

```
<ak-empty-state header=${msg(...)}></ak-empty-state>
```

with

```
<ak-empty-state><span slot="header">${msg(...)}</span></ak-empty-state>
```

## Why

1. It's correct.
2. It lets us elide the decorations for any slots we aren't using.
3. It's preparation for moving to Patternfly 5
4. It annoyed me.

## How

Since we already have Patternfly Elements installed, we have access to the PFE-Core, which has the
unbelievable useful `SlotsController`.  Using it, I created a conditional render template that will
only put in the header, body, and primary slots if there is something in the lightDOM requesting
those slots.  The conditional template will still put the spinner in if the header is not provided
but the loading state is true.

I then had to edit all the places where this is used. For about 30 of them, this script sufficed:

```
perl -pi.bak -e 's/header="?(\$\{msg\([^)]+\)\})"?>/><span slot="header">\1<\/span>/' \
     $(rg -l `<ak-empty-state[^>]header')

```

The other six had to be done by hand.  I have tested a handful of the automatic ones, and all of the
ones that were edited manually.  I'm pleasantly surprised that the textual rules [are inherited by
the slots as expected](https://htmlwithsuperpowers.netlify.app/styling/inheritable.htm).
2025-06-16 08:17:11 -07:00
be1dd3103b website/docs: release notes for 2025.6.2 (#15065)
* website/docs: release notes for `2025.6.2`

* fixup! website/docs: release notes for `2025.6.2`
2025-06-16 17:01:56 +02:00
5dfde5e1d3 website/docs: remove commented out config options (#15064)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-16 16:35:23 +02:00
7cb1e6d81e website/docs: postgres troubleshooting: get PGPASSWORD from POSTGRES_PASSWORD_FILE (#15039) 2025-06-16 15:00:23 +02:00
d7c3129b1c core: bump goauthentik/fips-python from 3.13.4-slim-bookworm-fips to 3.13.5-slim-bookworm-fips (#15058)
core: bump goauthentik/fips-python

Bumps goauthentik/fips-python from 3.13.4-slim-bookworm-fips to 3.13.5-slim-bookworm-fips.

---
updated-dependencies:
- dependency-name: goauthentik/fips-python
  dependency-version: 3.13.5-slim-bookworm-fips
  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-06-16 12:28:39 +02:00
2a1d33021b website: bump the eslint group in /website with 2 updates (#15059)
Bumps the eslint group in /website with 2 updates: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) and [eslint](https://github.com/eslint/eslint).


Updates `@eslint/js` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.29.0/packages/js)

Updates `eslint` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.28.0...v9.29.0)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 9.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:28:21 +02:00
f273e49ae6 web: bump the wdio group across 1 directory with 3 updates (#14593)
Bumps the wdio group with 3 updates in the /web directory: [@wdio/browser-runner](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-browser-runner), [@wdio/cli](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-cli) and [@wdio/spec-reporter](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-spec-reporter).


Updates `@wdio/browser-runner` from 9.4.1 to 9.14.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v9.14.0/packages/wdio-browser-runner)

Updates `@wdio/cli` from 9.4.1 to 9.14.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v9.14.0/packages/wdio-cli)

Updates `@wdio/spec-reporter` from 9.1.2 to 9.14.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v9.14.0/packages/wdio-spec-reporter)

---
updated-dependencies:
- dependency-name: "@wdio/browser-runner"
  dependency-version: 9.14.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: wdio
- dependency-name: "@wdio/cli"
  dependency-version: 9.14.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: wdio
- dependency-name: "@wdio/spec-reporter"
  dependency-version: 9.14.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: wdio
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:28:10 +02:00
cc31957900 web: bump @sentry/browser from 9.28.1 to 9.29.0 in /web in the sentry group across 1 directory (#15061)
web: bump @sentry/browser in /web in the sentry group across 1 directory

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


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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:27:30 +02:00
b1ccdecc8e web: bump the eslint group across 2 directories with 2 updates (#15062)
Bumps the eslint group with 1 update in the /packages/eslint-config directory: [eslint](https://github.com/eslint/eslint).
Bumps the eslint group with 1 update in the /web directory: [eslint](https://github.com/eslint/eslint).


Updates `eslint` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.28.0...v9.29.0)

Updates `@eslint/js` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.29.0/packages/js)

Updates `eslint` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.28.0...v9.29.0)

Updates `@eslint/js` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.29.0/packages/js)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@eslint/js"
  dependency-version: 9.29.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 9.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@eslint/js"
  dependency-version: 9.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:27:17 +02:00
34031003a4 core: bump axllent/mailpit from v1.26.0 to v1.26.1 in /tests/e2e (#15060)
Bumps axllent/mailpit from v1.26.0 to v1.26.1.

---
updated-dependencies:
- dependency-name: axllent/mailpit
  dependency-version: v1.26.1
  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-06-16 12:27:01 +02:00
055e1d1025 core: bump pydantic from 2.11.5 to 2.11.7 (#15063)
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.11.5 to 2.11.7.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.11.5...v2.11.7)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.11.7
  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-06-16 12:26:52 +02:00
59a804273e providers/oauth2: bug fixes from conformance testing (#15056)
* check authorize request param earlier

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

* fix basic suite?

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

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

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

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

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

# Conflicts:
#	tests/openid_conformance/test_conformance.py

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-16 12:23:18 +02:00
bce70a1796 website/integrations: change nextcloud scope name to avoid confusion (#15050)
changed "profile" to "nextcloud"

Signed-off-by: Marlin <77961876+Keksmo@users.noreply.github.com>
2025-06-16 03:10:53 +02:00
e86c40a00c web/flow: cleanup WebAuthn helper functions (#14460)
* pass #1

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

* pass #2

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

* add polyfill

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-16 02:39:50 +02:00
92 changed files with 1596 additions and 1859 deletions

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 2025.6.1 current_version = 2025.6.2
tag = True tag = True
commit = 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*))? parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?

View File

@ -202,7 +202,7 @@ jobs:
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: web/dist path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
- name: prepare web ui - name: prepare web ui
if: steps.cache-web.outputs.cache-hit != 'true' if: steps.cache-web.outputs.cache-hit != 'true'
working-directory: web working-directory: web

View File

@ -77,7 +77,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
# Stage 4: Download uv # Stage 4: Download uv
FROM ghcr.io/astral-sh/uv:0.7.13 AS uv FROM ghcr.io/astral-sh/uv:0.7.13 AS uv
# Stage 5: Base python image # Stage 5: Base python image
FROM ghcr.io/goauthentik/fips-python:3.13.4-slim-bookworm-fips AS python-base FROM ghcr.io/goauthentik/fips-python:3.13.5-slim-bookworm-fips AS python-base
ENV VENV_PATH="/ak-root/.venv" \ ENV VENV_PATH="/ak-root/.venv" \
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \ PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \

View File

@ -2,7 +2,7 @@
from os import environ from os import environ
__version__ = "2025.6.1" __version__ = "2025.6.2"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -15,7 +15,6 @@ class OAuth2Error(SentryIgnoredException):
error: str error: str
description: str description: str
cause: str | None = None
def create_dict(self): def create_dict(self):
"""Return error as dict for JSON Rendering""" """Return error as dict for JSON Rendering"""
@ -35,10 +34,6 @@ class OAuth2Error(SentryIgnoredException):
**kwargs, **kwargs,
) )
def with_cause(self, cause: str):
self.cause = cause
return self
class RedirectUriError(OAuth2Error): class RedirectUriError(OAuth2Error):
"""The request fails due to a missing, invalid, or mismatching """The request fails due to a missing, invalid, or mismatching

View File

@ -12,7 +12,7 @@ from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.lib.utils.time import timedelta_from_string from authentik.lib.utils.time import timedelta_from_string
from authentik.providers.oauth2.constants import SCOPE_OFFLINE_ACCESS, SCOPE_OPENID, TOKEN_TYPE from authentik.providers.oauth2.constants import TOKEN_TYPE
from authentik.providers.oauth2.errors import AuthorizeError, ClientIdError, RedirectUriError from authentik.providers.oauth2.errors import AuthorizeError, ClientIdError, RedirectUriError
from authentik.providers.oauth2.models import ( from authentik.providers.oauth2.models import (
AccessToken, AccessToken,
@ -43,7 +43,7 @@ class TestAuthorize(OAuthTestCase):
authorization_flow=create_test_flow(), authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid/Foo")], redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid/Foo")],
) )
with self.assertRaises(AuthorizeError) as cm: with self.assertRaises(AuthorizeError):
request = self.factory.get( request = self.factory.get(
"/", "/",
data={ data={
@ -53,7 +53,6 @@ class TestAuthorize(OAuthTestCase):
}, },
) )
OAuthAuthorizationParams.from_request(request) OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.error, "unsupported_response_type")
def test_invalid_client_id(self): def test_invalid_client_id(self):
"""Test invalid client ID""" """Test invalid client ID"""
@ -69,7 +68,7 @@ class TestAuthorize(OAuthTestCase):
authorization_flow=create_test_flow(), authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid/Foo")], redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid/Foo")],
) )
with self.assertRaises(AuthorizeError) as cm: with self.assertRaises(AuthorizeError):
request = self.factory.get( request = self.factory.get(
"/", "/",
data={ data={
@ -80,30 +79,19 @@ class TestAuthorize(OAuthTestCase):
}, },
) )
OAuthAuthorizationParams.from_request(request) OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.error, "request_not_supported")
def test_invalid_redirect_uri_missing(self):
"""test missing redirect URI"""
OAuth2Provider.objects.create(
name=generate_id(),
client_id="test",
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid")],
)
with self.assertRaises(RedirectUriError) as cm:
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "redirect_uri_missing")
def test_invalid_redirect_uri(self): def test_invalid_redirect_uri(self):
"""test invalid redirect URI""" """test missing/invalid redirect URI"""
OAuth2Provider.objects.create( OAuth2Provider.objects.create(
name=generate_id(), name=generate_id(),
client_id="test", client_id="test",
authorization_flow=create_test_flow(), authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid")], redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid")],
) )
with self.assertRaises(RedirectUriError) as cm: with self.assertRaises(RedirectUriError):
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
with self.assertRaises(RedirectUriError):
request = self.factory.get( request = self.factory.get(
"/", "/",
data={ data={
@ -113,7 +101,6 @@ class TestAuthorize(OAuthTestCase):
}, },
) )
OAuthAuthorizationParams.from_request(request) OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "redirect_uri_no_match")
def test_blocked_redirect_uri(self): def test_blocked_redirect_uri(self):
"""test missing/invalid redirect URI""" """test missing/invalid redirect URI"""
@ -121,9 +108,9 @@ class TestAuthorize(OAuthTestCase):
name=generate_id(), name=generate_id(),
client_id="test", client_id="test",
authorization_flow=create_test_flow(), authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "data:localhost")], redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "data:local.invalid")],
) )
with self.assertRaises(RedirectUriError) as cm: with self.assertRaises(RedirectUriError):
request = self.factory.get( request = self.factory.get(
"/", "/",
data={ data={
@ -133,7 +120,6 @@ class TestAuthorize(OAuthTestCase):
}, },
) )
OAuthAuthorizationParams.from_request(request) OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "redirect_uri_forbidden_scheme")
def test_invalid_redirect_uri_empty(self): def test_invalid_redirect_uri_empty(self):
"""test missing/invalid redirect URI""" """test missing/invalid redirect URI"""
@ -143,6 +129,9 @@ class TestAuthorize(OAuthTestCase):
authorization_flow=create_test_flow(), authorization_flow=create_test_flow(),
redirect_uris=[], redirect_uris=[],
) )
with self.assertRaises(RedirectUriError):
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
request = self.factory.get( request = self.factory.get(
"/", "/",
data={ data={
@ -161,9 +150,12 @@ class TestAuthorize(OAuthTestCase):
name=generate_id(), name=generate_id(),
client_id="test", client_id="test",
authorization_flow=create_test_flow(), authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.REGEX, "http://local.invalid?")], redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid?")],
) )
with self.assertRaises(RedirectUriError) as cm: with self.assertRaises(RedirectUriError):
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
with self.assertRaises(RedirectUriError):
request = self.factory.get( request = self.factory.get(
"/", "/",
data={ data={
@ -173,7 +165,6 @@ class TestAuthorize(OAuthTestCase):
}, },
) )
OAuthAuthorizationParams.from_request(request) OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "redirect_uri_no_match")
def test_redirect_uri_invalid_regex(self): def test_redirect_uri_invalid_regex(self):
"""test missing/invalid redirect URI (invalid regex)""" """test missing/invalid redirect URI (invalid regex)"""
@ -181,9 +172,12 @@ class TestAuthorize(OAuthTestCase):
name=generate_id(), name=generate_id(),
client_id="test", client_id="test",
authorization_flow=create_test_flow(), authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.REGEX, "+")], redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "+")],
) )
with self.assertRaises(RedirectUriError) as cm: with self.assertRaises(RedirectUriError):
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
with self.assertRaises(RedirectUriError):
request = self.factory.get( request = self.factory.get(
"/", "/",
data={ data={
@ -193,22 +187,23 @@ class TestAuthorize(OAuthTestCase):
}, },
) )
OAuthAuthorizationParams.from_request(request) OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "redirect_uri_no_match")
def test_redirect_uri_regex(self): def test_empty_redirect_uri(self):
"""test valid redirect URI (regex)""" """test empty redirect URI (configure in provider)"""
OAuth2Provider.objects.create( OAuth2Provider.objects.create(
name=generate_id(), name=generate_id(),
client_id="test", client_id="test",
authorization_flow=create_test_flow(), authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.REGEX, ".+")],
) )
with self.assertRaises(RedirectUriError):
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
OAuthAuthorizationParams.from_request(request)
request = self.factory.get( request = self.factory.get(
"/", "/",
data={ data={
"response_type": "code", "response_type": "code",
"client_id": "test", "client_id": "test",
"redirect_uri": "http://foo.bar.baz", "redirect_uri": "http://localhost",
}, },
) )
OAuthAuthorizationParams.from_request(request) OAuthAuthorizationParams.from_request(request)
@ -263,7 +258,7 @@ class TestAuthorize(OAuthTestCase):
GrantTypes.IMPLICIT, GrantTypes.IMPLICIT,
) )
# Implicit without openid scope # Implicit without openid scope
with self.assertRaises(AuthorizeError) as cm: with self.assertRaises(AuthorizeError):
request = self.factory.get( request = self.factory.get(
"/", "/",
data={ data={
@ -290,7 +285,7 @@ class TestAuthorize(OAuthTestCase):
self.assertEqual( self.assertEqual(
OAuthAuthorizationParams.from_request(request).grant_type, GrantTypes.HYBRID OAuthAuthorizationParams.from_request(request).grant_type, GrantTypes.HYBRID
) )
with self.assertRaises(AuthorizeError) as cm: with self.assertRaises(AuthorizeError):
request = self.factory.get( request = self.factory.get(
"/", "/",
data={ data={
@ -300,7 +295,6 @@ class TestAuthorize(OAuthTestCase):
}, },
) )
OAuthAuthorizationParams.from_request(request) OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.error, "unsupported_response_type")
def test_full_code(self): def test_full_code(self):
"""Test full authorization""" """Test full authorization"""
@ -393,8 +387,7 @@ class TestAuthorize(OAuthTestCase):
self.assertEqual( self.assertEqual(
response.url, response.url,
( (
f"http://localhost#access_token={token.token}" f"http://localhost#id_token={provider.encode(token.id_token.to_dict())}"
f"&id_token={provider.encode(token.id_token.to_dict())}"
f"&token_type={TOKEN_TYPE}" f"&token_type={TOKEN_TYPE}"
f"&expires_in={int(expires)}&state={state}" f"&expires_in={int(expires)}&state={state}"
), ),
@ -569,7 +562,6 @@ class TestAuthorize(OAuthTestCase):
"url": "http://localhost", "url": "http://localhost",
"title": f"Redirecting to {app.name}...", "title": f"Redirecting to {app.name}...",
"attrs": { "attrs": {
"access_token": token.token,
"id_token": provider.encode(token.id_token.to_dict()), "id_token": provider.encode(token.id_token.to_dict()),
"token_type": TOKEN_TYPE, "token_type": TOKEN_TYPE,
"expires_in": "3600", "expires_in": "3600",
@ -621,54 +613,3 @@ class TestAuthorize(OAuthTestCase):
}, },
}, },
) )
def test_openid_missing_invalid(self):
"""test request requiring an OpenID scope to be set"""
OAuth2Provider.objects.create(
name=generate_id(),
client_id="test",
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost")],
)
request = self.factory.get(
"/",
data={
"response_type": "id_token",
"client_id": "test",
"redirect_uri": "http://localhost",
"scope": "",
},
)
with self.assertRaises(AuthorizeError) as cm:
OAuthAuthorizationParams.from_request(request)
self.assertEqual(cm.exception.cause, "scope_openid_missing")
@apply_blueprint("system/providers-oauth2.yaml")
def test_offline_access_invalid(self):
"""test request for offline_access with invalid response type"""
provider = OAuth2Provider.objects.create(
name=generate_id(),
client_id="test",
authorization_flow=create_test_flow(),
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost")],
)
provider.property_mappings.set(
ScopeMapping.objects.filter(
managed__in=[
"goauthentik.io/providers/oauth2/scope-openid",
"goauthentik.io/providers/oauth2/scope-offline_access",
]
)
)
request = self.factory.get(
"/",
data={
"response_type": "id_token",
"client_id": "test",
"redirect_uri": "http://localhost",
"scope": f"{SCOPE_OPENID} {SCOPE_OFFLINE_ACCESS}",
"nonce": generate_id(),
},
)
parsed = OAuthAuthorizationParams.from_request(request)
self.assertNotIn(SCOPE_OFFLINE_ACCESS, parsed.scope)

View File

@ -150,12 +150,12 @@ class OAuthAuthorizationParams:
self.check_redirect_uri() self.check_redirect_uri()
self.check_grant() self.check_grant()
self.check_scope(github_compat) self.check_scope(github_compat)
self.check_nonce()
self.check_code_challenge()
if self.request: if self.request:
raise AuthorizeError( raise AuthorizeError(
self.redirect_uri, "request_not_supported", self.grant_type, self.state self.redirect_uri, "request_not_supported", self.grant_type, self.state
) )
self.check_nonce()
self.check_code_challenge()
def check_grant(self): def check_grant(self):
"""Check grant""" """Check grant"""
@ -190,7 +190,7 @@ class OAuthAuthorizationParams:
allowed_redirect_urls = self.provider.redirect_uris allowed_redirect_urls = self.provider.redirect_uris
if not self.redirect_uri: if not self.redirect_uri:
LOGGER.warning("Missing redirect uri.") LOGGER.warning("Missing redirect uri.")
raise RedirectUriError("", allowed_redirect_urls).with_cause("redirect_uri_missing") raise RedirectUriError("", allowed_redirect_urls)
if len(allowed_redirect_urls) < 1: if len(allowed_redirect_urls) < 1:
LOGGER.info("Setting redirect for blank redirect_uris", redirect=self.redirect_uri) LOGGER.info("Setting redirect for blank redirect_uris", redirect=self.redirect_uri)
@ -219,14 +219,10 @@ class OAuthAuthorizationParams:
provider=self.provider, provider=self.provider,
) )
if not match_found: if not match_found:
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls).with_cause( raise RedirectUriError(self.redirect_uri, allowed_redirect_urls)
"redirect_uri_no_match"
)
# Check against forbidden schemes # Check against forbidden schemes
if urlparse(self.redirect_uri).scheme in FORBIDDEN_URI_SCHEMES: if urlparse(self.redirect_uri).scheme in FORBIDDEN_URI_SCHEMES:
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls).with_cause( raise RedirectUriError(self.redirect_uri, allowed_redirect_urls)
"redirect_uri_forbidden_scheme"
)
def check_scope(self, github_compat=False): def check_scope(self, github_compat=False):
"""Ensure openid scope is set in Hybrid flows, or when requesting an id_token""" """Ensure openid scope is set in Hybrid flows, or when requesting an id_token"""
@ -255,9 +251,7 @@ class OAuthAuthorizationParams:
or self.response_type in [ResponseTypes.ID_TOKEN, ResponseTypes.ID_TOKEN_TOKEN] or self.response_type in [ResponseTypes.ID_TOKEN, ResponseTypes.ID_TOKEN_TOKEN]
): ):
LOGGER.warning("Missing 'openid' scope.") LOGGER.warning("Missing 'openid' scope.")
raise AuthorizeError( raise AuthorizeError(self.redirect_uri, "invalid_scope", self.grant_type, self.state)
self.redirect_uri, "invalid_scope", self.grant_type, self.state
).with_cause("scope_openid_missing")
if SCOPE_OFFLINE_ACCESS in self.scope: if SCOPE_OFFLINE_ACCESS in self.scope:
# https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess # https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
# Don't explicitly request consent with offline_access, as the spec allows for # Don't explicitly request consent with offline_access, as the spec allows for
@ -292,9 +286,7 @@ class OAuthAuthorizationParams:
return return
if not self.nonce: if not self.nonce:
LOGGER.warning("Missing nonce for OpenID Request") LOGGER.warning("Missing nonce for OpenID Request")
raise AuthorizeError( raise AuthorizeError(self.redirect_uri, "invalid_request", self.grant_type, self.state)
self.redirect_uri, "invalid_request", self.grant_type, self.state
).with_cause("none_missing")
def check_code_challenge(self): def check_code_challenge(self):
"""PKCE validation of the transformation method.""" """PKCE validation of the transformation method."""
@ -353,10 +345,10 @@ class AuthorizationFlowInitView(PolicyAccessView):
self.request, github_compat=self.github_compat self.request, github_compat=self.github_compat
) )
except AuthorizeError as error: except AuthorizeError as error:
LOGGER.warning(error.description, redirect_uri=error.redirect_uri, cause=error.cause) LOGGER.warning(error.description, redirect_uri=error.redirect_uri)
raise RequestValidationError(error.get_response(self.request)) from None raise RequestValidationError(error.get_response(self.request)) from None
except OAuth2Error as error: except OAuth2Error as error:
LOGGER.warning(error.description, cause=error.cause) LOGGER.warning(error.description)
raise RequestValidationError( raise RequestValidationError(
bad_request_message(self.request, error.description, title=error.error) bad_request_message(self.request, error.description, title=error.error)
) from None ) from None
@ -638,7 +630,6 @@ class OAuthFulfillmentStage(StageView):
if self.params.response_type in [ if self.params.response_type in [
ResponseTypes.ID_TOKEN_TOKEN, ResponseTypes.ID_TOKEN_TOKEN,
ResponseTypes.CODE_ID_TOKEN_TOKEN, ResponseTypes.CODE_ID_TOKEN_TOKEN,
ResponseTypes.ID_TOKEN,
ResponseTypes.CODE_TOKEN, ResponseTypes.CODE_TOKEN,
]: ]:
query_fragment["access_token"] = token.token query_fragment["access_token"] = token.token

View File

@ -190,6 +190,7 @@ class SAMLProviderSerializer(ProviderSerializer):
"sign_response", "sign_response",
"sp_binding", "sp_binding",
"default_relay_state", "default_relay_state",
"default_name_id_policy",
"url_download_metadata", "url_download_metadata",
"url_sso_post", "url_sso_post",
"url_sso_redirect", "url_sso_redirect",

View File

@ -0,0 +1,31 @@
# Generated by Django 5.1.11 on 2025-06-18 09:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_saml", "0018_alter_samlprovider_acs_url"),
]
operations = [
migrations.AddField(
model_name="samlprovider",
name="default_name_id_policy",
field=models.TextField(
choices=[
("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "Email"),
("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", "Persistent"),
("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName", "X509"),
(
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName",
"Windows",
),
("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", "Transient"),
("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", "Unspecified"),
],
default="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
),
),
]

View File

@ -12,6 +12,7 @@ from authentik.core.models import PropertyMapping, Provider
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.lib.models import DomainlessURLValidator from authentik.lib.models import DomainlessURLValidator
from authentik.lib.utils.time import timedelta_string_validator from authentik.lib.utils.time import timedelta_string_validator
from authentik.sources.saml.models import SAMLNameIDPolicy
from authentik.sources.saml.processors.constants import ( from authentik.sources.saml.processors.constants import (
DSA_SHA1, DSA_SHA1,
ECDSA_SHA1, ECDSA_SHA1,
@ -179,6 +180,9 @@ class SAMLProvider(Provider):
default_relay_state = models.TextField( default_relay_state = models.TextField(
default="", blank=True, help_text=_("Default relay_state value for IDP-initiated logins") default="", blank=True, help_text=_("Default relay_state value for IDP-initiated logins")
) )
default_name_id_policy = models.TextField(
choices=SAMLNameIDPolicy.choices, default=SAMLNameIDPolicy.UNSPECIFIED
)
sign_assertion = models.BooleanField(default=True) sign_assertion = models.BooleanField(default=True)
sign_response = models.BooleanField(default=False) sign_response = models.BooleanField(default=False)

View File

@ -205,6 +205,13 @@ class AssertionProcessor:
def get_name_id(self) -> Element: def get_name_id(self) -> Element:
"""Get NameID Element""" """Get NameID Element"""
name_id = Element(f"{{{NS_SAML_ASSERTION}}}NameID") name_id = Element(f"{{{NS_SAML_ASSERTION}}}NameID")
# For requests that don't specify a NameIDPolicy, check if we
# can fall back to the provider default
if (
self.auth_n_request.name_id_policy == SAML_NAME_ID_FORMAT_UNSPECIFIED
and self.provider.default_name_id_policy != SAML_NAME_ID_FORMAT_UNSPECIFIED
):
self.auth_n_request.name_id_policy = self.provider.default_name_id_policy
name_id.attrib["Format"] = self.auth_n_request.name_id_policy name_id.attrib["Format"] = self.auth_n_request.name_id_policy
# persistent is used as a fallback, so always generate it # persistent is used as a fallback, so always generate it
persistent = self.http_request.user.uid persistent = self.http_request.user.uid

View File

@ -13,6 +13,7 @@ from authentik.lib.xml import lxml_from_string
from authentik.providers.saml.exceptions import CannotHandleAssertion from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import SAMLProvider from authentik.providers.saml.models import SAMLProvider
from authentik.providers.saml.utils.encoding import decode_base64_and_inflate from authentik.providers.saml.utils.encoding import decode_base64_and_inflate
from authentik.sources.saml.models import SAMLNameIDPolicy
from authentik.sources.saml.processors.constants import ( from authentik.sources.saml.processors.constants import (
DSA_SHA1, DSA_SHA1,
NS_MAP, NS_MAP,
@ -175,7 +176,9 @@ class AuthNRequestParser:
def idp_initiated(self) -> AuthNRequest: def idp_initiated(self) -> AuthNRequest:
"""Create IdP Initiated AuthNRequest""" """Create IdP Initiated AuthNRequest"""
relay_state = None request = AuthNRequest(relay_state=None)
if self.provider.default_relay_state != "": if self.provider.default_relay_state != "":
relay_state = self.provider.default_relay_state request.relay_state = self.provider.default_relay_state
return AuthNRequest(relay_state=relay_state) if self.provider.default_name_id_policy != SAMLNameIDPolicy.UNSPECIFIED:
request.name_id_policy = self.provider.default_name_id_policy
return request

View File

@ -13,6 +13,7 @@ from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.utils.encoding import PEM_FOOTER, PEM_HEADER from authentik.providers.saml.utils.encoding import PEM_FOOTER, PEM_HEADER
from authentik.sources.saml.models import SAMLNameIDPolicy
from authentik.sources.saml.processors.constants import ( from authentik.sources.saml.processors.constants import (
NS_MAP, NS_MAP,
NS_SAML_METADATA, NS_SAML_METADATA,
@ -46,6 +47,7 @@ class ServiceProviderMetadata:
auth_n_request_signed: bool auth_n_request_signed: bool
assertion_signed: bool assertion_signed: bool
name_id_policy: SAMLNameIDPolicy
signing_keypair: CertificateKeyPair | None = None signing_keypair: CertificateKeyPair | None = None
@ -60,6 +62,7 @@ class ServiceProviderMetadata:
provider.issuer = self.entity_id provider.issuer = self.entity_id
provider.sp_binding = self.acs_binding provider.sp_binding = self.acs_binding
provider.acs_url = self.acs_location provider.acs_url = self.acs_location
provider.default_name_id_policy = self.name_id_policy
if self.signing_keypair and self.auth_n_request_signed: if self.signing_keypair and self.auth_n_request_signed:
self.signing_keypair.name = f"Provider {name} - SAML Signing Certificate" self.signing_keypair.name = f"Provider {name} - SAML Signing Certificate"
self.signing_keypair.save() self.signing_keypair.save()
@ -148,6 +151,11 @@ class ServiceProviderMetadataParser:
if signing_keypair: if signing_keypair:
self.check_signature(root, signing_keypair) self.check_signature(root, signing_keypair)
name_id_format = descriptor.findall(f"{{{NS_SAML_METADATA}}}NameIDFormat")
name_id_policy = SAMLNameIDPolicy.UNSPECIFIED
if len(name_id_format) > 0:
name_id_policy = SAMLNameIDPolicy(name_id_format[0].text)
return ServiceProviderMetadata( return ServiceProviderMetadata(
entity_id=entity_id, entity_id=entity_id,
acs_binding=acs_binding, acs_binding=acs_binding,
@ -155,4 +163,5 @@ class ServiceProviderMetadataParser:
auth_n_request_signed=auth_n_request_signed, auth_n_request_signed=auth_n_request_signed,
assertion_signed=assertion_signed, assertion_signed=assertion_signed,
signing_keypair=signing_keypair, signing_keypair=signing_keypair,
name_id_policy=name_id_policy,
) )

View File

@ -4,7 +4,7 @@
cacheDuration="PT604800S" cacheDuration="PT604800S"
entityID="http://localhost:8080/saml/metadata"> entityID="http://localhost:8080/saml/metadata">
<md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://localhost:8080/saml/acs" Location="http://localhost:8080/saml/acs"
index="1" /> index="1" />

View File

@ -14,6 +14,7 @@ from authentik.lib.xml import lxml_from_string
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.processors.metadata import MetadataProcessor from authentik.providers.saml.processors.metadata import MetadataProcessor
from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser
from authentik.sources.saml.models import SAMLNameIDPolicy
from authentik.sources.saml.processors.constants import ECDSA_SHA256, NS_MAP, NS_SAML_METADATA from authentik.sources.saml.processors.constants import ECDSA_SHA256, NS_MAP, NS_SAML_METADATA
@ -86,6 +87,7 @@ class TestServiceProviderMetadataParser(TestCase):
self.assertEqual(provider.acs_url, "http://localhost:8080/saml/acs") self.assertEqual(provider.acs_url, "http://localhost:8080/saml/acs")
self.assertEqual(provider.issuer, "http://localhost:8080/saml/metadata") self.assertEqual(provider.issuer, "http://localhost:8080/saml/metadata")
self.assertEqual(provider.sp_binding, SAMLBindings.POST) self.assertEqual(provider.sp_binding, SAMLBindings.POST)
self.assertEqual(provider.default_name_id_policy, SAMLNameIDPolicy.EMAIL)
self.assertEqual( self.assertEqual(
len(provider.property_mappings.all()), len(provider.property_mappings.all()),
len(SAMLPropertyMapping.objects.exclude(managed__isnull=True)), len(SAMLPropertyMapping.objects.exclude(managed__isnull=True)),

View File

@ -166,6 +166,7 @@ SPECTACULAR_SETTINGS = {
"UserVerificationEnum": "authentik.stages.authenticator_webauthn.models.UserVerification", "UserVerificationEnum": "authentik.stages.authenticator_webauthn.models.UserVerification",
"UserTypeEnum": "authentik.core.models.UserTypes", "UserTypeEnum": "authentik.core.models.UserTypes",
"OutgoingSyncDeleteAction": "authentik.lib.sync.outgoing.models.OutgoingSyncDeleteAction", "OutgoingSyncDeleteAction": "authentik.lib.sync.outgoing.models.OutgoingSyncDeleteAction",
"SAMLNameIDPolicyEnum": "authentik.sources.saml.models.SAMLNameIDPolicy",
}, },
"ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE": False, "ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE": False,
"ENUM_GENERATE_CHOICE_DESCRIPTION": False, "ENUM_GENERATE_CHOICE_DESCRIPTION": False,

View File

@ -0,0 +1,32 @@
# Generated by Django 5.1.11 on 2025-06-18 09:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_sources_saml", "0019_migrate_usersamlsourceconnection_identifier"),
]
operations = [
migrations.AlterField(
model_name="samlsource",
name="name_id_policy",
field=models.TextField(
choices=[
("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "Email"),
("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", "Persistent"),
("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName", "X509"),
(
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName",
"Windows",
),
("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", "Transient"),
("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", "Unspecified"),
],
default="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
help_text="NameID Policy sent to the IdP. Can be unset, in which case no Policy is sent.",
),
),
]

View File

@ -39,6 +39,7 @@ from authentik.sources.saml.processors.constants import (
SAML_NAME_ID_FORMAT_EMAIL, SAML_NAME_ID_FORMAT_EMAIL,
SAML_NAME_ID_FORMAT_PERSISTENT, SAML_NAME_ID_FORMAT_PERSISTENT,
SAML_NAME_ID_FORMAT_TRANSIENT, SAML_NAME_ID_FORMAT_TRANSIENT,
SAML_NAME_ID_FORMAT_UNSPECIFIED,
SAML_NAME_ID_FORMAT_WINDOWS, SAML_NAME_ID_FORMAT_WINDOWS,
SAML_NAME_ID_FORMAT_X509, SAML_NAME_ID_FORMAT_X509,
SHA1, SHA1,
@ -73,6 +74,7 @@ class SAMLNameIDPolicy(models.TextChoices):
X509 = SAML_NAME_ID_FORMAT_X509 X509 = SAML_NAME_ID_FORMAT_X509
WINDOWS = SAML_NAME_ID_FORMAT_WINDOWS WINDOWS = SAML_NAME_ID_FORMAT_WINDOWS
TRANSIENT = SAML_NAME_ID_FORMAT_TRANSIENT TRANSIENT = SAML_NAME_ID_FORMAT_TRANSIENT
UNSPECIFIED = SAML_NAME_ID_FORMAT_UNSPECIFIED
class SAMLSource(Source): class SAMLSource(Source):

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema", "$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json", "$id": "https://goauthentik.io/blueprints/schema.json",
"type": "object", "type": "object",
"title": "authentik 2025.6.1 Blueprint schema", "title": "authentik 2025.6.2 Blueprint schema",
"required": [ "required": [
"version", "version",
"entries" "entries"
@ -9233,6 +9233,18 @@
"type": "string", "type": "string",
"title": "Default relay state", "title": "Default relay state",
"description": "Default relay_state value for IDP-initiated logins" "description": "Default relay_state value for IDP-initiated logins"
},
"default_name_id_policy": {
"type": "string",
"enum": [
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
"urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName",
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName",
"urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
],
"title": "Default name id policy"
} }
}, },
"required": [] "required": []
@ -11655,7 +11667,8 @@
"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
"urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName", "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName",
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName", "urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName",
"urn:oasis:names:tc:SAML:2.0:nameid-format:transient" "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
], ],
"title": "Name id policy", "title": "Name id policy",
"description": "NameID Policy sent to the IdP. Can be unset, in which case no Policy is sent." "description": "NameID Policy sent to the IdP. Can be unset, in which case no Policy is sent."

View File

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

4
go.mod
View File

@ -18,7 +18,7 @@ require (
github.com/gorilla/sessions v1.4.0 github.com/gorilla/sessions v1.4.0
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/grafana/pyroscope-go v1.2.2 github.com/grafana/pyroscope-go v1.2.2
github.com/jellydator/ttlcache/v3 v3.3.0 github.com/jellydator/ttlcache/v3 v3.4.0
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.8.1 github.com/pires/go-proxyproto v0.8.1
@ -29,7 +29,7 @@ require (
github.com/spf13/cobra v1.9.1 github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/wwt/guac v1.3.2 github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2025061.2 goauthentik.io/api/v3 v3.2025062.1
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.30.0 golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.15.0 golang.org/x/sync v0.15.0

8
go.sum
View File

@ -203,8 +203,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc= github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY=
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw= github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@ -298,8 +298,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.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 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
goauthentik.io/api/v3 v3.2025061.2 h1:bKmrl82Gz6J8lz3f+QIH9g+MEkl3MvkMXF34GktesA0= goauthentik.io/api/v3 v3.2025062.1 h1:spvILDpDDWJNO3pM6QGqmryx6NvSchr1E8H60J/XUCA=
goauthentik.io/api/v3 v3.2025061.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= goauthentik.io/api/v3 v3.2025062.1/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-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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=

View File

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

View File

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

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "@goauthentik/authentik", "name": "@goauthentik/authentik",
"version": "2025.6.1", "version": "2025.6.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@goauthentik/authentik", "name": "@goauthentik/authentik",
"version": "2025.6.1", "version": "2025.6.2",
"devDependencies": { "devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^5.2.2", "@trivago/prettier-plugin-sort-imports": "^5.2.2",
"prettier": "^3.3.3", "prettier": "^3.3.3",

View File

@ -1,6 +1,6 @@
{ {
"name": "@goauthentik/authentik", "name": "@goauthentik/authentik",
"version": "2025.6.1", "version": "2025.6.2",
"private": true, "private": true,
"type": "module", "type": "module",
"devDependencies": { "devDependencies": {

View File

@ -216,9 +216,9 @@
} }
}, },
"node_modules/@eslint/config-array": { "node_modules/@eslint/config-array": {
"version": "0.20.0", "version": "0.20.1",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz",
"integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/object-schema": "^2.1.6", "@eslint/object-schema": "^2.1.6",
@ -274,9 +274,9 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.28.0", "version": "9.29.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz",
"integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", "integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -576,17 +576,17 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz",
"integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", "integrity": "sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.10.0", "@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/scope-manager": "8.34.1",
"@typescript-eslint/type-utils": "8.34.0", "@typescript-eslint/type-utils": "8.34.1",
"@typescript-eslint/utils": "8.34.0", "@typescript-eslint/utils": "8.34.1",
"@typescript-eslint/visitor-keys": "8.34.0", "@typescript-eslint/visitor-keys": "8.34.1",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^7.0.0", "ignore": "^7.0.0",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
@ -600,7 +600,7 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
}, },
"peerDependencies": { "peerDependencies": {
"@typescript-eslint/parser": "^8.34.0", "@typescript-eslint/parser": "^8.34.1",
"eslint": "^8.57.0 || ^9.0.0", "eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0" "typescript": ">=4.8.4 <5.9.0"
} }
@ -616,16 +616,16 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.1.tgz",
"integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", "integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/scope-manager": "8.34.1",
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/typescript-estree": "8.34.0", "@typescript-eslint/typescript-estree": "8.34.1",
"@typescript-eslint/visitor-keys": "8.34.0", "@typescript-eslint/visitor-keys": "8.34.1",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -641,14 +641,14 @@
} }
}, },
"node_modules/@typescript-eslint/project-service": { "node_modules/@typescript-eslint/project-service": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.1.tgz",
"integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", "integrity": "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.34.0", "@typescript-eslint/tsconfig-utils": "^8.34.1",
"@typescript-eslint/types": "^8.34.0", "@typescript-eslint/types": "^8.34.1",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -663,14 +663,14 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.1.tgz",
"integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", "integrity": "sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/visitor-keys": "8.34.0" "@typescript-eslint/visitor-keys": "8.34.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -681,9 +681,9 @@
} }
}, },
"node_modules/@typescript-eslint/tsconfig-utils": { "node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.1.tgz",
"integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", "integrity": "sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -698,14 +698,14 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.1.tgz",
"integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", "integrity": "sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "8.34.0", "@typescript-eslint/typescript-estree": "8.34.1",
"@typescript-eslint/utils": "8.34.0", "@typescript-eslint/utils": "8.34.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"ts-api-utils": "^2.1.0" "ts-api-utils": "^2.1.0"
}, },
@ -722,9 +722,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.1.tgz",
"integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", "integrity": "sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -736,16 +736,16 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.1.tgz",
"integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", "integrity": "sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/project-service": "8.34.0", "@typescript-eslint/project-service": "8.34.1",
"@typescript-eslint/tsconfig-utils": "8.34.0", "@typescript-eslint/tsconfig-utils": "8.34.1",
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/visitor-keys": "8.34.0", "@typescript-eslint/visitor-keys": "8.34.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@ -765,9 +765,9 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "2.0.1", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -804,16 +804,16 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.1.tgz",
"integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", "integrity": "sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.7.0", "@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/scope-manager": "8.34.1",
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/typescript-estree": "8.34.0" "@typescript-eslint/typescript-estree": "8.34.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -828,14 +828,14 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.1.tgz",
"integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", "integrity": "sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.34.1",
"eslint-visitor-keys": "^4.2.0" "eslint-visitor-keys": "^4.2.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -846,9 +846,9 @@
} }
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.14.1", "version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
@ -1554,18 +1554,18 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "9.28.0", "version": "9.29.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz",
"integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.20.0", "@eslint/config-array": "^0.20.1",
"@eslint/config-helpers": "^0.2.1", "@eslint/config-helpers": "^0.2.1",
"@eslint/core": "^0.14.0", "@eslint/core": "^0.14.0",
"@eslint/eslintrc": "^3.3.1", "@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.28.0", "@eslint/js": "9.29.0",
"@eslint/plugin-kit": "^0.3.1", "@eslint/plugin-kit": "^0.3.1",
"@humanfs/node": "^0.16.6", "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
@ -1577,9 +1577,9 @@
"cross-spawn": "^7.0.6", "cross-spawn": "^7.0.6",
"debug": "^4.3.2", "debug": "^4.3.2",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.3.0", "eslint-scope": "^8.4.0",
"eslint-visitor-keys": "^4.2.0", "eslint-visitor-keys": "^4.2.1",
"espree": "^10.3.0", "espree": "^10.4.0",
"esquery": "^1.5.0", "esquery": "^1.5.0",
"esutils": "^2.0.2", "esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
@ -1792,9 +1792,9 @@
} }
}, },
"node_modules/eslint-scope": { "node_modules/eslint-scope": {
"version": "8.3.0", "version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
"integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"esrecurse": "^4.3.0", "esrecurse": "^4.3.0",
@ -1808,9 +1808,9 @@
} }
}, },
"node_modules/eslint-visitor-keys": { "node_modules/eslint-visitor-keys": {
"version": "4.2.0", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1820,14 +1820,14 @@
} }
}, },
"node_modules/espree": { "node_modules/espree": {
"version": "10.3.0", "version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
"integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"acorn": "^8.14.0", "acorn": "^8.15.0",
"acorn-jsx": "^5.3.2", "acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.2.0" "eslint-visitor-keys": "^4.2.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -4035,15 +4035,15 @@
} }
}, },
"node_modules/typescript-eslint": { "node_modules/typescript-eslint": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.0.tgz", "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.1.tgz",
"integrity": "sha512-MRpfN7uYjTrTGigFCt8sRyNqJFhjN0WwZecldaqhWm+wy0gaRt8Edb/3cuUy0zdq2opJWT6iXINKAtewnDOltQ==", "integrity": "sha512-XjS+b6Vg9oT1BaIUfkW3M3LvqZE++rbzAMEHuccCfO/YkP43ha6w3jTEMilQxMF92nVOYCcdjv1ZUhAa1D/0ow==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/eslint-plugin": "8.34.0", "@typescript-eslint/eslint-plugin": "8.34.1",
"@typescript-eslint/parser": "8.34.0", "@typescript-eslint/parser": "8.34.1",
"@typescript-eslint/utils": "8.34.0" "@typescript-eslint/utils": "8.34.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"

View File

@ -1,6 +1,6 @@
[project] [project]
name = "authentik" name = "authentik"
version = "2025.6.1" version = "2025.6.2"
description = "" description = ""
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }] authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
requires-python = "==3.13.*" requires-python = "==3.13.*"
@ -48,7 +48,7 @@ dependencies = [
"packaging==25.0", "packaging==25.0",
"paramiko==3.5.1", "paramiko==3.5.1",
"psycopg[c,pool]==3.2.9", "psycopg[c,pool]==3.2.9",
"pydantic==2.11.5", "pydantic==2.11.7",
"pydantic-scim==0.0.8", "pydantic-scim==0.0.8",
"pyjwt==2.10.1", "pyjwt==2.10.1",
"pyrad==2.4", "pyrad==2.4",
@ -68,7 +68,7 @@ dependencies = [
"urllib3<3", "urllib3<3",
"uvicorn[standard]==0.34.3", "uvicorn[standard]==0.34.3",
"watchdog==6.0.0", "watchdog==6.0.0",
"webauthn==2.5.2", "webauthn==2.6.0",
"wsproto==1.2.0", "wsproto==1.2.0",
"xmlsec==1.3.15", "xmlsec==1.3.15",
"zxcvbn==4.5.0", "zxcvbn==4.5.0",

View File

@ -1,7 +1,7 @@
openapi: 3.0.3 openapi: 3.0.3
info: info:
title: authentik title: authentik
version: 2025.6.1 version: 2025.6.2
description: Making authentication simple. description: Making authentication simple.
contact: contact:
email: hello@goauthentik.io email: hello@goauthentik.io
@ -22454,6 +22454,17 @@ paths:
schema: schema:
type: string type: string
format: uuid format: uuid
- in: query
name: default_name_id_policy
schema:
type: string
enum:
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
- in: query - in: query
name: default_relay_state name: default_relay_state
schema: schema:
@ -29670,6 +29681,7 @@ paths:
enum: enum:
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName - urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName - urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient - urn:oasis:names:tc:SAML:2.0:nameid-format:transient
@ -48745,14 +48757,6 @@ components:
- mode - mode
- name - name
- user_attribute - user_attribute
NameIdPolicyEnum:
enum:
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
type: string
NetworkBindingEnum: NetworkBindingEnum:
enum: enum:
- no_binding - no_binding
@ -54501,6 +54505,8 @@ components:
default_relay_state: default_relay_state:
type: string type: string
description: Default relay_state value for IDP-initiated logins description: Default relay_state value for IDP-initiated logins
default_name_id_policy:
$ref: '#/components/schemas/SAMLNameIDPolicyEnum'
PatchedSAMLSourcePropertyMappingRequest: PatchedSAMLSourcePropertyMappingRequest:
type: object type: object
description: SAMLSourcePropertyMapping Serializer description: SAMLSourcePropertyMapping Serializer
@ -54594,7 +54600,7 @@ components:
be a security risk, as no validation of the request ID is done. be a security risk, as no validation of the request ID is done.
name_id_policy: name_id_policy:
allOf: allOf:
- $ref: '#/components/schemas/NameIdPolicyEnum' - $ref: '#/components/schemas/SAMLNameIDPolicyEnum'
description: NameID Policy sent to the IdP. Can be unset, in which case description: NameID Policy sent to the IdP. Can be unset, in which case
no Policy is sent. no Policy is sent.
binding_type: binding_type:
@ -57305,6 +57311,15 @@ components:
required: required:
- download_url - download_url
- metadata - metadata
SAMLNameIDPolicyEnum:
enum:
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
type: string
SAMLPropertyMapping: SAMLPropertyMapping:
type: object type: object
description: SAMLPropertyMapping Serializer description: SAMLPropertyMapping Serializer
@ -57522,6 +57537,8 @@ components:
default_relay_state: default_relay_state:
type: string type: string
description: Default relay_state value for IDP-initiated logins description: Default relay_state value for IDP-initiated logins
default_name_id_policy:
$ref: '#/components/schemas/SAMLNameIDPolicyEnum'
url_download_metadata: url_download_metadata:
type: string type: string
description: Get metadata download URL description: Get metadata download URL
@ -57694,6 +57711,8 @@ components:
default_relay_state: default_relay_state:
type: string type: string
description: Default relay_state value for IDP-initiated logins description: Default relay_state value for IDP-initiated logins
default_name_id_policy:
$ref: '#/components/schemas/SAMLNameIDPolicyEnum'
required: required:
- acs_url - acs_url
- authorization_flow - authorization_flow
@ -57802,7 +57821,7 @@ components:
be a security risk, as no validation of the request ID is done. be a security risk, as no validation of the request ID is done.
name_id_policy: name_id_policy:
allOf: allOf:
- $ref: '#/components/schemas/NameIdPolicyEnum' - $ref: '#/components/schemas/SAMLNameIDPolicyEnum'
description: NameID Policy sent to the IdP. Can be unset, in which case description: NameID Policy sent to the IdP. Can be unset, in which case
no Policy is sent. no Policy is sent.
binding_type: binding_type:
@ -57992,7 +58011,7 @@ components:
be a security risk, as no validation of the request ID is done. be a security risk, as no validation of the request ID is done.
name_id_policy: name_id_policy:
allOf: allOf:
- $ref: '#/components/schemas/NameIdPolicyEnum' - $ref: '#/components/schemas/SAMLNameIDPolicyEnum'
description: NameID Policy sent to the IdP. Can be unset, in which case description: NameID Policy sent to the IdP. Can be unset, in which case
no Policy is sent. no Policy is sent.
binding_type: binding_type:

View File

@ -7,7 +7,7 @@ services:
network_mode: host network_mode: host
restart: always restart: always
mailpit: mailpit:
image: docker.io/axllent/mailpit:v1.26.0 image: docker.io/axllent/mailpit:v1.26.1
ports: ports:
- 1025:1025 - 1025:1025
- 8025:8025 - 8025:8025

18
uv.lock generated
View File

@ -165,7 +165,7 @@ wheels = [
[[package]] [[package]]
name = "authentik" name = "authentik"
version = "2025.6.1" version = "2025.6.2"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "argon2-cffi" }, { name = "argon2-cffi" },
@ -309,7 +309,7 @@ requires-dist = [
{ name = "packaging", specifier = "==25.0" }, { name = "packaging", specifier = "==25.0" },
{ name = "paramiko", specifier = "==3.5.1" }, { name = "paramiko", specifier = "==3.5.1" },
{ name = "psycopg", extras = ["c", "pool"], specifier = "==3.2.9" }, { name = "psycopg", extras = ["c", "pool"], specifier = "==3.2.9" },
{ name = "pydantic", specifier = "==2.11.5" }, { name = "pydantic", specifier = "==2.11.7" },
{ name = "pydantic-scim", specifier = "==0.0.8" }, { name = "pydantic-scim", specifier = "==0.0.8" },
{ name = "pyjwt", specifier = "==2.10.1" }, { name = "pyjwt", specifier = "==2.10.1" },
{ name = "pyrad", specifier = "==2.4" }, { name = "pyrad", specifier = "==2.4" },
@ -329,7 +329,7 @@ requires-dist = [
{ name = "urllib3", specifier = "<3" }, { name = "urllib3", specifier = "<3" },
{ name = "uvicorn", extras = ["standard"], specifier = "==0.34.3" }, { name = "uvicorn", extras = ["standard"], specifier = "==0.34.3" },
{ name = "watchdog", specifier = "==6.0.0" }, { name = "watchdog", specifier = "==6.0.0" },
{ name = "webauthn", specifier = "==2.5.2" }, { name = "webauthn", specifier = "==2.6.0" },
{ name = "wsproto", specifier = "==1.2.0" }, { name = "wsproto", specifier = "==1.2.0" },
{ name = "xmlsec", specifier = "==1.3.15" }, { name = "xmlsec", specifier = "==1.3.15" },
{ name = "zxcvbn", specifier = "==4.5.0" }, { name = "zxcvbn", specifier = "==4.5.0" },
@ -2463,7 +2463,7 @@ wheels = [
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "2.11.5" version = "2.11.7"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "annotated-types" }, { name = "annotated-types" },
@ -2471,9 +2471,9 @@ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions" },
{ name = "typing-inspection" }, { name = "typing-inspection" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" } sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" }, { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" },
] ]
[package.optional-dependencies] [package.optional-dependencies]
@ -3391,7 +3391,7 @@ wheels = [
[[package]] [[package]]
name = "webauthn" name = "webauthn"
version = "2.5.2" version = "2.6.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "asn1crypto" }, { name = "asn1crypto" },
@ -3399,9 +3399,9 @@ dependencies = [
{ name = "cryptography" }, { name = "cryptography" },
{ name = "pyopenssl" }, { name = "pyopenssl" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/8d/92/8d2a4eec83d8e7feacdaad37c6eb6eb922100cecce5c14a41d8069a59a03/webauthn-2.5.2.tar.gz", hash = "sha256:09c13dfc1c68c810f32fa4d89b1d37acb9f9ae9091c9d7019e313be4525a95ef", size = 124114, upload-time = "2025-03-07T19:44:05.243Z" } sdist = { url = "https://files.pythonhosted.org/packages/63/38/5792cb2034673c162a721df0ad65825699516ee0c938a65670ad3cdabf6c/webauthn-2.6.0.tar.gz", hash = "sha256:13cf5b009a64cef569599ffecf24550df1d7c0cd4fbaea870f937148484a80b4", size = 123608, upload-time = "2025-06-16T22:25:26.76Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/fe/f6ae41de9f383439e30b303a67f6f45d2fceabedaedc34c62f74d58c5c73/webauthn-2.5.2-py3-none-any.whl", hash = "sha256:44246e496e617eb5e2f51165046b9f0197fcdf470f69cd6734061a27ba365f8e", size = 71624, upload-time = "2025-03-07T19:44:03.728Z" }, { url = "https://files.pythonhosted.org/packages/56/c5/b1bba7f6a50caca77f37003e098f48f8dc68d990aba8a03ac8376016430b/webauthn-2.6.0-py3-none-any.whl", hash = "sha256:459973eb5780c1f41bec42b682acf587456b185733398a0b99a0714705b79447", size = 71189, upload-time = "2025-06-16T22:25:25.535Z" },
] ]
[[package]] [[package]]

1703
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -93,7 +93,7 @@
"@floating-ui/dom": "^1.6.11", "@floating-ui/dom": "^1.6.11",
"@formatjs/intl-listformat": "^7.7.11", "@formatjs/intl-listformat": "^7.7.11",
"@fortawesome/fontawesome-free": "^6.7.2", "@fortawesome/fontawesome-free": "^6.7.2",
"@goauthentik/api": "^2025.6.1-1749515784", "@goauthentik/api": "^2025.6.2-1750112513",
"@lit/context": "^1.1.2", "@lit/context": "^1.1.2",
"@lit/localize": "^0.12.2", "@lit/localize": "^0.12.2",
"@lit/reactive-element": "^2.0.4", "@lit/reactive-element": "^2.0.4",
@ -102,10 +102,9 @@
"@open-wc/lit-helpers": "^0.7.0", "@open-wc/lit-helpers": "^0.7.0",
"@patternfly/elements": "^4.1.0", "@patternfly/elements": "^4.1.0",
"@patternfly/patternfly": "^4.224.2", "@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^9.28.1", "@sentry/browser": "^9.30.0",
"@spotlightjs/spotlight": "^3.0.0", "@spotlightjs/spotlight": "^3.0.0",
"@webcomponents/webcomponentsjs": "^2.8.0", "@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1",
"change-case": "^5.4.4", "change-case": "^5.4.4",
"chart.js": "^4.4.9", "chart.js": "^4.4.9",
"chartjs-adapter-date-fns": "^3.0.0", "chartjs-adapter-date-fns": "^3.0.0",
@ -137,6 +136,7 @@
"trusted-types": "^2.0.0", "trusted-types": "^2.0.0",
"ts-pattern": "^5.7.1", "ts-pattern": "^5.7.1",
"unist-util-visit": "^5.0.0", "unist-util-visit": "^5.0.0",
"webauthn-polyfills": "^0.1.7",
"webcomponent-qr-code": "^1.2.0", "webcomponent-qr-code": "^1.2.0",
"yaml": "^2.8.0" "yaml": "^2.8.0"
}, },
@ -170,16 +170,16 @@
"@types/react-dom": "^19.1.5", "@types/react-dom": "^19.1.5",
"@typescript-eslint/eslint-plugin": "^8.8.0", "@typescript-eslint/eslint-plugin": "^8.8.0",
"@typescript-eslint/parser": "^8.8.0", "@typescript-eslint/parser": "^8.8.0",
"@wdio/browser-runner": "9.4", "@wdio/browser-runner": "9.15",
"@wdio/cli": "9.4", "@wdio/cli": "9.15",
"@wdio/spec-reporter": "^9.1.2", "@wdio/spec-reporter": "^9.15.0",
"@web/test-runner": "^0.20.2", "@web/test-runner": "^0.20.2",
"chromedriver": "^136.0.3", "chromedriver": "^136.0.3",
"esbuild": "^0.25.5", "esbuild": "^0.25.5",
"esbuild-plugin-copy": "^2.1.1", "esbuild-plugin-copy": "^2.1.1",
"esbuild-plugin-polyfill-node": "^0.3.0", "esbuild-plugin-polyfill-node": "^0.3.0",
"esbuild-plugins-node-modules-polyfill": "^1.7.0", "esbuild-plugins-node-modules-polyfill": "^1.7.0",
"eslint": "^9.28.0", "eslint": "^9.29.0",
"eslint-plugin-lit": "^2.1.1", "eslint-plugin-lit": "^2.1.1",
"eslint-plugin-wc": "^3.0.1", "eslint-plugin-wc": "^3.0.1",
"github-slugger": "^2.0.0", "github-slugger": "^2.0.0",
@ -194,7 +194,7 @@
"storybook-addon-mock": "^5.0.0", "storybook-addon-mock": "^5.0.0",
"turnstile-types": "^1.2.3", "turnstile-types": "^1.2.3",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"typescript-eslint": "^8.34.0", "typescript-eslint": "^8.34.1",
"vite-plugin-lit-css": "^2.0.0", "vite-plugin-lit-css": "^2.0.0",
"vite-tsconfig-paths": "^5.0.1", "vite-tsconfig-paths": "^5.0.1",
"wireit": "^0.14.12" "wireit": "^0.14.12"

View File

@ -14,7 +14,7 @@ declare module "module" {
* const relativeDirname = dirname(fileURLToPath(import.meta.url)); * const relativeDirname = dirname(fileURLToPath(import.meta.url));
* ``` * ```
*/ */
// eslint-disable-next-line no-var
var __dirname: string; var __dirname: string;
} }
} }

View File

@ -11,11 +11,11 @@
}, },
"dependencies": { "dependencies": {
"@goauthentik/api": "^2024.6.0-1719577139", "@goauthentik/api": "^2024.6.0-1719577139",
"base64-js": "^1.5.1",
"bootstrap": "^4.6.1", "bootstrap": "^4.6.1",
"formdata-polyfill": "^4.0.10", "formdata-polyfill": "^4.0.10",
"jquery": "^3.7.1", "jquery": "^3.7.1",
"weakmap-polyfill": "^2.0.4" "weakmap-polyfill": "^2.0.4",
"webauthn-polyfills": "^0.1.7"
}, },
"devDependencies": { "devDependencies": {
"@goauthentik/core": "^1.0.0", "@goauthentik/core": "^1.0.0",

View File

@ -1,7 +1,7 @@
import { fromByteArray } from "base64-js";
import "formdata-polyfill"; import "formdata-polyfill";
import $ from "jquery"; import $ from "jquery";
import "weakmap-polyfill"; import "weakmap-polyfill";
import "webauthn-polyfills";
import { import {
type AuthenticatorValidationChallenge, type AuthenticatorValidationChallenge,
@ -257,47 +257,9 @@ class AutosubmitStage extends Stage<AutosubmitChallenge> {
} }
} }
export interface Assertion {
id: string;
rawId: string;
type: string;
registrationClientExtensions: string;
response: {
clientDataJSON: string;
attestationObject: string;
};
}
export interface AuthAssertion {
id: string;
rawId: string;
type: string;
assertionClientExtensions: string;
response: {
clientDataJSON: string;
authenticatorData: string;
signature: string;
userHandle: string | null;
};
}
class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge> { class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge> {
deviceChallenge?: DeviceChallenge; deviceChallenge?: DeviceChallenge;
b64enc(buf: Uint8Array): string {
return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
b64RawEnc(buf: Uint8Array): string {
return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_");
}
u8arr(input: string): Uint8Array {
return Uint8Array.from(atob(input.replace(/_/g, "/").replace(/-/g, "+")), (c) =>
c.charCodeAt(0),
);
}
checkWebAuthnSupport(): boolean { checkWebAuthnSupport(): boolean {
if ("credentials" in navigator) { if ("credentials" in navigator) {
return true; return true;
@ -310,98 +272,6 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
return false; return false;
} }
/**
* Transforms items in the credentialCreateOptions generated on the server
* into byte arrays expected by the navigator.credentials.create() call
*/
transformCredentialCreateOptions(
credentialCreateOptions: PublicKeyCredentialCreationOptions,
userId: string,
): PublicKeyCredentialCreationOptions {
const user = credentialCreateOptions.user;
// Because json can't contain raw bytes, the server base64-encodes the User ID
// So to get the base64 encoded byte array, we first need to convert it to a regular
// string, then a byte array, re-encode it and wrap that in an array.
const stringId = decodeURIComponent(window.atob(userId));
user.id = this.u8arr(this.b64enc(this.u8arr(stringId)));
const challenge = this.u8arr(credentialCreateOptions.challenge.toString());
return Object.assign({}, credentialCreateOptions, {
challenge,
user,
});
}
/**
* Transforms the binary data in the credential into base64 strings
* for posting to the server.
* @param {PublicKeyCredential} newAssertion
*/
transformNewAssertionForServer(newAssertion: PublicKeyCredential): Assertion {
const attObj = new Uint8Array(
(newAssertion.response as AuthenticatorAttestationResponse).attestationObject,
);
const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON);
const rawId = new Uint8Array(newAssertion.rawId);
const registrationClientExtensions = newAssertion.getClientExtensionResults();
return {
id: newAssertion.id,
rawId: this.b64enc(rawId),
type: newAssertion.type,
registrationClientExtensions: JSON.stringify(registrationClientExtensions),
response: {
clientDataJSON: this.b64enc(clientDataJSON),
attestationObject: this.b64enc(attObj),
},
};
}
transformCredentialRequestOptions(
credentialRequestOptions: PublicKeyCredentialRequestOptions,
): PublicKeyCredentialRequestOptions {
const challenge = this.u8arr(credentialRequestOptions.challenge.toString());
const allowCredentials = (credentialRequestOptions.allowCredentials || []).map(
(credentialDescriptor) => {
const id = this.u8arr(credentialDescriptor.id.toString());
return Object.assign({}, credentialDescriptor, { id });
},
);
return Object.assign({}, credentialRequestOptions, {
challenge,
allowCredentials,
});
}
/**
* Encodes the binary data in the assertion into strings for posting to the server.
* @param {PublicKeyCredential} newAssertion
*/
transformAssertionForServer(newAssertion: PublicKeyCredential): AuthAssertion {
const response = newAssertion.response as AuthenticatorAssertionResponse;
const authData = new Uint8Array(response.authenticatorData);
const clientDataJSON = new Uint8Array(response.clientDataJSON);
const rawId = new Uint8Array(newAssertion.rawId);
const sig = new Uint8Array(response.signature);
const assertionClientExtensions = newAssertion.getClientExtensionResults();
return {
id: newAssertion.id,
rawId: this.b64enc(rawId),
type: newAssertion.type,
assertionClientExtensions: JSON.stringify(assertionClientExtensions),
response: {
clientDataJSON: this.b64RawEnc(clientDataJSON),
signature: this.b64RawEnc(sig),
authenticatorData: this.b64RawEnc(authData),
userHandle: null,
},
};
}
render() { render() {
if (this.challenge.deviceChallenges.length === 1) { if (this.challenge.deviceChallenges.length === 1) {
this.deviceChallenge = this.challenge.deviceChallenges[0]; this.deviceChallenge = this.challenge.deviceChallenges[0];
@ -505,8 +375,8 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
`); `);
navigator.credentials navigator.credentials
.get({ .get({
publicKey: this.transformCredentialRequestOptions( publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(
this.deviceChallenge?.challenge as PublicKeyCredentialRequestOptions, this.deviceChallenge?.challenge as PublicKeyCredentialRequestOptionsJSON,
), ),
}) })
.then((assertion) => { .then((assertion) => {
@ -514,15 +384,9 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
throw new Error("No assertion"); throw new Error("No assertion");
} }
try { try {
// we now have an authentication assertion! encode the byte arrays contained
// in the assertion data as strings for posting to the server
const transformedAssertionForServer = this.transformAssertionForServer(
assertion as PublicKeyCredential,
);
// post the assertion to the server for verification. // post the assertion to the server for verification.
this.executor.submit({ this.executor.submit({
webauthn: transformedAssertionForServer, webauthn: (assertion as PublicKeyCredential).toJSON(),
}); });
} catch (err) { } catch (err) {
throw new Error(`Error when validating assertion on server: ${err}`); throw new Error(`Error when validating assertion on server: ${err}`);

View File

@ -88,7 +88,8 @@ export class RecentEventsCard extends Table<Event> {
} }
return super.renderEmpty( return super.renderEmpty(
html`<ak-empty-state header=${msg("No Events found.")}> html`<ak-empty-state
><span slot="header">${msg("No Events found.")}</span>
<div slot="body">${msg("No matching events could be found.")}</div> <div slot="body">${msg("No matching events could be found.")}</div>
</ak-empty-state>`, </ak-empty-state>`,
); );

View File

@ -5,6 +5,7 @@ import { policyEngineModes } from "@goauthentik/admin/policies/PolicyEngineModes
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-file-input"; import "@goauthentik/components/ak-file-input";
import "@goauthentik/components/ak-radio-input"; import "@goauthentik/components/ak-radio-input";
import "@goauthentik/components/ak-slug-input";
import "@goauthentik/components/ak-switch-input"; import "@goauthentik/components/ak-switch-input";
import "@goauthentik/components/ak-text-input"; import "@goauthentik/components/ak-text-input";
import "@goauthentik/components/ak-textarea-input"; import "@goauthentik/components/ak-textarea-input";
@ -130,14 +131,14 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
required required
help=${msg("Application's display Name.")} help=${msg("Application's display Name.")}
></ak-text-input> ></ak-text-input>
<ak-text-input <ak-slug-input
name="slug" name="slug"
value=${ifDefined(this.instance?.slug)} value=${ifDefined(this.instance?.slug)}
label=${msg("Slug")} label=${msg("Slug")}
required required
help=${msg("Internal application name used in URLs.")} help=${msg("Internal application name used in URLs.")}
input-hint="code" input-hint="code"
></ak-text-input> ></ak-slug-input>
<ak-text-input <ak-text-input
name="group" name="group"
value=${ifDefined(this.instance?.group)} value=${ifDefined(this.instance?.group)}

View File

@ -117,13 +117,11 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
?invalid=${this.errors.has("name")} ?invalid=${this.errors.has("name")}
.errorMessages=${errors.name ?? this.errorMessages("name")} .errorMessages=${errors.name ?? this.errorMessages("name")}
help=${msg("Application's display Name.")} help=${msg("Application's display Name.")}
id="ak-application-wizard-details-name"
></ak-text-input> ></ak-text-input>
<ak-slug-input <ak-slug-input
name="slug" name="slug"
value=${ifDefined(app.slug)} value=${ifDefined(app.slug)}
label=${msg("Slug")} label=${msg("Slug")}
source="#ak-application-wizard-details-name"
required required
?invalid=${errors.slug ?? this.errors.has("slug")} ?invalid=${errors.slug ?? this.errors.has("slug")}
.errorMessages=${this.errorMessages("slug")} .errorMessages=${this.errorMessages("slug")}

View File

@ -115,7 +115,8 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
.columns=${COLUMNS} .columns=${COLUMNS}
.content=${[]} .content=${[]}
></ak-select-table> ></ak-select-table>
<ak-empty-state header=${msg("No bound policies.")} icon="pf-icon-module"> <ak-empty-state icon="pf-icon-module"
><span slot="header">${msg("No bound policies.")} </span>
<div slot="body">${msg("No policies are currently bound to this object.")}</div> <div slot="body">${msg("No policies are currently bound to this object.")}</div>
<div slot="primary"> <div slot="primary">
<button <button

View File

@ -135,7 +135,8 @@ export class BoundStagesList extends Table<FlowStageBinding> {
renderEmpty(): TemplateResult { renderEmpty(): TemplateResult {
return super.renderEmpty( return super.renderEmpty(
html`<ak-empty-state header=${msg("No Stages bound")} icon="pf-icon-module"> html`<ak-empty-state icon="pf-icon-module">
<span slot="header">${msg("No Stages bound")}</span>
<div slot="body">${msg("No stages are currently bound to this flow.")}</div> <div slot="body">${msg("No stages are currently bound to this flow.")}</div>
<div slot="primary"> <div slot="primary">
<ak-stage-wizard <ak-stage-wizard

View File

@ -3,6 +3,7 @@ import { DesignationToLabel, LayoutToLabel } from "@goauthentik/admin/flows/util
import { policyEngineModes } from "@goauthentik/admin/policies/PolicyEngineModes"; import { policyEngineModes } from "@goauthentik/admin/policies/PolicyEngineModes";
import { AuthenticationEnum } from "@goauthentik/api/dist/models/AuthenticationEnum"; import { AuthenticationEnum } from "@goauthentik/api/dist/models/AuthenticationEnum";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-slug-input.js";
import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/HorizontalFormElement";
import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
@ -91,17 +92,16 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
/> />
<p class="pf-c-form__helper-text">${msg("Shown as the Title in Flow pages.")}</p> <p class="pf-c-form__helper-text">${msg("Shown as the Title in Flow pages.")}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
<input <ak-slug-input
type="text" name="slug"
value="${ifDefined(this.instance?.slug)}" value=${ifDefined(this.instance?.slug)}
class="pf-c-form-control pf-m-monospace" label=${msg("Slug")}
autocomplete="off" required
spellcheck="false" help=${msg("Visible in the URL.")}
required input-hint="code"
/> ></ak-slug-input>
<p class="pf-c-form__helper-text">${msg("Visible in the URL.")}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Designation")} required name="designation"> <ak-form-element-horizontal label=${msg("Designation")} required name="designation">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.instance?.designation === undefined}> <option value="" ?selected=${this.instance?.designation === undefined}>

View File

@ -198,7 +198,8 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
renderEmpty(): TemplateResult { renderEmpty(): TemplateResult {
return super.renderEmpty( return super.renderEmpty(
html`<ak-empty-state header=${msg("No Policies bound.")} icon="pf-icon-module"> html`<ak-empty-state icon="pf-icon-module"
><span slot="header">${msg("No Policies bound.")}</span>
<div slot="body">${msg("No policies are currently bound to this object.")}</div> <div slot="body">${msg("No policies are currently bound to this object.")}</div>
<div slot="primary"> <div slot="primary">
<ak-policy-wizard <ak-policy-wizard

View File

@ -16,6 +16,7 @@ import {
FlowsInstancesListDesignationEnum, FlowsInstancesListDesignationEnum,
PropertymappingsApi, PropertymappingsApi,
PropertymappingsProviderSamlListRequest, PropertymappingsProviderSamlListRequest,
SAMLNameIDPolicyEnum,
SAMLPropertyMapping, SAMLPropertyMapping,
SAMLProvider, SAMLProvider,
SpBindingEnum, SpBindingEnum,
@ -316,6 +317,54 @@ export function renderForm(
"When using IDP-initiated logins, the relay state will be set to this value.", "When using IDP-initiated logins, the relay state will be set to this value.",
)} )}
></ak-text-input> ></ak-text-input>
<ak-form-element-horizontal
label=${msg("Default NameID Policy")}
required
name="defaultNameIdPolicy"
>
<select class="pf-c-form-control">
<option
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
?selected=${provider?.defaultNameIdPolicy ===
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
>
${msg("Persistent")}
</option>
<option
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
?selected=${provider?.defaultNameIdPolicy ===
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
>
${msg("Email address")}
</option>
<option
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
?selected=${provider?.defaultNameIdPolicy ===
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
>
${msg("Windows")}
</option>
<option
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
?selected=${provider?.defaultNameIdPolicy ===
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
>
${msg("X509 Subject")}
</option>
<option
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
?selected=${provider?.defaultNameIdPolicy ===
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
>
${msg("Transient")}
</option>
</select>
<p class="pf-c-form__helper-text">
${msg(
"Configure the default NameID Policy used by IDP-initiated logins and when an incoming assertion doesn't specify a NameID Policy (also applies when using a custom NameID Mapping).",
)}
</p>
</ak-form-element-horizontal>
<ak-radio-input <ak-radio-input
name="digestAlgorithm" name="digestAlgorithm"

View File

@ -9,6 +9,7 @@ import {
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-secret-text-input.js"; import "@goauthentik/components/ak-secret-text-input.js";
import "@goauthentik/components/ak-secret-textarea-input.js"; import "@goauthentik/components/ak-secret-textarea-input.js";
import "@goauthentik/components/ak-slug-input.js";
import "@goauthentik/components/ak-switch-input"; import "@goauthentik/components/ak-switch-input";
import "@goauthentik/components/ak-text-input"; import "@goauthentik/components/ak-text-input";
import "@goauthentik/components/ak-textarea-input"; import "@goauthentik/components/ak-textarea-input";
@ -87,12 +88,13 @@ export class KerberosSourceForm extends WithCapabilitiesConfig(BaseSourceForm<Ke
value=${ifDefined(this.instance?.name)} value=${ifDefined(this.instance?.name)}
required required
></ak-text-input> ></ak-text-input>
<ak-text-input <ak-slug-input
name="slug" name="slug"
label=${msg("Slug")}
value=${ifDefined(this.instance?.slug)} value=${ifDefined(this.instance?.slug)}
label=${msg("Slug")}
required required
></ak-text-input> input-hint="code"
></ak-slug-input>
<ak-switch-input <ak-switch-input
name="enabled" name="enabled"
?checked=${this.instance?.enabled ?? true} ?checked=${this.instance?.enabled ?? true}

View File

@ -3,6 +3,7 @@ import { placeholderHelperText } from "@goauthentik/admin/helperText";
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm"; import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-secret-text-input.js"; import "@goauthentik/components/ak-secret-text-input.js";
import "@goauthentik/components/ak-slug-input.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/HorizontalFormElement";
@ -54,14 +55,15 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
<input <ak-slug-input
type="text" name="slug"
value="${ifDefined(this.instance?.slug)}" value=${ifDefined(this.instance?.slug)}
class="pf-c-form-control" label=${msg("Slug")}
required required
/> input-hint="code"
</ak-form-element-horizontal> ></ak-slug-input>
<ak-form-element-horizontal name="enabled"> <ak-form-element-horizontal name="enabled">
<label class="pf-c-switch"> <label class="pf-c-switch">
<input <input

View File

@ -10,6 +10,7 @@ import {
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-radio-input"; import "@goauthentik/components/ak-radio-input";
import "@goauthentik/components/ak-secret-textarea-input.js"; import "@goauthentik/components/ak-secret-textarea-input.js";
import "@goauthentik/components/ak-slug-input.js";
import "@goauthentik/elements/CodeMirror"; import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror"; import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
@ -267,16 +268,13 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Slug")} required name="slug"> <ak-slug-input
<input name="slug"
type="text" value=${ifDefined(this.instance?.slug)}
value="${ifDefined(this.instance?.slug)}" label=${msg("Slug")}
class="pf-c-form-control pf-m-monospace" required
autocomplete="off" input-hint="code"
spellcheck="false" ></ak-slug-input>
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="enabled"> <ak-form-element-horizontal name="enabled">
<label class="pf-c-switch"> <label class="pf-c-switch">
<input <input

View File

@ -10,6 +10,7 @@ import {
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { PlexAPIClient, PlexResource, popupCenterScreen } from "@goauthentik/common/helpers/plex"; import { PlexAPIClient, PlexResource, popupCenterScreen } from "@goauthentik/common/helpers/plex";
import { ascii_letters, digits, randomString } from "@goauthentik/common/utils"; import { ascii_letters, digits, randomString } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-slug-input.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider.js"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider.js";
@ -183,14 +184,15 @@ export class PlexSourceForm extends WithCapabilitiesConfig(BaseSourceForm<PlexSo
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
<input <ak-slug-input
type="text" name="slug"
value="${ifDefined(this.instance?.slug)}" value=${ifDefined(this.instance?.slug)}
class="pf-c-form-control" label=${msg("Slug")}
required required
/> input-hint="code"
</ak-form-element-horizontal> ></ak-slug-input>
<ak-form-element-horizontal name="enabled"> <ak-form-element-horizontal name="enabled">
<label class="pf-c-switch"> <label class="pf-c-switch">
<input <input

View File

@ -9,6 +9,7 @@ import {
UserMatchingModeToLabel, UserMatchingModeToLabel,
} from "@goauthentik/admin/sources/oauth/utils"; } from "@goauthentik/admin/sources/oauth/utils";
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-slug-input.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/HorizontalFormElement";
@ -25,7 +26,7 @@ import {
DigestAlgorithmEnum, DigestAlgorithmEnum,
FlowsInstancesListDesignationEnum, FlowsInstancesListDesignationEnum,
GroupMatchingModeEnum, GroupMatchingModeEnum,
NameIdPolicyEnum, SAMLNameIDPolicyEnum,
SAMLSource, SAMLSource,
SignatureAlgorithmEnum, SignatureAlgorithmEnum,
SourcesApi, SourcesApi,
@ -89,14 +90,15 @@ export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm<SAMLSo
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
<input <ak-slug-input
type="text" name="slug"
value="${ifDefined(this.instance?.slug)}" value=${ifDefined(this.instance?.slug)}
class="pf-c-form-control" label=${msg("Slug")}
required required
/> input-hint="code"
</ak-form-element-horizontal> ></ak-slug-input>
<ak-form-element-horizontal name="enabled"> <ak-form-element-horizontal name="enabled">
<label class="pf-c-switch"> <label class="pf-c-switch">
<input <input
@ -351,37 +353,37 @@ export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm<SAMLSo
> >
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option <option
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent} value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
?selected=${this.instance?.nameIdPolicy === ?selected=${this.instance?.nameIdPolicy ===
NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent} SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
> >
${msg("Persistent")} ${msg("Persistent")}
</option> </option>
<option <option
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress} value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
?selected=${this.instance?.nameIdPolicy === ?selected=${this.instance?.nameIdPolicy ===
NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress} SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
> >
${msg("Email address")} ${msg("Email address")}
</option> </option>
<option <option
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName} value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
?selected=${this.instance?.nameIdPolicy === ?selected=${this.instance?.nameIdPolicy ===
NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName} SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
> >
${msg("Windows")} ${msg("Windows")}
</option> </option>
<option <option
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName} value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
?selected=${this.instance?.nameIdPolicy === ?selected=${this.instance?.nameIdPolicy ===
NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName} SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
> >
${msg("X509 Subject")} ${msg("X509 Subject")}
</option> </option>
<option <option
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient} value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
?selected=${this.instance?.nameIdPolicy === ?selected=${this.instance?.nameIdPolicy ===
NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient} SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
> >
${msg("Transient")} ${msg("Transient")}
</option> </option>

View File

@ -1,6 +1,7 @@
import { placeholderHelperText } from "@goauthentik/admin/helperText"; import { placeholderHelperText } from "@goauthentik/admin/helperText";
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm"; import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/components/ak-slug-input.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/HorizontalFormElement";
@ -48,14 +49,15 @@ export class SCIMSourceForm extends BaseSourceForm<SCIMSource> {
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
<input <ak-slug-input
type="text" name="slug"
value="${ifDefined(this.instance?.slug)}" value=${ifDefined(this.instance?.slug)}
class="pf-c-form-control" label=${msg("Slug")}
required required
/> input-hint="code"
</ak-form-element-horizontal> ></ak-slug-input>
<ak-form-element-horizontal name="enabled"> <ak-form-element-horizontal name="enabled">
<div class="pf-c-check"> <div class="pf-c-check">
<input <input

View File

@ -41,14 +41,27 @@ export class InvitationForm extends ModelForm<Invitation, string> {
} }
renderForm(): TemplateResult { renderForm(): TemplateResult {
return html` <ak-form-element-horizontal slugMode label=${msg("Name")} required name="name"> const checkSlug = (ev: InputEvent) => {
if (ev && ev.target && ev.target instanceof HTMLInputElement) {
ev.target.value = (ev.target.value ?? "").replace(/[^a-z0-9-]/g, "");
}
};
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
<input <input
type="text" type="text"
id="admin-stages-invitation-name"
value="${this.instance?.name || ""}" value="${this.instance?.name || ""}"
class="pf-c-form-control" class="pf-c-form-control"
required required
@input=${(ev: InputEvent) => checkSlug(ev)}
data-ak-slug="true" data-ak-slug="true"
/> />
<p class="pf-c-form__helper-text">
${msg(
"The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.",
)}
</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Expires")} required name="expires"> <ak-form-element-horizontal label=${msg("Expires")} required name="expires">
<input <input

View File

@ -1,21 +1,5 @@
import * as base64js from "base64-js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
export function b64enc(buf: Uint8Array): string {
return base64js.fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
export function b64RawEnc(buf: Uint8Array): string {
return base64js.fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_");
}
export function u8arr(input: string): Uint8Array {
return Uint8Array.from(atob(input.replace(/_/g, "/").replace(/-/g, "+")), (c) =>
c.charCodeAt(0),
);
}
export function checkWebAuthnSupport() { export function checkWebAuthnSupport() {
if ("credentials" in navigator) { if ("credentials" in navigator) {
return; return;
@ -25,121 +9,3 @@ export function checkWebAuthnSupport() {
} }
throw new Error(msg("WebAuthn not supported by browser.")); throw new Error(msg("WebAuthn not supported by browser."));
} }
/**
* Transforms items in the credentialCreateOptions generated on the server
* into byte arrays expected by the navigator.credentials.create() call
*/
export function transformCredentialCreateOptions(
credentialCreateOptions: PublicKeyCredentialCreationOptions,
userId: string,
): PublicKeyCredentialCreationOptions {
const user = credentialCreateOptions.user;
// Because json can't contain raw bytes, the server base64-encodes the User ID
// So to get the base64 encoded byte array, we first need to convert it to a regular
// string, then a byte array, re-encode it and wrap that in an array.
const stringId = decodeURIComponent(window.atob(userId));
user.id = u8arr(b64enc(u8arr(stringId)));
const challenge = u8arr(credentialCreateOptions.challenge.toString());
return {
...credentialCreateOptions,
challenge,
user,
};
}
export interface Assertion {
id: string;
rawId: string;
type: string;
registrationClientExtensions: string;
response: {
clientDataJSON: string;
attestationObject: string;
};
}
/**
* Transforms the binary data in the credential into base64 strings
* for posting to the server.
* @param {PublicKeyCredential} newAssertion
*/
export function transformNewAssertionForServer(newAssertion: PublicKeyCredential): Assertion {
const attObj = new Uint8Array(
(newAssertion.response as AuthenticatorAttestationResponse).attestationObject,
);
const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON);
const rawId = new Uint8Array(newAssertion.rawId);
const registrationClientExtensions = newAssertion.getClientExtensionResults();
return {
id: newAssertion.id,
rawId: b64enc(rawId),
type: newAssertion.type,
registrationClientExtensions: JSON.stringify(registrationClientExtensions),
response: {
clientDataJSON: b64enc(clientDataJSON),
attestationObject: b64enc(attObj),
},
};
}
export function transformCredentialRequestOptions(
credentialRequestOptions: PublicKeyCredentialRequestOptions,
): PublicKeyCredentialRequestOptions {
const challenge = u8arr(credentialRequestOptions.challenge.toString());
const allowCredentials = (credentialRequestOptions.allowCredentials || []).map(
(credentialDescriptor) => {
const id = u8arr(credentialDescriptor.id.toString());
return Object.assign({}, credentialDescriptor, { id });
},
);
return {
...credentialRequestOptions,
challenge,
allowCredentials,
};
}
export interface AuthAssertion {
id: string;
rawId: string;
type: string;
assertionClientExtensions: string;
response: {
clientDataJSON: string;
authenticatorData: string;
signature: string;
userHandle: string | null;
};
}
/**
* Encodes the binary data in the assertion into strings for posting to the server.
* @param {PublicKeyCredential} newAssertion
*/
export function transformAssertionForServer(newAssertion: PublicKeyCredential): AuthAssertion {
const response = newAssertion.response as AuthenticatorAssertionResponse;
const authData = new Uint8Array(response.authenticatorData);
const clientDataJSON = new Uint8Array(response.clientDataJSON);
const rawId = new Uint8Array(newAssertion.rawId);
const sig = new Uint8Array(response.signature);
const assertionClientExtensions = newAssertion.getClientExtensionResults();
return {
id: newAssertion.id,
rawId: b64enc(rawId),
type: newAssertion.type,
assertionClientExtensions: JSON.stringify(assertionClientExtensions),
response: {
clientDataJSON: b64RawEnc(clientDataJSON),
signature: b64RawEnc(sig),
authenticatorData: b64RawEnc(authData),
userHandle: null,
},
};
}

View File

@ -1,5 +1,6 @@
import { me } from "@goauthentik/common/users.js"; import { me } from "@goauthentik/common/users.js";
import { isUserRoute } from "@goauthentik/elements/router/utils.js"; import { isUserRoute } from "@goauthentik/elements/router/utils.js";
import { deepmerge, deepmergeInto } from "deepmerge-ts";
import { UiThemeEnum, UserSelf } from "@goauthentik/api"; import { UiThemeEnum, UserSelf } from "@goauthentik/api";
import { CurrentBrand } from "@goauthentik/api"; import { CurrentBrand } from "@goauthentik/api";
@ -96,13 +97,12 @@ export class DefaultUIConfig implements UIConfig {
let globalUiConfig: Promise<UIConfig>; let globalUiConfig: Promise<UIConfig>;
export function getConfigForUser(user: UserSelf): UIConfig { export function getConfigForUser(user: UserSelf): UIConfig {
const settings = user.settings; const settings = user.settings as UIConfig;
let config = new DefaultUIConfig(); const config = new DefaultUIConfig();
if (!settings) { if (!settings) {
return config; return config;
} }
config = Object.assign(new DefaultUIConfig(), settings); return deepmerge({ ...config }, settings);
return config;
} }
export function uiConfig(): Promise<UIConfig> { export function uiConfig(): Promise<UIConfig> {

View File

@ -1,4 +1,5 @@
import { formatSlug } from "@goauthentik/elements/router/utils.js"; import { bound } from "@goauthentik/elements/decorators/bound.js";
import { kebabCase } from "change-case";
import { html } from "lit"; import { html } from "lit";
import { customElement, property, query } from "lit/decorators.js"; import { customElement, property, query } from "lit/decorators.js";
@ -6,59 +7,83 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { HorizontalLightComponent } from "./HorizontalLightComponent"; import { HorizontalLightComponent } from "./HorizontalLightComponent";
const slugify = (s: string) => kebabCase(s, { suffixCharacters: "-" });
/**
* @element ak-slug-input
* @class AkSlugInput
*
* A wrapper around `ak-form-element-horizontal` and a text input control that listens for input on
* a peer text input control and automatically mirrors that control's value, transforming the value
* into a slug and displaying it separately.
*
* If the user manually changes the slug, mirroring and transformation stop. If, after that, both
* fields are cleared manually, mirroring and transformation resume.
*
* ## Limitations:
*
* Both the source text field and the slug field must be rendered in the same render pass (i.e.,
* part of the same singular call to a `render` function) so that the slug field can find its
* source.
*
* For the same reason, both the source text field and the slug field must share the same immediate
* parent DOM object.
*
* Since we expect the source text field and the slug to be part of the same form and rendered not
* just in the same form but in the same form group, these are not considered burdensome
* restrictions.
*/
@customElement("ak-slug-input") @customElement("ak-slug-input")
export class AkSlugInput extends HorizontalLightComponent<string> { export class AkSlugInput extends HorizontalLightComponent<string> {
@property({ type: String, reflect: true }) /**
value = ""; * A selector indicating the source text input control. Must be unique within the whole DOM
* context of the slug and source controls. The most common use in authentik is the default:
* slugifying the "name" of something.
*/
@property({ type: String }) @property({ type: String })
source = ""; public source = "[name='name']";
origin?: HTMLInputElement | null; @property({ type: String, reflect: true })
public value = "";
@query("input") @query("input")
input!: HTMLInputElement; private input!: HTMLInputElement;
touched: boolean = false; #origin?: HTMLInputElement | null;
constructor() { #touched: boolean = false;
super();
this.slugify = this.slugify.bind(this);
this.handleTouch = this.handleTouch.bind(this);
}
firstUpdated() {
this.input.addEventListener("input", this.handleTouch);
}
// Do not stop propagation of this event; it must be sent up the tree so that a parent // Do not stop propagation of this event; it must be sent up the tree so that a parent
// component, such as a custom forms manager, may receive it. // component, such as a custom forms manager, may receive it.
handleTouch(ev: Event) { protected handleTouch(ev: Event) {
this.input.value = formatSlug(this.input.value); this.value = this.input.value = slugify(this.input.value);
this.value = this.input.value;
if (this.origin && this.origin.value === "" && this.input.value === "") { // Reset 'touched' status if the slug & target have been reset
this.touched = false; if (this.#origin && this.#origin.value === "" && this.input.value === "") {
this.#touched = false;
return; return;
} }
if (ev && ev.target && ev.target instanceof HTMLInputElement) { if (ev && ev.target && ev.target instanceof HTMLInputElement) {
this.touched = true; this.#touched = true;
} }
} }
slugify(ev: Event) { @bound
protected slugify(ev: Event) {
if (!(ev && ev.target && ev.target instanceof HTMLInputElement)) { if (!(ev && ev.target && ev.target instanceof HTMLInputElement)) {
return; return;
} }
// Reset 'touched' status if the slug & target have been reset // Reset 'touched' status if the slug & target have been reset
if (ev.target.value === "" && this.input.value === "") { if (ev.target.value === "" && this.input.value === "") {
this.touched = false; this.#touched = false;
} }
// Don't proceed if the user has hand-modified the slug // Don't proceed if the user has hand-modified the slug. (Note the order of statements: if
if (this.touched) { // the user hand modified the slug to be empty as part of resetting the slug/source
// relationship, that's a "not-touched" condition and falls through.)
if (this.#touched) {
return; return;
} }
@ -67,7 +92,7 @@ export class AkSlugInput extends HorizontalLightComponent<string> {
// "any event which adds or removes a character but leaves the rest of the slug looking like // "any event which adds or removes a character but leaves the rest of the slug looking like
// the previous iteration, set it to the current iteration." // the previous iteration, set it to the current iteration."
const newSlug = formatSlug(ev.target.value); const newSlug = slugify(ev.target.value);
const oldSlug = this.input.value; const oldSlug = this.input.value;
const [shorter, longer] = const [shorter, longer] =
newSlug.length < oldSlug.length ? [newSlug, oldSlug] : [oldSlug, newSlug]; newSlug.length < oldSlug.length ? [newSlug, oldSlug] : [oldSlug, newSlug];
@ -81,7 +106,6 @@ export class AkSlugInput extends HorizontalLightComponent<string> {
// to listeners, both the name and value of the host must match those of the target // to listeners, both the name and value of the host must match those of the target
// input. The name is already handled since it's both required and automatically // input. The name is already handled since it's both required and automatically
// forwarded to our templated input, but the value must also be set. // forwarded to our templated input, but the value must also be set.
this.value = this.input.value = newSlug; this.value = this.input.value = newSlug;
this.dispatchEvent( this.dispatchEvent(
new Event("input", { new Event("input", {
@ -91,38 +115,36 @@ export class AkSlugInput extends HorizontalLightComponent<string> {
); );
} }
connectedCallback() { public override disconnectedCallback() {
super.connectedCallback(); if (this.#origin) {
this.#origin.removeEventListener("input", this.slugify);
// Set up listener on source element, so we can slugify the content.
setTimeout(() => {
if (this.source) {
const rootNode = this.getRootNode();
if (rootNode instanceof ShadowRoot || rootNode instanceof Document) {
this.origin = rootNode.querySelector(this.source);
}
if (this.origin) {
this.origin.addEventListener("input", this.slugify);
}
}
}, 0);
}
disconnectedCallback() {
if (this.origin) {
this.origin.removeEventListener("input", this.slugify);
} }
super.disconnectedCallback(); super.disconnectedCallback();
} }
renderControl() { public override renderControl() {
return html`<input return html`<input
@input=${(ev: Event) => this.handleTouch(ev)}
type="text" type="text"
value=${ifDefined(this.value)} value=${ifDefined(this.value)}
class="pf-c-form-control" class="pf-c-form-control"
?required=${this.required} ?required=${this.required}
/>`; />`;
} }
public override firstUpdated() {
if (!this.source) {
return;
}
const rootNode = this.getRootNode();
if (rootNode instanceof ShadowRoot || rootNode instanceof Document) {
this.#origin = rootNode.querySelector(this.source);
}
if (this.#origin) {
this.#origin.addEventListener("input", this.slugify);
}
}
} }
export default AkSlugInput; export default AkSlugInput;

View File

@ -94,7 +94,8 @@ export class ObjectChangelog extends Table<Event> {
renderEmpty(): TemplateResult { renderEmpty(): TemplateResult {
return super.renderEmpty( return super.renderEmpty(
html`<ak-empty-state header=${msg("No Events found.")}> html`<ak-empty-state
><span slot="header">${msg("No Events found.")}</span>
<div slot="body">${msg("No matching events could be found.")}</div> <div slot="body">${msg("No matching events could be found.")}</div>
</ak-empty-state>`, </ak-empty-state>`,
); );

View File

@ -66,7 +66,8 @@ export class UserEvents extends Table<Event> {
renderEmpty(): TemplateResult { renderEmpty(): TemplateResult {
return super.renderEmpty( return super.renderEmpty(
html`<ak-empty-state header=${msg("No Events found.")}> html`<ak-empty-state
><span slot="header">${msg("No Events found.")}</span>
<div slot="body">${msg("No matching events could be found.")}</div> <div slot="body">${msg("No matching events could be found.")}</div>
</ak-empty-state>`, </ak-empty-state>`,
); );

View File

@ -3,6 +3,7 @@ import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/Spinner"; import "@goauthentik/elements/Spinner";
import { type SlottedTemplateResult, type Spread } from "@goauthentik/elements/types"; import { type SlottedTemplateResult, type Spread } from "@goauthentik/elements/types";
import { spread } from "@open-wc/lit-helpers"; import { spread } from "@open-wc/lit-helpers";
import { SlotController } from "@patternfly/pfe-core/controllers/slot-controller.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { css, html, nothing } from "lit"; import { css, html, nothing } from "lit";
@ -33,6 +34,8 @@ export class EmptyState extends AKElement implements IEmptyState {
@property() @property()
header?: string; header?: string;
slots = new SlotController(this, "header", "body", "primary");
static get styles() { static get styles() {
return [ return [
PFBase, PFBase,
@ -48,6 +51,12 @@ export class EmptyState extends AKElement implements IEmptyState {
} }
render() { render() {
const showHeader = this.loading || this.slots.hasSlotted("header");
const header = () =>
this.slots.hasSlotted("header")
? html`<slot name="header"></slot>`
: html`<span>${msg("Loading")}</span>`;
return html`<div class="pf-c-empty-state ${this.fullHeight && "pf-m-full-height"}"> return html`<div class="pf-c-empty-state ${this.fullHeight && "pf-m-full-height"}">
<div class="pf-c-empty-state__content"> <div class="pf-c-empty-state__content">
${this.loading ${this.loading
@ -59,15 +68,17 @@ export class EmptyState extends AKElement implements IEmptyState {
"fa-question-circle"} pf-c-empty-state__icon" "fa-question-circle"} pf-c-empty-state__icon"
aria-hidden="true" aria-hidden="true"
></i>`} ></i>`}
<h1 class="pf-c-title pf-m-lg"> ${showHeader ? html` <h1 class="pf-c-title pf-m-lg">${header()}</h1>` : nothing}
${this.loading && this.header === undefined ? msg("Loading") : this.header} ${this.slots.hasSlotted("body")
</h1> ? html` <div class="pf-c-empty-state__body">
<div class="pf-c-empty-state__body"> <slot name="body"></slot>
<slot name="body"></slot> </div>`
</div> : nothing}
<div class="pf-c-empty-state__primary"> ${this.slots.hasSlotted("primary")
<slot name="primary"></slot> ? html` <div class="pf-c-empty-state__primary">
</div> <slot name="primary"></slot>
</div>`
: nothing}
</div> </div>
</div>`; </div>`;
} }

View File

@ -200,7 +200,8 @@ export abstract class AKChart<T> extends AKElement {
<div class="container"> <div class="container">
${this.error ${this.error
? html` ? html`
<ak-empty-state header="${msg("Failed to fetch data.")}" icon="fa-times"> <ak-empty-state icon="fa-times"
><span slot="header">${msg("Failed to fetch data.")}</span>
<p slot="body">${pluckErrorDetail(this.error)}</p> <p slot="body">${pluckErrorDetail(this.error)}</p>
</ak-empty-state> </ak-empty-state>
` `

View File

@ -40,7 +40,9 @@ export class LogViewer extends Table<LogEvent> {
renderEmpty(): TemplateResult { renderEmpty(): TemplateResult {
return super.renderEmpty( return super.renderEmpty(
html`<ak-empty-state header=${msg("No log messages.")}> </ak-empty-state>`, html`<ak-empty-state
><span slot="header">${msg("No log messages.")}</span>
</ak-empty-state>`,
); );
} }

View File

@ -7,7 +7,6 @@ import { AKElement } from "@goauthentik/elements/Base";
import { HorizontalFormElement } from "@goauthentik/elements/forms/HorizontalFormElement"; import { HorizontalFormElement } from "@goauthentik/elements/forms/HorizontalFormElement";
import { PreventFormSubmit } from "@goauthentik/elements/forms/helpers"; import { PreventFormSubmit } from "@goauthentik/elements/forms/helpers";
import { showMessage } from "@goauthentik/elements/messages/MessageContainer"; import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
import { formatSlug } from "@goauthentik/elements/router/utils.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit"; import { CSSResult, TemplateResult, css, html } from "lit";
@ -197,39 +196,6 @@ export abstract class Form<T> extends AKElement {
return this.successMessage; return this.successMessage;
} }
/**
* After rendering the form, if there is both a `name` and `slug` element within the form,
* events the `name` element so that the slug will always have a slugified version of the
* `name.`. This duplicates functionality within ak-form-element-horizontal.
*/
updated(): void {
this.shadowRoot
?.querySelectorAll("ak-form-element-horizontal[name=name]")
.forEach((nameInput) => {
const input = nameInput.firstElementChild as HTMLInputElement;
const form = nameInput.closest("form");
if (form === null) {
return;
}
const slugFieldWrapper = form.querySelector(
"ak-form-element-horizontal[name=slug]",
);
if (!slugFieldWrapper) {
return;
}
const slugField = slugFieldWrapper.firstElementChild as HTMLInputElement;
// Only attach handler if the slug is already equal to the name
// if not, they are probably completely different and shouldn't update
// each other
if (formatSlug(input.value) !== slugField.value) {
return;
}
nameInput.addEventListener("input", () => {
slugField.value = formatSlug(input.value);
});
});
}
resetForm(): void { resetForm(): void {
const form = this.shadowRoot?.querySelector<HTMLFormElement>("form"); const form = this.shadowRoot?.querySelector<HTMLFormElement>("form");
form?.reset(); form?.reset();

View File

@ -77,9 +77,6 @@ export class HorizontalFormElement extends AKElement {
@property({ attribute: false }) @property({ attribute: false })
errorMessages: string[] | string[][] = []; errorMessages: string[] | string[][] = [];
@property({ type: Boolean })
slugMode = false;
_invalid = false; _invalid = false;
/* If this property changes, we want to make sure the parent control is "opened" so /* If this property changes, we want to make sure the parent control is "opened" so
@ -109,13 +106,6 @@ export class HorizontalFormElement extends AKElement {
this.querySelectorAll<HTMLInputElement>("input[autofocus]").forEach((input) => { this.querySelectorAll<HTMLInputElement>("input[autofocus]").forEach((input) => {
input.focus(); input.focus();
}); });
if (this.name === "slug" || this.slugMode) {
this.querySelectorAll<HTMLInputElement>("input[type='text']").forEach((input) => {
input.addEventListener("keyup", () => {
input.value = formatSlug(input.value);
});
});
}
this.querySelectorAll("*").forEach((input) => { this.querySelectorAll("*").forEach((input) => {
if (isAkControl(input) && !input.getAttribute("name")) { if (isAkControl(input) && !input.getAttribute("name")) {
input.setAttribute("name", this.name); input.setAttribute("name", this.name);

View File

@ -163,7 +163,8 @@ export class NotificationDrawer extends AKElement {
} }
renderEmpty() { renderEmpty() {
return html`<ak-empty-state header=${msg("No notifications found.")}> return html`<ak-empty-state
><span slot="header">${msg("No notifications found.")}</span>
<div slot="body">${msg("You don't have any notifications currently.")}</div> <div slot="body">${msg("You don't have any notifications currently.")}</div>
</ak-empty-state>`; </ak-empty-state>`;
} }

View File

@ -288,7 +288,9 @@ export abstract class Table<T> extends AKElement implements TableLike {
return html`<tr role="row"> return html`<tr role="row">
<td role="cell" colspan="25"> <td role="cell" colspan="25">
<div class="pf-l-bullseye"> <div class="pf-l-bullseye">
<ak-empty-state loading header=${msg("Loading")}></ak-empty-state> <ak-empty-state loading
><span slot="header">${msg("Loading")}</span></ak-empty-state
>
</div> </div>
</td> </td>
</tr>`; </tr>`;
@ -300,8 +302,9 @@ export abstract class Table<T> extends AKElement implements TableLike {
<td role="cell" colspan="8"> <td role="cell" colspan="8">
<div class="pf-l-bullseye"> <div class="pf-l-bullseye">
${inner ?? ${inner ??
html`<ak-empty-state header="${msg("No objects found.")}" html`<ak-empty-state
><div slot="primary">${this.renderObjectCreate()}</div> ><span slot="header">${msg("No objects found.")}</span> >
<div slot="primary">${this.renderObjectCreate()}</div>
</ak-empty-state>`} </ak-empty-state>`}
</div> </div>
</td> </td>
@ -316,7 +319,8 @@ export abstract class Table<T> extends AKElement implements TableLike {
renderError(): SlottedTemplateResult { renderError(): SlottedTemplateResult {
if (!this.error) return nothing; if (!this.error) return nothing;
return html`<ak-empty-state header="${msg("Failed to fetch objects.")}" icon="fa-ban"> return html`<ak-empty-state icon="fa-ban"
><span slot="header">${msg("Failed to fetch objects.")}</span>
<div slot="body">${pluckErrorDetail(this.error)}</div> <div slot="body">${pluckErrorDetail(this.error)}</div>
</ak-empty-state>`; </ak-empty-state>`;
} }

View File

@ -19,7 +19,11 @@ describe("ak-empty-state", () => {
}); });
it("should render the default loader", async () => { it("should render the default loader", async () => {
render(html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`); render(
html`<ak-empty-state loading
><span slot="header">${msg("Loading")}</span>
</ak-empty-state>`,
);
const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon"); const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon");
await expect(empty).toExist(); await expect(empty).toExist();
@ -29,7 +33,11 @@ describe("ak-empty-state", () => {
}); });
it("should handle standard boolean", async () => { it("should handle standard boolean", async () => {
render(html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`); render(
html`<ak-empty-state loading
><span slot="header">${msg("Loading")}</span>
</ak-empty-state>`,
);
const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon"); const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon");
await expect(empty).toExist(); await expect(empty).toExist();
@ -39,7 +47,11 @@ describe("ak-empty-state", () => {
}); });
it("should render a static empty state", async () => { it("should render a static empty state", async () => {
render(html`<ak-empty-state header=${msg("No messages found")}> </ak-empty-state>`); render(
html`<ak-empty-state
><span slot="header">${msg("No messages found")}</span>
</ak-empty-state>`,
);
const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon"); const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon");
await expect(empty).toExist(); await expect(empty).toExist();
@ -51,7 +63,8 @@ describe("ak-empty-state", () => {
it("should render a slotted message", async () => { it("should render a slotted message", async () => {
render( render(
html`<ak-empty-state header=${msg("No messages found")}> html`<ak-empty-state
><span slot="header">${msg("No messages found")}</span>
<p slot="body">Try again with a different filter</p> <p slot="body">Try again with a different filter</p>
</ak-empty-state>`, </ak-empty-state>`,
); );

View File

@ -1,8 +1,4 @@
import { import { checkWebAuthnSupport } from "@goauthentik/common/helpers/webauthn";
checkWebAuthnSupport,
transformAssertionForServer,
transformCredentialRequestOptions,
} from "@goauthentik/common/helpers/webauthn";
import "@goauthentik/elements/EmptyState"; import "@goauthentik/elements/EmptyState";
import { BaseDeviceStage } from "@goauthentik/flow/stages/authenticator_validate/base"; import { BaseDeviceStage } from "@goauthentik/flow/stages/authenticator_validate/base";
@ -38,12 +34,12 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage<
async authenticate(): Promise<void> { async authenticate(): Promise<void> {
// request the authenticator to create an assertion signature using the // request the authenticator to create an assertion signature using the
// credential private key // credential private key
let assertion; let assertion: PublicKeyCredential;
checkWebAuthnSupport(); checkWebAuthnSupport();
try { try {
assertion = await navigator.credentials.get({ assertion = (await navigator.credentials.get({
publicKey: this.transformedCredentialRequestOptions, publicKey: this.transformedCredentialRequestOptions,
}); })) as PublicKeyCredential;
if (!assertion) { if (!assertion) {
throw new Error("Assertions is empty"); throw new Error("Assertions is empty");
} }
@ -51,17 +47,11 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage<
throw new Error(`Error when creating credential: ${err}`); throw new Error(`Error when creating credential: ${err}`);
} }
// we now have an authentication assertion! encode the byte arrays contained
// in the assertion data as strings for posting to the server
const transformedAssertionForServer = transformAssertionForServer(
assertion as PublicKeyCredential,
);
// post the assertion to the server for verification. // post the assertion to the server for verification.
try { try {
await this.host?.submit( await this.host?.submit(
{ {
webauthn: transformedAssertionForServer, webauthn: assertion.toJSON(),
}, },
{ {
invisible: true, invisible: true,
@ -74,12 +64,10 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage<
updated(changedProperties: PropertyValues<this>) { updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has("challenge") && this.challenge !== undefined) { if (changedProperties.has("challenge") && this.challenge !== undefined) {
// convert certain members of the PublicKeyCredentialRequestOptions into
// byte arrays as expected by the spec.
const credentialRequestOptions = this.deviceChallenge const credentialRequestOptions = this.deviceChallenge
?.challenge as PublicKeyCredentialRequestOptions; ?.challenge as unknown as PublicKeyCredentialRequestOptionsJSON;
this.transformedCredentialRequestOptions = this.transformedCredentialRequestOptions =
transformCredentialRequestOptions(credentialRequestOptions); PublicKeyCredential.parseRequestOptionsFromJSON(credentialRequestOptions);
this.authenticateWrapper(); this.authenticateWrapper();
} }
} }

View File

@ -1,9 +1,4 @@
import { import { checkWebAuthnSupport } from "@goauthentik/common/helpers/webauthn";
Assertion,
checkWebAuthnSupport,
transformCredentialCreateOptions,
transformNewAssertionForServer,
} from "@goauthentik/common/helpers/webauthn";
import "@goauthentik/elements/EmptyState"; import "@goauthentik/elements/EmptyState";
import { BaseStage } from "@goauthentik/flow/stages/base"; import { BaseStage } from "@goauthentik/flow/stages/base";
@ -24,10 +19,6 @@ import {
AuthenticatorWebAuthnChallengeResponseRequest, AuthenticatorWebAuthnChallengeResponseRequest,
} from "@goauthentik/api"; } from "@goauthentik/api";
export interface WebAuthnAuthenticatorRegisterChallengeResponse {
response: Assertion;
}
@customElement("ak-stage-authenticator-webauthn") @customElement("ak-stage-authenticator-webauthn")
export class WebAuthnAuthenticatorRegisterStage extends BaseStage< export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
AuthenticatorWebAuthnChallenge, AuthenticatorWebAuthnChallenge,
@ -68,7 +59,7 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
} }
checkWebAuthnSupport(); checkWebAuthnSupport();
// request the authenticator(s) to create a new credential keypair. // request the authenticator(s) to create a new credential keypair.
let credential; let credential: PublicKeyCredential;
try { try {
credential = (await navigator.credentials.create({ credential = (await navigator.credentials.create({
publicKey: this.publicKeyCredentialCreateOptions, publicKey: this.publicKeyCredentialCreateOptions,
@ -80,16 +71,12 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
throw new Error(msg(str`Error creating credential: ${err}`)); throw new Error(msg(str`Error creating credential: ${err}`));
} }
// we now have a new credential! We now need to encode the byte arrays
// in the credential into strings, for posting to our server.
const newAssertionForServer = transformNewAssertionForServer(credential);
// post the transformed credential data to the server for validation // post the transformed credential data to the server for validation
// and storing the public key // and storing the public key
try { try {
await this.host?.submit( await this.host?.submit(
{ {
response: newAssertionForServer, response: credential.toJSON(),
}, },
{ {
invisible: true, invisible: true,
@ -118,12 +105,10 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
updated(changedProperties: PropertyValues<this>) { updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has("challenge") && this.challenge !== undefined) { if (changedProperties.has("challenge") && this.challenge !== undefined) {
// convert certain members of the PublicKeyCredentialCreateOptions into this.publicKeyCredentialCreateOptions =
// byte arrays as expected by the spec. PublicKeyCredential.parseCreationOptionsFromJSON(
this.publicKeyCredentialCreateOptions = transformCredentialCreateOptions( this.challenge?.registration as PublicKeyCredentialCreationOptionsJSON,
this.challenge?.registration as PublicKeyCredentialCreationOptions, );
this.challenge?.registration.user.id,
);
this.registerWrapper(); this.registerWrapper();
} }
} }

View File

@ -3,6 +3,7 @@ import "construct-style-sheets-polyfill";
import "@webcomponents/webcomponentsjs"; import "@webcomponents/webcomponentsjs";
import "lit/polyfill-support.js"; import "lit/polyfill-support.js";
import "core-js/actual"; import "core-js/actual";
import "webauthn-polyfills";
import "@formatjs/intl-listformat/polyfill"; import "@formatjs/intl-listformat/polyfill";
import "@formatjs/intl-listformat/locale-data/en"; import "@formatjs/intl-listformat/locale-data/en";

2
web/types/node.d.ts vendored
View File

@ -14,7 +14,7 @@ declare module "module" {
* const relativeDirname = dirname(fileURLToPath(import.meta.url)); * const relativeDirname = dirname(fileURLToPath(import.meta.url));
* ``` * ```
*/ */
// eslint-disable-next-line no-var
var __dirname: string; var __dirname: string;
} }
} }

View File

@ -9245,6 +9245,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -7753,6 +7753,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -9305,6 +9305,9 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -9874,6 +9874,9 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -9857,6 +9857,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -9213,6 +9213,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -9117,6 +9117,9 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -9540,6 +9540,9 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -9549,4 +9549,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit> </trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit>
</body></file></xliff> </body></file></xliff>

View File

@ -9632,6 +9632,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -9604,6 +9604,9 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -6368,6 +6368,9 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit> </trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@ -9878,12 +9878,18 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sf9686d31d28fcf7d"> <trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source> <source>Show field content</source>
<target>显示字段内容</target>
</trans-unit> </trans-unit>
<trans-unit id="sb1b05a7573ab618c"> <trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source> <source>Hide field content</source>
<target>隐藏字段内容</target>
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
<target>使用 Plex 重新验证身份</target>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -7453,6 +7453,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -3011,11 +3011,6 @@ doesn't pass when either or both of the selected options are equal or above the
<source>Load servers</source> <source>Load servers</source>
<target>加载服务器</target> <target>加载服务器</target>
</trans-unit>
<trans-unit id="s24f405197ede5ebb">
<source>Re-authenticate with plex</source>
<target>使用 Plex 重新验证身份</target>
</trans-unit> </trans-unit>
<trans-unit id="sc297b2e13c28ecf9"> <trans-unit id="sc297b2e13c28ecf9">
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source> <source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
@ -9880,6 +9875,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sb3d5c0a0501669df"> <trans-unit id="sb3d5c0a0501669df">
<source>Generate New Certificate-Key Pair</source> <source>Generate New Certificate-Key Pair</source>
<target>生成新的证书密钥对</target> <target>生成新的证书密钥对</target>
</trans-unit>
<trans-unit id="sf9686d31d28fcf7d">
<source>Show field content</source>
<target>显示字段内容</target>
</trans-unit>
<trans-unit id="sb1b05a7573ab618c">
<source>Hide field content</source>
<target>隐藏字段内容</target>
</trans-unit>
<trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source>
<target>使用 Plex 重新验证身份</target>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -9192,6 +9192,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="s4f820625804ed29b"> <trans-unit id="s4f820625804ed29b">
<source>Re-authenticate with Plex</source> <source>Re-authenticate with Plex</source>
</trans-unit>
<trans-unit id="s0433d667ea6eec1a">
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -70,9 +70,6 @@ To check if your config has been applied correctly, you can run the following co
- `AUTHENTIK_POSTGRESQL__USER`: Database user - `AUTHENTIK_POSTGRESQL__USER`: Database user
- `AUTHENTIK_POSTGRESQL__PORT`: Database port, defaults to 5432 - `AUTHENTIK_POSTGRESQL__PORT`: Database port, defaults to 5432
- `AUTHENTIK_POSTGRESQL__PASSWORD`: Database password, defaults to the environment variable `POSTGRES_PASSWORD` - `AUTHENTIK_POSTGRESQL__PASSWORD`: Database password, defaults to the environment variable `POSTGRES_PASSWORD`
{/* TODO: Temporarily deactivated feature, see https://github.com/goauthentik/authentik/issues/14320 */}
{/* - `AUTHENTIK_POSTGRESQL__USE_POOL`: Use a [connection pool](https://docs.djangoproject.com/en/stable/ref/databases/#connection-pool) for PostgreSQL connections. Defaults to `false`. :ak-version[2025.4] */}
{/* - `AUTHENTIK_POSTGRESQL__POOL_OPTIONS`: Extra configuration to pass to the [ConnectionPool object](https://www.psycopg.org/psycopg3/docs/api/pool.html#psycopg_pool.ConnectionPool) when it is created. Must be a base64-encoded JSON dictionary. Ignored when `USE_POOL` is set to `false`. :ak-version[2025.4] */}
- `AUTHENTIK_POSTGRESQL__USE_PGBOUNCER`: Adjust configuration to support connection to PgBouncer. Deprecated, see below - `AUTHENTIK_POSTGRESQL__USE_PGBOUNCER`: Adjust configuration to support connection to PgBouncer. Deprecated, see below
- `AUTHENTIK_POSTGRESQL__USE_PGPOOL`: Adjust configuration to support connection to Pgpool. Deprecated, see below - `AUTHENTIK_POSTGRESQL__USE_PGPOOL`: Adjust configuration to support connection to Pgpool. Deprecated, see below
- `AUTHENTIK_POSTGRESQL__SSLMODE`: Strictness of ssl verification. Defaults to `"verify-ca"` - `AUTHENTIK_POSTGRESQL__SSLMODE`: Strictness of ssl verification. Defaults to `"verify-ca"`
@ -85,7 +82,7 @@ To check if your config has been applied correctly, you can run the following co
The PostgreSQL settings `HOST`, `PORT`, `USER`, and `PASSWORD` support hot-reloading. Adding and removing read replicas doesn't support hot-reloading. The PostgreSQL settings `HOST`, `PORT`, `USER`, and `PASSWORD` support hot-reloading. Adding and removing read replicas doesn't support hot-reloading.
- `AUTHENTIK_POSTGRESQL__DEFAULT_SCHEMA`:ak-version[2024.12] - `AUTHENTIK_POSTGRESQL__DEFAULT_SCHEMA` :ak-version[2024.12]
The name of the schema used by default in the database. Defaults to `public`. The name of the schema used by default in the database. Defaults to `public`.

View File

@ -142,6 +142,17 @@ helm upgrade authentik authentik/authentik -f values.yaml --version ^2025.6
- tenants: fix tenant aware celery scheduler (cherry-pick #14921) - tenants: fix tenant aware celery scheduler (cherry-pick #14921)
- web/user: fix user settings flow not loading (cherry-pick #14911) (#14930) - web/user: fix user settings flow not loading (cherry-pick #14911) (#14930)
## Fixed in 2025.6.2
- brands: fix custom_css being escaped (cherry-pick #14994) (#14996)
- core: bump django from 5.1.10 to 5.1.11 (cherry-pick #14997) (#15010)
- core: bump django from 5.1.9 to 5.1.10 (cherry-pick #14951) (#15008)
- internal/outpost: fix incorrect usage of golang SHA API (cherry-pick #14981) (#14982)
- providers/rac: fixes prompt data not being merged with connection_settings (cherry-pick #15037) (#15038)
- stages/email: Only attach logo to email if used (cherry-pick #14835) (#14969)
- web/elements: fix dual select without sortBy (cherry-pick #14977) (#14979)
- web/elements: fix typo in localeComparator (cherry-pick #15054) (#15055)
## API Changes ## API Changes
#### What's New #### What's New

View File

@ -27,3 +27,29 @@ uv run ak create_recovery_key 10 akadmin
``` ```
This will output a link, that can be used to instantly gain access to authentik as the user specified above. The link is valid for amount of years specified above, in this case, 10 years. This will output a link, that can be used to instantly gain access to authentik as the user specified above. The link is valid for amount of years specified above, in this case, 10 years.
## Can't access initial setup flow during installation steps
If you're unable to access the initial setup flow (`/if/flow/initial-setup/`) immediately after installing authentik, first try restarting the containers because this often resolves temporary issues.
However, if the issue persists after restarting, you can directly set the admin password using the following commands:
Docker Compose deployments:
```bash
docker compose exec server ak changepassword akadmin
```
Kubernetes deployments:
```bash
kubectl exec -it deployment/authentik-server -c server -- ak changepassword akadmin
```
After following the prompts to set a new password, you can then login via: `https://authentik.company/if/flow/default-authentication-flow/?next=%2F`
After logging in, you can set the email address and other settings for the account by navigating to **Directory** > **Users** and editing the user account.
:::note
This method bypasses the initial setup flow and should only be used as a last resort. The initial setup flow is the recommended method to configure the administrator user.
:::

View File

@ -49,7 +49,7 @@ After you are connected, execute these commands to create a database backup:
cd /bitnami/postgresql/ cd /bitnami/postgresql/
# Set the PostgreSQL password from environment variable # Set the PostgreSQL password from environment variable
export PGPASSWORD=$POSTGRES_POSTGRES_PASSWORD export PGPASSWORD=$(cat $POSTGRES_PASSWORD_FILE)
# Create a full database dump # Create a full database dump
pg_dump -U $POSTGRES_USER $POSTGRES_DB > /bitnami/postgresql/dump.sql pg_dump -U $POSTGRES_USER $POSTGRES_DB > /bitnami/postgresql/dump.sql
@ -117,7 +117,7 @@ cd /bitnami/postgresql/
ls -lh dump.sql ls -lh dump.sql
# Set the PostgreSQL password # Set the PostgreSQL password
export PGPASSWORD=$POSTGRES_POSTGRES_PASSWORD export PGPASSWORD=$(cat $POSTGRES_PASSWORD_FILE)
# Import the database dump # Import the database dump
psql -U $POSTGRES_USER $POSTGRES_DB < dump.sql psql -U $POSTGRES_USER $POSTGRES_DB < dump.sql

View File

@ -81,7 +81,7 @@ If you want to control user storage and designate Nextcloud administrators, you
- **Create Scope Mapping**: - **Create Scope Mapping**:
- **Name**: `Nextcloud Profile` - **Name**: `Nextcloud Profile`
- **Scope name**: `profile` - **Scope name**: `nextcloud`
- **Expression**: - **Expression**:
```python ```python
@ -146,7 +146,7 @@ Depending on your Nextcloud configuration, you may need to use `https://nextclou
- **Client ID**: Client ID from authentik - **Client ID**: Client ID from authentik
- **Client secret**: Client secret from authentik - **Client secret**: Client secret from authentik
- **Discovery endpoint**: `https://authentik.company/application/o/<application_slug>/.well-known/openid-configuration` - **Discovery endpoint**: `https://authentik.company/application/o/<application_slug>/.well-known/openid-configuration`
- **Scope**: `email profile openid` - **Scope**: `email nextcloud openid`
- Under **Attribute mappings**: - Under **Attribute mappings**:
- **User ID mapping**: `sub` (or `user_id` for existing users) - **User ID mapping**: `sub` (or `user_id` for existing users)
@ -161,6 +161,10 @@ Depending on your Nextcloud configuration, you may need to use `https://nextclou
- **Use unique user ID**: If this option is disabled, Nextcloud will use the mapped user ID as the Federated Cloud ID. - **Use unique user ID**: If this option is disabled, Nextcloud will use the mapped user ID as the Federated Cloud ID.
:::note
If authentik and Nextcloud are running on the same host, you will need to add `'allow_local_remote_servers' => true` to your nextcloud `config.php` file. This setting allows remote servers with local addresses.
:::
:::tip :::tip
To avoid a hashed Federated Cloud ID, deselect **Use unique user ID** and use `user_id` for the User ID mapping. To avoid a hashed Federated Cloud ID, deselect **Use unique user ID** and use `user_id` for the User ID mapping.
::: :::

View File

@ -40,6 +40,7 @@ To support the integration of Zipline with authentik, you need to create an appl
- Note the **Client ID** and **Client Secret** values because they will be required later. - Note the **Client ID** and **Client Secret** values because they will be required later.
- Set a `Strict` redirect URI to `https://zipline.company/api/auth/oauth/oidc`. - Set a `Strict` redirect URI to `https://zipline.company/api/auth/oauth/oidc`.
- Select any available signing key. - Select any available signing key.
- Under **Advanced Protocol Settings** > **Scopes**, add `authentik default OAuth Mapping: OpenID 'offline_access'` to the **Selected Scopes** list.
- **Configure Bindings** _(optional)_: Create a [binding](/docs/add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page. - **Configure Bindings** _(optional)_: Create a [binding](/docs/add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.
3. Click **Submit** to save the new application and provider. 3. Click **Submit** to save the new application and provider.

View File

@ -19,11 +19,10 @@
"@goauthentik/docusaurus-config": "^1.1.0", "@goauthentik/docusaurus-config": "^1.1.0",
"@goauthentik/tsconfig": "^1.0.4", "@goauthentik/tsconfig": "^1.0.4",
"@mdx-js/react": "^3.1.0", "@mdx-js/react": "^3.1.0",
"@swc/html-linux-x64-gnu": "1.12.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"docusaurus-plugin-openapi-docs": "^4.4.0", "docusaurus-plugin-openapi-docs": "^4.4.0",
"docusaurus-theme-openapi-docs": "^4.4.0", "docusaurus-theme-openapi-docs": "^4.4.0",
"postcss": "^8.5.5", "postcss": "^8.5.6",
"prism-react-renderer": "^2.4.1", "prism-react-renderer": "^2.4.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-before-after-slider-component": "^1.1.8", "react-before-after-slider-component": "^1.1.8",
@ -36,26 +35,26 @@
"@docusaurus/module-type-aliases": "^3.7.0", "@docusaurus/module-type-aliases": "^3.7.0",
"@docusaurus/tsconfig": "^3.7.0", "@docusaurus/tsconfig": "^3.7.0",
"@docusaurus/types": "^3.7.0", "@docusaurus/types": "^3.7.0",
"@eslint/js": "^9.28.0", "@eslint/js": "^9.29.0",
"@goauthentik/eslint-config": "^1.0.5", "@goauthentik/eslint-config": "^1.0.5",
"@goauthentik/prettier-config": "^1.0.5", "@goauthentik/prettier-config": "^1.0.5",
"@goauthentik/tsconfig": "^1.0.4", "@goauthentik/tsconfig": "^1.0.4",
"@trivago/prettier-plugin-sort-imports": "^5.2.2", "@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/lodash": "^4.17.17", "@types/lodash": "^4.17.18",
"@types/node": "^24.0.1", "@types/node": "^24.0.3",
"@types/postman-collection": "^3.5.11", "@types/postman-collection": "^3.5.11",
"@types/react": "^18.3.22", "@types/react": "^18.3.22",
"@types/semver": "^7.7.0", "@types/semver": "^7.7.0",
"@typescript-eslint/eslint-plugin": "^8.34.0", "@typescript-eslint/eslint-plugin": "^8.34.1",
"@typescript-eslint/parser": "^8.34.0", "@typescript-eslint/parser": "^8.34.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^9.28.0", "eslint": "^9.29.0",
"fast-glob": "^3.3.3", "fast-glob": "^3.3.3",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"prettier-plugin-packagejson": "^2.5.15", "prettier-plugin-packagejson": "^2.5.15",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"typescript-eslint": "^8.34.0" "typescript-eslint": "^8.34.1"
}, },
"engines": { "engines": {
"node": ">=22.14.0" "node": ">=22.14.0"
@ -4209,9 +4208,9 @@
} }
}, },
"node_modules/@eslint/config-array": { "node_modules/@eslint/config-array": {
"version": "0.20.0", "version": "0.20.1",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz",
"integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@ -4308,9 +4307,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.28.0", "version": "9.29.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz",
"integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", "integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -6581,9 +6580,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/lodash": { "node_modules/@types/lodash": {
"version": "4.17.17", "version": "4.17.18",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.17.tgz", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.18.tgz",
"integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==", "integrity": "sha512-KJ65INaxqxmU6EoCiJmRPZC9H9RVWCRd349tXM2M3O5NA7cY6YL7c0bHAHQ93NOfTObEQ004kd2QVHs/r0+m4g==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@ -6615,9 +6614,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "24.0.1", "version": "24.0.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.3.tgz",
"integrity": "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==", "integrity": "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~7.8.0" "undici-types": "~7.8.0"
@ -6831,17 +6830,17 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz",
"integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", "integrity": "sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.10.0", "@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/scope-manager": "8.34.1",
"@typescript-eslint/type-utils": "8.34.0", "@typescript-eslint/type-utils": "8.34.1",
"@typescript-eslint/utils": "8.34.0", "@typescript-eslint/utils": "8.34.1",
"@typescript-eslint/visitor-keys": "8.34.0", "@typescript-eslint/visitor-keys": "8.34.1",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^7.0.0", "ignore": "^7.0.0",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
@ -6855,7 +6854,7 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
}, },
"peerDependencies": { "peerDependencies": {
"@typescript-eslint/parser": "^8.34.0", "@typescript-eslint/parser": "^8.34.1",
"eslint": "^8.57.0 || ^9.0.0", "eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0" "typescript": ">=4.8.4 <5.9.0"
} }
@ -6871,16 +6870,16 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.1.tgz",
"integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", "integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/scope-manager": "8.34.1",
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/typescript-estree": "8.34.0", "@typescript-eslint/typescript-estree": "8.34.1",
"@typescript-eslint/visitor-keys": "8.34.0", "@typescript-eslint/visitor-keys": "8.34.1",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -6896,14 +6895,14 @@
} }
}, },
"node_modules/@typescript-eslint/project-service": { "node_modules/@typescript-eslint/project-service": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.1.tgz",
"integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", "integrity": "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.34.0", "@typescript-eslint/tsconfig-utils": "^8.34.1",
"@typescript-eslint/types": "^8.34.0", "@typescript-eslint/types": "^8.34.1",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -6918,14 +6917,14 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.1.tgz",
"integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", "integrity": "sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/visitor-keys": "8.34.0" "@typescript-eslint/visitor-keys": "8.34.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -6936,9 +6935,9 @@
} }
}, },
"node_modules/@typescript-eslint/tsconfig-utils": { "node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.1.tgz",
"integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", "integrity": "sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -6953,14 +6952,14 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.1.tgz",
"integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", "integrity": "sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "8.34.0", "@typescript-eslint/typescript-estree": "8.34.1",
"@typescript-eslint/utils": "8.34.0", "@typescript-eslint/utils": "8.34.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"ts-api-utils": "^2.1.0" "ts-api-utils": "^2.1.0"
}, },
@ -6977,9 +6976,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.1.tgz",
"integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", "integrity": "sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -6991,16 +6990,16 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.1.tgz",
"integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", "integrity": "sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/project-service": "8.34.0", "@typescript-eslint/project-service": "8.34.1",
"@typescript-eslint/tsconfig-utils": "8.34.0", "@typescript-eslint/tsconfig-utils": "8.34.1",
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/visitor-keys": "8.34.0", "@typescript-eslint/visitor-keys": "8.34.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@ -7020,9 +7019,9 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "2.0.1", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -7046,16 +7045,16 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.1.tgz",
"integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", "integrity": "sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.7.0", "@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/scope-manager": "8.34.1",
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/typescript-estree": "8.34.0" "@typescript-eslint/typescript-estree": "8.34.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -7070,14 +7069,14 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.1.tgz",
"integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", "integrity": "sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.34.1",
"eslint-visitor-keys": "^4.2.0" "eslint-visitor-keys": "^4.2.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -7291,9 +7290,10 @@
} }
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.14.0", "version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@ -12218,19 +12218,19 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "9.28.0", "version": "9.29.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz",
"integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.20.0", "@eslint/config-array": "^0.20.1",
"@eslint/config-helpers": "^0.2.1", "@eslint/config-helpers": "^0.2.1",
"@eslint/core": "^0.14.0", "@eslint/core": "^0.14.0",
"@eslint/eslintrc": "^3.3.1", "@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.28.0", "@eslint/js": "9.29.0",
"@eslint/plugin-kit": "^0.3.1", "@eslint/plugin-kit": "^0.3.1",
"@humanfs/node": "^0.16.6", "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
@ -12242,9 +12242,9 @@
"cross-spawn": "^7.0.6", "cross-spawn": "^7.0.6",
"debug": "^4.3.2", "debug": "^4.3.2",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.3.0", "eslint-scope": "^8.4.0",
"eslint-visitor-keys": "^4.2.0", "eslint-visitor-keys": "^4.2.1",
"espree": "^10.3.0", "espree": "^10.4.0",
"esquery": "^1.5.0", "esquery": "^1.5.0",
"esutils": "^2.0.2", "esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
@ -12558,9 +12558,9 @@
} }
}, },
"node_modules/eslint/node_modules/eslint-scope": { "node_modules/eslint/node_modules/eslint-scope": {
"version": "8.3.0", "version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
"integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
"devOptional": true, "devOptional": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
@ -12575,9 +12575,9 @@
} }
}, },
"node_modules/eslint/node_modules/eslint-visitor-keys": { "node_modules/eslint/node_modules/eslint-visitor-keys": {
"version": "4.2.0", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
@ -12706,15 +12706,15 @@
} }
}, },
"node_modules/espree": { "node_modules/espree": {
"version": "10.3.0", "version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
"integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
"devOptional": true, "devOptional": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"acorn": "^8.14.0", "acorn": "^8.15.0",
"acorn-jsx": "^5.3.2", "acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.2.0" "eslint-visitor-keys": "^4.2.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -12724,9 +12724,9 @@
} }
}, },
"node_modules/espree/node_modules/eslint-visitor-keys": { "node_modules/espree/node_modules/eslint-visitor-keys": {
"version": "4.2.0", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
@ -20672,9 +20672,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.5", "version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -26387,15 +26387,15 @@
} }
}, },
"node_modules/typescript-eslint": { "node_modules/typescript-eslint": {
"version": "8.34.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.0.tgz", "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.1.tgz",
"integrity": "sha512-MRpfN7uYjTrTGigFCt8sRyNqJFhjN0WwZecldaqhWm+wy0gaRt8Edb/3cuUy0zdq2opJWT6iXINKAtewnDOltQ==", "integrity": "sha512-XjS+b6Vg9oT1BaIUfkW3M3LvqZE++rbzAMEHuccCfO/YkP43ha6w3jTEMilQxMF92nVOYCcdjv1ZUhAa1D/0ow==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/eslint-plugin": "8.34.0", "@typescript-eslint/eslint-plugin": "8.34.1",
"@typescript-eslint/parser": "8.34.0", "@typescript-eslint/parser": "8.34.1",
"@typescript-eslint/utils": "8.34.0" "@typescript-eslint/utils": "8.34.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"

View File

@ -37,7 +37,7 @@
"clsx": "^2.1.1", "clsx": "^2.1.1",
"docusaurus-plugin-openapi-docs": "^4.4.0", "docusaurus-plugin-openapi-docs": "^4.4.0",
"docusaurus-theme-openapi-docs": "^4.4.0", "docusaurus-theme-openapi-docs": "^4.4.0",
"postcss": "^8.5.5", "postcss": "^8.5.6",
"prism-react-renderer": "^2.4.1", "prism-react-renderer": "^2.4.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-before-after-slider-component": "^1.1.8", "react-before-after-slider-component": "^1.1.8",
@ -50,26 +50,26 @@
"@docusaurus/module-type-aliases": "^3.7.0", "@docusaurus/module-type-aliases": "^3.7.0",
"@docusaurus/tsconfig": "^3.7.0", "@docusaurus/tsconfig": "^3.7.0",
"@docusaurus/types": "^3.7.0", "@docusaurus/types": "^3.7.0",
"@eslint/js": "^9.28.0", "@eslint/js": "^9.29.0",
"@goauthentik/eslint-config": "^1.0.5", "@goauthentik/eslint-config": "^1.0.5",
"@goauthentik/prettier-config": "^1.0.5", "@goauthentik/prettier-config": "^1.0.5",
"@goauthentik/tsconfig": "^1.0.4", "@goauthentik/tsconfig": "^1.0.4",
"@trivago/prettier-plugin-sort-imports": "^5.2.2", "@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/lodash": "^4.17.17", "@types/lodash": "^4.17.18",
"@types/node": "^24.0.1", "@types/node": "^24.0.3",
"@types/postman-collection": "^3.5.11", "@types/postman-collection": "^3.5.11",
"@types/react": "^18.3.22", "@types/react": "^18.3.22",
"@types/semver": "^7.7.0", "@types/semver": "^7.7.0",
"@typescript-eslint/eslint-plugin": "^8.34.0", "@typescript-eslint/eslint-plugin": "^8.34.1",
"@typescript-eslint/parser": "^8.34.0", "@typescript-eslint/parser": "^8.34.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^9.28.0", "eslint": "^9.29.0",
"fast-glob": "^3.3.3", "fast-glob": "^3.3.3",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"prettier-plugin-packagejson": "^2.5.15", "prettier-plugin-packagejson": "^2.5.15",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"typescript-eslint": "^8.34.0" "typescript-eslint": "^8.34.1"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rspack/binding-darwin-arm64": "1.3.15", "@rspack/binding-darwin-arm64": "1.3.15",