diff --git a/.all-contributorsrc b/.all-contributorsrc
index 05ca52cb6..97fc3b530 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -23,7 +23,13 @@
"maintenance",
"question",
"review",
- "test"
+ "test",
+ "mentoring",
+ "infra",
+ "design",
+ "userTesting",
+ "tool",
+ "research"
]
},
{
@@ -38,7 +44,13 @@
"maintenance",
"question",
"review",
- "test"
+ "test",
+ "mentoring",
+ "infra",
+ "design",
+ "userTesting",
+ "tool",
+ "research"
]
},
{
@@ -53,7 +65,13 @@
"maintenance",
"question",
"review",
- "test"
+ "test",
+ "mentoring",
+ "infra",
+ "design",
+ "userTesting",
+ "tool",
+ "research"
]
},
{
@@ -649,7 +667,14 @@
"profile": "https://github.com/mind-ar",
"contributions": [
"code",
- "translation"
+ "translation",
+ "doc",
+ "bug",
+ "design",
+ "test",
+ "review",
+ "research",
+ "ideas"
]
},
{
@@ -834,6 +859,63 @@
"contributions": [
"doc"
]
+ },
+ {
+ "login": "chillinPanda",
+ "name": "Dinh Bao Dang",
+ "avatar_url": "https://avatars.githubusercontent.com/u/250694?v=4",
+ "profile": "https://github.com/chillinPanda",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "levkoburburas",
+ "name": "levkoburburas",
+ "avatar_url": "https://avatars.githubusercontent.com/u/62853952?v=4",
+ "profile": "https://github.com/levkoburburas",
+ "contributions": [
+ "code",
+ "ideas",
+ "bug"
+ ]
+ },
+ {
+ "login": "tiuub",
+ "name": "tiuub",
+ "avatar_url": "https://avatars.githubusercontent.com/u/46517077?v=4",
+ "profile": "https://github.com/tiuub",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "joshgordon",
+ "name": "Josh Gordon",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2125341?v=4",
+ "profile": "http://joshgordon.net",
+ "contributions": [
+ "ideas",
+ "security"
+ ]
+ },
+ {
+ "login": "silasfrancisco",
+ "name": "silasfrancisco",
+ "avatar_url": "https://avatars.githubusercontent.com/u/84447762?v=4",
+ "profile": "https://github.com/silasfrancisco",
+ "contributions": [
+ "security"
+ ]
+ },
+ {
+ "login": "n4m3l3ss-b0t",
+ "name": "Ricardo Pesqueira",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1162710?v=4",
+ "profile": "https://github.com/n4m3l3ss-b0t",
+ "contributions": [
+ "security"
+ ]
}
],
"contributorsPerLine": 7
diff --git a/.editorconfig b/.editorconfig
index cc3c38a0c..11e3deb80 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -7,7 +7,7 @@ trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true
-[*.{sh,yml,yaml}]
+[*.{html,sh,yml,yaml}]
indent_style = space
indent_size = 2
diff --git a/.renovaterc b/.renovaterc
index ef5a91e74..23dd0f45c 100644
--- a/.renovaterc
+++ b/.renovaterc
@@ -22,7 +22,7 @@
"npm"
],
"kubernetes": {
- "fileMatch": ["kube/.+\\.yml$"],
+ "fileMatch": ["kube/.+\\.yml$"]
},
"labels": [
"dependencies"
@@ -30,7 +30,6 @@
"packageRules": [
{
"matchUpdateTypes": ["digest", "minor", "patch"],
- "matchCurrentVersion": "!/^0/",
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
diff --git a/Dockerfile b/Dockerfile
index 31728093a..a7a55d1ca 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
# ===================================
# ===== Authelia official image =====
# ===================================
-FROM alpine:3.17.0
+FROM alpine:3.17.1
ARG TARGETOS
ARG TARGETARCH
diff --git a/Dockerfile.coverage b/Dockerfile.coverage
index e37244997..6189a1cb3 100644
--- a/Dockerfile.coverage
+++ b/Dockerfile.coverage
@@ -15,7 +15,7 @@ RUN yarn global add pnpm && \
# =======================================
# ===== Build image for the backend =====
# =======================================
-FROM golang:1.19.4-alpine AS builder-backend
+FROM golang:1.19.5-alpine AS builder-backend
WORKDIR /go/src/app
@@ -46,7 +46,7 @@ RUN \
# ===================================
# ===== Authelia official image =====
# ===================================
-FROM alpine:3.17.0
+FROM alpine:3.17.1
RUN apk --no-cache add ca-certificates tzdata
diff --git a/Dockerfile.dev b/Dockerfile.dev
index bf8408f3f..47791c70b 100644
--- a/Dockerfile.dev
+++ b/Dockerfile.dev
@@ -13,7 +13,7 @@ RUN yarn install --frozen-lockfile && yarn build
# =======================================
# ===== Build image for the backend =====
# =======================================
-FROM golang:1.19.4-alpine AS builder-backend
+FROM golang:1.19.5-alpine AS builder-backend
WORKDIR /go/src/app
@@ -43,7 +43,7 @@ RUN \
# ===================================
# ===== Authelia official image =====
# ===================================
-FROM alpine:3.17.0
+FROM alpine:3.17.1
WORKDIR /app
diff --git a/README.md b/README.md
index 66b643c25..6bd90e958 100644
--- a/README.md
+++ b/README.md
@@ -197,9 +197,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
diff --git a/cmd/authelia-gen/types.go b/cmd/authelia-gen/types.go
index 99e13fd45..479f0410d 100644
--- a/cmd/authelia-gen/types.go
+++ b/cmd/authelia-gen/types.go
@@ -79,7 +79,7 @@ const (
type labelPriority int
-//nolint:deadcode // Kept for future use.
+//nolint:deadcode,varcheck // Kept for future use.
const (
labelPriorityCritical labelPriority = iota
labelPriorityHigh
@@ -122,7 +122,7 @@ func (s labelStatus) String() string {
type labelType int
-//nolint:deadcode // Kept for future use.
+//nolint:deadcode,varcheck // Kept for future use.
const (
labelTypeFeature labelType = iota
labelTypeBugUnconfirmed
diff --git a/cmd/authelia-scripts/cmd/bootstrap.go b/cmd/authelia-scripts/cmd/bootstrap.go
index 0e43531d6..82cd8a0c0 100644
--- a/cmd/authelia-scripts/cmd/bootstrap.go
+++ b/cmd/authelia-scripts/cmd/bootstrap.go
@@ -114,6 +114,30 @@ var hostEntries = []HostEntry{
{Domain: "redis-sentinel-0.example.com", IP: "192.168.240.120"},
{Domain: "redis-sentinel-1.example.com", IP: "192.168.240.121"},
{Domain: "redis-sentinel-2.example.com", IP: "192.168.240.122"},
+
+ // For multi cookie domain tests.
+ {Domain: "login.example2.com", IP: "192.168.240.100"},
+ {Domain: "admin.example2.com", IP: "192.168.240.100"},
+ {Domain: "singlefactor.example2.com", IP: "192.168.240.100"},
+ {Domain: "dev.example2.com", IP: "192.168.240.100"},
+ {Domain: "home.example2.com", IP: "192.168.240.100"},
+ {Domain: "mx1.mail.example2.com", IP: "192.168.240.100"},
+ {Domain: "mx2.mail.example2.com", IP: "192.168.240.100"},
+ {Domain: "public.example2.com", IP: "192.168.240.100"},
+ {Domain: "secure.example2.com", IP: "192.168.240.100"},
+ {Domain: "mail.example2.com", IP: "192.168.240.100"},
+ {Domain: "duo.example2.com", IP: "192.168.240.100"},
+ {Domain: "login.example3.com", IP: "192.168.240.100"},
+ {Domain: "admin.example3.com", IP: "192.168.240.100"},
+ {Domain: "singlefactor.example3.com", IP: "192.168.240.100"},
+ {Domain: "dev.example3.com", IP: "192.168.240.100"},
+ {Domain: "home.example3.com", IP: "192.168.240.100"},
+ {Domain: "mx1.mail.example3.com", IP: "192.168.240.100"},
+ {Domain: "mx2.mail.example3.com", IP: "192.168.240.100"},
+ {Domain: "public.example3.com", IP: "192.168.240.100"},
+ {Domain: "secure.example3.com", IP: "192.168.240.100"},
+ {Domain: "mail.example3.com", IP: "192.168.240.100"},
+ {Domain: "duo.example3.com", IP: "192.168.240.100"},
}
func runCommand(cmd string, args ...string) {
diff --git a/config.template.yml b/config.template.yml
index 6dc5a39eb..0d966b038 100644
--- a/config.template.yml
+++ b/config.template.yml
@@ -662,38 +662,76 @@ access_control:
## The session cookies identify the user once logged in.
## The available providers are: `memory`, `redis`. Memory is the provider unless redis is defined.
session:
- ## The name of the session cookie.
- name: authelia_session
-
- ## The domain to protect.
- ## Note: the authenticator must also be in that domain.
- ## If empty, the cookie is restricted to the subdomain of the issuer.
- domain: example.com
-
- ## Sets the Cookie SameSite value. Possible options are none, lax, or strict.
- ## Please read https://www.authelia.com/c/session#same_site
- same_site: lax
-
## The secret to encrypt the session data. This is only used with Redis / Redis Sentinel.
## Secret can also be set using a secret: https://www.authelia.com/c/secrets
- secret: insecure_session_secret
+ secret: 'insecure_session_secret'
- ## The value for expiration, inactivity, and remember_me_duration are in seconds or the duration notation format.
+ ## Cookies configures the list of allowed cookie domains for sessions to be created on.
+ ## Undefined values will default to the values below.
+ # cookies:
+ # -
+ ## The name of the session cookie.
+ # name: 'authelia_session'
+
+ ## The domain to protect.
+ ## Note: the Authelia portal must also be in that domain.
+ # domain: 'example.com'
+
+ ## Optional. The fully qualified URI of the portal to redirect users to on proxies that support redirections.
+ ## Rules:
+ ## - MUST use the secure scheme 'https://'
+ ## - The above domain MUST either:
+ ## - Match the host portion of this URI.
+ ## - Match the suffix of the host portion when prefixed with '.'.
+ # authelia_url: 'https://auth.example.com'
+
+ ## Sets the Cookie SameSite value. Possible options are none, lax, or strict.
+ ## Please read https://www.authelia.com/c/session#same_site
+ # same_site: 'lax'
+
+ ## The value for inactivity, expiration, and remember_me are in seconds or the duration notation format.
+ ## See: https://www.authelia.com/c/common#duration-notation-format
+ ## All three of these values affect the cookie/session validity period. Longer periods are considered less secure
+ ## because a stolen cookie will last longer giving attackers more time to spy or attack.
+
+ ## The inactivity time before the session is reset. If expiration is set to 1h, and this is set to 5m, if the user
+ ## does not select the remember me option their session will get destroyed after 1h, or after 5m since the last
+ ## time Authelia detected user activity.
+ # inactivity: '5m'
+
+ ## The time before the session cookie expires and the session is destroyed if remember me IS NOT selected by the
+ ## user.
+ # expiration: '1h'
+
+ ## The time before the cookie expires and the session is destroyed if remember me IS selected by the user. Setting
+ ## this value to -1 disables remember me for this session cookie domain.
+ # remember_me: '1M'
+
+ ## Cookie Session Domain default 'name' value. The name of the session cookie.
+ name: 'authelia_session'
+
+ ## Cookie Session Domain default 'same_site' value. Sets the Cookie SameSite value. Possible options are none, lax,
+ ## or strict. Please read https://www.authelia.com/c/session#same_site
+ same_site: 'lax'
+
+ ## The value for inactivity, expiration, and remember_me are in seconds or the duration notation format.
## See: https://www.authelia.com/c/common#duration-notation-format
## All three of these values affect the cookie/session validity period. Longer periods are considered less secure
## because a stolen cookie will last longer giving attackers more time to spy or attack.
- ## The time before the cookie expires and the session is destroyed if remember me IS NOT selected.
- expiration: 1h
+ ## Cookie Session Domain default 'inactivity' value. The inactivity time before the session is reset. If expiration is
+ ## set to 1h, and this is set to 5m, if the user does not select the remember me option their session will get
+ ## destroyed after 1h, or after 5m since the last time Authelia detected user activity.
+ inactivity: '5m'
- ## The inactivity time before the session is reset. If expiration is set to 1h, and this is set to 5m, if the user
- ## does not select the remember me option their session will get destroyed after 1h, or after 5m since the last time
- ## Authelia detected user activity.
- inactivity: 5m
+ ## Cookie Session Domain default 'expiration' value. The time before the session cookie expires and the session is
+ ## destroyed if remember me IS NOT selected by the user.
+ expiration: '1h'
- ## The time before the cookie expires and the session is destroyed if remember me IS selected.
- ## Value of -1 disables remember me.
- remember_me_duration: 1M
+ ## Cookie Session Domain default 'remember_me' value. The time before the cookie expires and the session is destroyed
+ ## if remember me IS selected by the user. Setting this value to -1 disables remember me for all session cookie
+ ## domains which do not have a specific 'remember_me' value.
+ remember_me: '1M'
##
## Redis Provider
diff --git a/docs/README.md b/docs/README.md
index c16c540b2..e208c6f77 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -5,5 +5,5 @@ Please:
- Only edit the `/content` folder via the main Authelia [monorepo](https://github.com/authelia/authelia/tree/master/docs)
- Edit everything else via the Authelia [website repo](https://github.com/authelia/website)
-See the [Documentation Contributing Guide](https://www.authelia.com/contributing/prologue/documentation/) for more
+See the [Documentation Contributing Guide](https://www.authelia.com/contributing/prologue/documentation-contributions/) for more
information.
diff --git a/docs/content/en/configuration/methods/files.md b/docs/content/en/configuration/methods/files.md
index 7bda7c9dc..667d38df3 100644
--- a/docs/content/en/configuration/methods/files.md
+++ b/docs/content/en/configuration/methods/files.md
@@ -150,7 +150,7 @@ filesystem but before parsing their content. These filters are _**NOT**_ covered
argument or environment variable will change and usage of these will either break or just not work.
The filters are configured as a list of filter names by the `--config.experimental.filters` CLI argument and
-`X_AUTHELIA_CONFIG_EXPERIMENTAL_FILTERS` environment variable. We recommend using the environment variable as it ensures
+`X_AUTHELIA_CONFIG_FILTERS` environment variable. We recommend using the environment variable as it ensures
commands executed from the container use the same filters. If both the CLI argument and environment variable are used
the environment variable is completely ignored.
@@ -164,7 +164,7 @@ authelia --config config.yml --config.experimental.filters expand-env,template
```
```text
-X_AUTHELIA_CONFIG_EXPERIMENTAL_FILTERS=expand-env,template
+X_AUTHELIA_CONFIG_FILTERS=expand-env,template
```
### Expand Environment Variable Filter
diff --git a/docs/content/en/configuration/prologue/common.md b/docs/content/en/configuration/prologue/common.md
index 1f4be33a1..2a8c8b381 100644
--- a/docs/content/en/configuration/prologue/common.md
+++ b/docs/content/en/configuration/prologue/common.md
@@ -22,7 +22,7 @@ describes the implementation of this. You can use this implementation in various
* session:
* expiration
* inactivity
- * remember_me_duration
+ * remember_me
* regulation:
* ban_time
* find_time
diff --git a/docs/content/en/configuration/security/access-control.md b/docs/content/en/configuration/security/access-control.md
index 8993b2027..011cec776 100644
--- a/docs/content/en/configuration/security/access-control.md
+++ b/docs/content/en/configuration/security/access-control.md
@@ -198,7 +198,7 @@ When used in conjunction with [domain] the rule will match when either the [doma
In addition to standard regex patterns this criteria can match some [Named Regex Groups].
-[domain_regex]: #domainregex
+[domain_regex]: #domain_regex
##### Examples
@@ -339,7 +339,7 @@ access_control:
{{< confkey type="list(string)" required="no" >}}
This criteria is a list of values which can be an IP Address, network address range in CIDR notation, or an alias from
-the [global](#networks--global-) section. It matches against the first address in the `X-Forwarded-For` header, or if there
+the [global](#networks-global) section. It matches against the first address in the `X-Forwarded-For` header, or if there
are none it will fall back to the IP address of the packet TCP source IP address. For this reason it's important for you
to configure the proxy server correctly in order to accurately match requests with this criteria. *__Note:__ you may
combine CIDR networks with the alias rules as you please.*
@@ -360,7 +360,7 @@ for administrators to tune the security to their specific needs if desired.
##### Examples
-*Require [two_factor](#twofactor) for all clients other than internal clients and `112.134.145.167`. The first two
+*Require [two_factor](#two_factor) for all clients other than internal clients and `112.134.145.167`. The first two
rules in this list are effectively the same rule just expressed in different ways.*
```yaml
@@ -485,7 +485,7 @@ access_control:
## Policies
The policy of the first matching rule in the configured list decides the policy applied to the request, if no rule
-matches the request the [default_policy](#defaultpolicy) is applied.
+matches the request the [default_policy](#default_policy) is applied.
[policies]: #policies
@@ -510,14 +510,14 @@ about the subject is [one_factor]. See [Rule Matching Concept 2] for more inform
This policy requires the user at minimum complete 1FA successfully (username and password). This means if they have
performed 2FA then they will be allowed to access the resource.
-[one_factor]: #onefactor
+[one_factor]: #one_factor
### two_factor
This policy requires the user to complete 2FA successfully. This is currently the highest level of authentication
policy available.
-[two_factor]: #twofactor
+[two_factor]: #two_factor
## Rule Matching
@@ -554,7 +554,7 @@ a match for that request.
policy: two_factor
```
-[Rule Matching Concept 1]: #rule-matching-concept-1--sequential-order
+[Rule Matching Concept 1]: #rule-matching-concept-1-sequential-order
### Rule Matching Concept 2: Subject Criteria Requires Authentication
@@ -569,7 +569,7 @@ for authentication if no prior rules match the request per [Rule Matching Concep
identical rules, and one of them has a subject based reliant criteria, and the other one is a [bypass] rule then the
[bypass] rule should generally come first.
-[Rule Matching Concept 2]: #rule-matching-concept-2--subject-criteria-requires-authentication
+[Rule Matching Concept 2]: #rule-matching-concept-2-subject-criteria-requires-authentication
## Named Regex Groups
diff --git a/docs/content/en/configuration/session/introduction.md b/docs/content/en/configuration/session/introduction.md
index 24e54f1aa..67d9290cf 100644
--- a/docs/content/en/configuration/session/introduction.md
+++ b/docs/content/en/configuration/session/introduction.md
@@ -25,13 +25,21 @@ authenticated user and can then order the reverse proxy to let the request pass
```yaml
session:
+ secret: insecure_session_secret
+
name: authelia_session
- domain: example.com
same_site: lax
- secret: unsecure_session_secret
- expiration: 1h
inactivity: 5m
- remember_me_duration: 1M
+ expiration: 1h
+ remember_me: 1M
+
+ cookies:
+ - name: authelia_session
+ domain: example.com
+ same_site: lax
+ inactivity: 5m
+ expiration: 1h
+ remember_me: 1d
```
## Providers
@@ -50,34 +58,6 @@ providers are recommended.
## Options
-### name
-
-{{< confkey type="string" default="authelia_session" required="no" >}}
-
-The name of the session cookie. By default this is set to authelia_session. It's mostly useful to change this if you are
-doing development or running multiple instances of Authelia.
-
-### domain
-
-{{< confkey type="string" required="yes" >}}
-
-The domain the cookie is assigned to protect. This must be the same as the domain Authelia is served on or the root
-of the domain. For example if listening on auth.example.com the cookie should be auth.example.com or example.com.
-
-### same_site
-
-{{< confkey type="string" default="lax" required="no" >}}
-
-Sets the cookies SameSite value. Prior to offering the configuration choice this defaulted to None. The new default is
-Lax. This option is defined in lower-case. So for example if you want to set it to `Strict`, the value in configuration
-needs to be `strict`.
-
-You can read about the SameSite cookie in detail on the
-[MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite). In short setting SameSite to Lax
-is generally the most desirable option for Authelia. None is not recommended unless you absolutely know what you're
-doing and trust all the protected apps. Strict is not going to work in many use cases and we have not tested it in this
-state but it's available as an option anyway.
-
### secret
{{< confkey type="string" required="yes" >}}
@@ -91,15 +71,29 @@ It's __strongly recommended__ this is a
[Random Alphanumeric String](../../reference/guides/generating-secure-values.md#generating-a-random-alphanumeric-string) with 64 or more
characters.
-### expiration
+### domain
-{{< confkey type="duration" default="1h" required="no" >}}
+{{< confkey type="string" required="no" >}}
-*__Note:__ This setting uses the [duration notation format](../prologue/common.md#duration-notation-format). Please see
-the [common options](../prologue/common.md#duration-notation-format) documentation for information on this format.*
+_**Deprecation Notice:** This option is deprecated. See the [cookies](#cookies) section instead._
-The period of time before the cookie expires and the session is destroyed. This is overriden by
-[remember_me_duration](#remembermeduration) when the remember me box is checked.
+The domain the cookie is assigned to protect. This must be the same as the domain Authelia is served on or the root
+of the domain. For example if listening on auth.example.com the cookie should be auth.example.com or example.com.
+
+This value automatically maps to a single cookies configuration using the default values. It cannot be assigned at the
+same time as a `cookies` configuration.
+
+### name
+
+{{< confkey type="string" default="authelia_session" required="no" >}}
+
+The default `name` value for all [cookies](#cookies) configurations.
+
+### same_site
+
+{{< confkey type="string" default="lax" required="no" >}}
+
+The default `same_site` value for all `cookies` configurations.
### inactivity
@@ -108,18 +102,119 @@ The period of time before the cookie expires and the session is destroyed. This
*__Note:__ This setting uses the [duration notation format](../prologue/common.md#duration-notation-format). Please see
the [common options](../prologue/common.md#duration-notation-format) documentation for information on this format.*
-The period of time the user can be inactive for until the session is destroyed. Useful if you want long session timers
-but don't want unused devices to be vulnerable.
+The default `inactivity` value for all [cookies](#cookies) configurations.
-### remember_me_duration
+### expiration
+
+{{< confkey type="duration" default="1h" required="no" >}}
+
+*__Note:__ This setting uses the [duration notation format](../prologue/common.md#duration-notation-format). Please see
+the [common options](../prologue/common.md#duration-notation-format) documentation for information on this format.*
+
+The default `expiration` value for all [cookies](#cookies) configurations.
+
+### remember_me
{{< confkey type="duration" default="1M" required="no" >}}
*__Note:__ This setting uses the [duration notation format](../prologue/common.md#duration-notation-format). Please see
the [common options](../prologue/common.md#duration-notation-format) documentation for information on this format.*
+The default `remember_me` value for all [cookies](#cookies) configurations.
+
+### cookies
+
+The list of specific cookie domains that Authelia is configured to handle. Domains not properly configured will
+automatically be denied by Authelia. The list allows administrators to define multiple session cookie domain
+configurations with individual settings.
+
+#### name
+
+{{< confkey type="string" required="no" >}}
+
+*__Default Value:__ This option takes its default value from the [name](#name) setting above.*
+
+The name of the session cookie. By default this is set to the `name` value in the main session configuration section.
+
+#### domain
+
+{{< confkey type="string" required="yes" >}}
+
+The domain the cookie is assigned to protect. This must be the same as the domain Authelia is served on or the root
+of the domain, and consequently if the [authelia_url](#authelia_url) is configured must be able to read and write cookies
+for the domain. For example if listening on `auth.example.com` the cookie should be either `auth.example.com` or
+`example.com`.
+
+Please note most good DynamicDNS solutions fall into a specially protected group of domains and browsers do not allow
+you to write cookies for the root domain. i.e. if you have been assigned `john.duckdns.org` you can't use `duckdns.org`
+for the domain value as browsers will not allow `john.duckdns.org` to read or write cookies for `duckdns.org`.
+
+Consequently, if you have `john.duckdns.org` and `mary.duckdns.org` you cannot share cookies between these domains.
+
+#### authelia_url
+
+{{< confkey type="string" required="no" >}}
+
+*__Note:__ The AuthRequest implementation does not support redirection control on the authorization server. This means
+that the `authelia_url` option is ineffectual for both NGINX and HAProxy, or any other proxy which uses the AuthRequest
+implementation.*
+
+This is a completely optional URL which is the root URL of your Authelia installation for this cookie domain which can
+be used to generate the appropriate redirection for proxies which support this.
+
+If this option is absent you must use the appropriate query parameter or header for your relevant proxy.
+
+#### same_site
+
+{{< confkey type="string" required="no" >}}
+
+*__Default Value:__ This option takes its default value from the [same_site](#samesite) setting above.*
+
+Sets the cookies SameSite value. Prior to offering the configuration choice this defaulted to None. The new default is
+Lax. This option is defined in lower-case. So for example if you want to set it to `Strict`, the value in configuration
+needs to be `strict`.
+
+You can read about the SameSite cookie in detail on the
+[MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite). In short setting SameSite to Lax
+is generally the most desirable option for Authelia. None is not recommended unless you absolutely know what you're
+doing and trust all the protected apps. Strict is not going to work in many use cases and we have not tested it in this
+state but it's available as an option anyway.
+
+#### inactivity
+
+{{< confkey type="duration" required="no" >}}
+
+*__Default Value:__ This option takes its default value from the [inactivity](#inactivity) setting above.*
+
+*__Note:__ This setting uses the [duration notation format](../prologue/common.md#duration-notation-format). Please see
+the [common options](../prologue/common.md#duration-notation-format) documentation for information on this format.*
+
+The period of time the user can be inactive for until the session is destroyed. Useful if you want long session timers
+but don't want unused devices to be vulnerable.
+
+#### expiration
+
+{{< confkey type="duration" required="no" >}}
+
+*__Default Value:__ This option takes its default value from the [expiration](#expiration) setting above.*
+
+*__Note:__ This setting uses the [duration notation format](../prologue/common.md#duration-notation-format). Please see
+the [common options](../prologue/common.md#duration-notation-format) documentation for information on this format.*
+
+The period of time before the cookie expires and the session is destroyed. This is overriden by
+[remember_me](#rememberme) when the remember me box is checked.
+
+#### remember_me
+
+{{< confkey type="duration" required="no" >}}
+
+*__Default Value:__ This option takes its default value from the [remember_me](#rememberme) setting above.*
+
+*__Note:__ This setting uses the [duration notation format](../prologue/common.md#duration-notation-format). Please see
+the [common options](../prologue/common.md#duration-notation-format) documentation for information on this format.*
+
The period of time before the cookie expires and the session is destroyed when the remember me box is checked. Setting
-this to `-1` disables this feature entirely.
+this to `-1` disables this feature entirely for this session cookie domain.
## Security
diff --git a/docs/content/en/contributing/guidelines/commit-message.md b/docs/content/en/contributing/guidelines/commit-message.md
index 8683eb194..5d67c65e6 100644
--- a/docs/content/en/contributing/guidelines/commit-message.md
+++ b/docs/content/en/contributing/guidelines/commit-message.md
@@ -51,8 +51,8 @@ for, and the structure it must have.
β βββ«Έ Commit Scope: api|autheliabot|authentication|authorization|buildkite|bundler|cmd|
β codecov|commands|configuration|deps|docker|duo|go|golangci-lint|
β handlers|logging|metrics|middlewares|mocks|model|notification|npm|ntp|
- β oidc|regulation|renovate|reviewdog|server|session|storage|suites|
- β templates|totp|utils|web
+ β oidc|random|regulation|renovate|reviewdog|server|session|storage|
+ β suites|templates|totp|utils|web
β
βββ«Έ Commit Type: build|ci|docs|feat|fix|i18n|perf|refactor|release|revert|test
```
@@ -93,6 +93,7 @@ commit messages).
* notification
* ntp
* oidc
+* random
* regulation
* server
* session
diff --git a/docs/content/en/contributing/prologue/documentation-contributions.md b/docs/content/en/contributing/prologue/documentation-contributions.md
index d07bdbffb..34d4663e6 100644
--- a/docs/content/en/contributing/prologue/documentation-contributions.md
+++ b/docs/content/en/contributing/prologue/documentation-contributions.md
@@ -43,7 +43,7 @@ The following steps will allow you to run the website on the localhost and view
```bash
git clone https://github.com/authelia/authelia.git
cd authelia/docs
- npm run install
+ npm install
npm run start
```
2. Visit [http://localhost:1313/](http://localhost:1313/) in your browser.
diff --git a/docs/content/en/overview/security/measures.md b/docs/content/en/overview/security/measures.md
index d94b5c7fe..b050512fc 100644
--- a/docs/content/en/overview/security/measures.md
+++ b/docs/content/en/overview/security/measures.md
@@ -256,7 +256,7 @@ database. The value of this option should be long and as random as possible. See
[documentation](../../configuration/session/introduction.md#secret) for this option.
The validity period of session is highly configurable. For example in a highly security conscious domain you could
-set the session [remember_me_duration](../../configuration/session/introduction.md#remembermeduration) to 0 to disable this
+set the session [remember_me](../../configuration/session/introduction.md#rememberme) to 0 to disable this
feature, and set the [expiration](../../configuration/session/introduction.md#expiration) to 2 hours and the
[inactivity](../../configuration/session/introduction.md#inactivity) of 10 minutes. Configuring the session security in this
manner would mean if the cookie age was more than 2 hours or if the user was inactive for more than 10 minutes the
diff --git a/docs/data/configkeys.json b/docs/data/configkeys.json
index 20377f12a..1d0937984 100644
--- a/docs/data/configkeys.json
+++ b/docs/data/configkeys.json
@@ -1 +1 @@
-[{"path":"theme","secret":false,"env":"AUTHELIA_THEME"},{"path":"certificates_directory","secret":false,"env":"AUTHELIA_CERTIFICATES_DIRECTORY"},{"path":"jwt_secret","secret":true,"env":"AUTHELIA_JWT_SECRET_FILE"},{"path":"default_redirection_url","secret":false,"env":"AUTHELIA_DEFAULT_REDIRECTION_URL"},{"path":"default_2fa_method","secret":false,"env":"AUTHELIA_DEFAULT_2FA_METHOD"},{"path":"log.level","secret":false,"env":"AUTHELIA_LOG_LEVEL"},{"path":"log.format","secret":false,"env":"AUTHELIA_LOG_FORMAT"},{"path":"log.file_path","secret":false,"env":"AUTHELIA_LOG_FILE_PATH"},{"path":"log.keep_stdout","secret":false,"env":"AUTHELIA_LOG_KEEP_STDOUT"},{"path":"identity_providers.oidc.hmac_secret","secret":true,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE"},{"path":"identity_providers.oidc.issuer_certificate_chain","secret":true,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_CERTIFICATE_CHAIN_FILE"},{"path":"identity_providers.oidc.issuer_private_key","secret":true,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE"},{"path":"identity_providers.oidc.access_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ACCESS_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.authorize_code_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_AUTHORIZE_CODE_LIFESPAN"},{"path":"identity_providers.oidc.id_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ID_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.refresh_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_REFRESH_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.enable_client_debug_messages","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_CLIENT_DEBUG_MESSAGES"},{"path":"identity_providers.oidc.minimum_parameter_entropy","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_MINIMUM_PARAMETER_ENTROPY"},{"path":"identity_providers.oidc.enforce_pkce","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENFORCE_PKCE"},{"path":"identity_providers.oidc.enable_pkce_plain_challenge","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_PKCE_PLAIN_CHALLENGE"},{"path":"identity_providers.oidc.cors.endpoints","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ENDPOINTS"},{"path":"identity_providers.oidc.cors.allowed_origins","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ALLOWED_ORIGINS"},{"path":"identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ALLOWED_ORIGINS_FROM_CLIENT_REDIRECT_URIS"},{"path":"identity_providers.oidc.clients","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CLIENTS"},{"path":"authentication_backend.password_reset.disable","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_DISABLE"},{"path":"authentication_backend.password_reset.custom_url","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_CUSTOM_URL"},{"path":"authentication_backend.refresh_interval","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_REFRESH_INTERVAL"},{"path":"authentication_backend.file.path","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PATH"},{"path":"authentication_backend.file.watch","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_WATCH"},{"path":"authentication_backend.file.password.algorithm","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ALGORITHM"},{"path":"authentication_backend.file.password.argon2.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_VARIANT"},{"path":"authentication_backend.file.password.argon2.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_ITERATIONS"},{"path":"authentication_backend.file.password.argon2.memory","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_MEMORY"},{"path":"authentication_backend.file.password.argon2.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_PARALLELISM"},{"path":"authentication_backend.file.password.argon2.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_KEY_LENGTH"},{"path":"authentication_backend.file.password.argon2.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_SALT_LENGTH"},{"path":"authentication_backend.file.password.sha2crypt.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_VARIANT"},{"path":"authentication_backend.file.password.sha2crypt.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_ITERATIONS"},{"path":"authentication_backend.file.password.sha2crypt.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_SALT_LENGTH"},{"path":"authentication_backend.file.password.pbkdf2.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_VARIANT"},{"path":"authentication_backend.file.password.pbkdf2.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_ITERATIONS"},{"path":"authentication_backend.file.password.pbkdf2.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_SALT_LENGTH"},{"path":"authentication_backend.file.password.bcrypt.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_VARIANT"},{"path":"authentication_backend.file.password.bcrypt.cost","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_COST"},{"path":"authentication_backend.file.password.scrypt.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_ITERATIONS"},{"path":"authentication_backend.file.password.scrypt.block_size","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_BLOCK_SIZE"},{"path":"authentication_backend.file.password.scrypt.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_PARALLELISM"},{"path":"authentication_backend.file.password.scrypt.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_KEY_LENGTH"},{"path":"authentication_backend.file.password.scrypt.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_SALT_LENGTH"},{"path":"authentication_backend.file.password.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ITERATIONS"},{"path":"authentication_backend.file.password.memory","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_MEMORY"},{"path":"authentication_backend.file.password.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PARALLELISM"},{"path":"authentication_backend.file.password.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_KEY_LENGTH"},{"path":"authentication_backend.file.password.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SALT_LENGTH"},{"path":"authentication_backend.file.search.email","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_EMAIL"},{"path":"authentication_backend.file.search.case_insensitive","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_CASE_INSENSITIVE"},{"path":"authentication_backend.ldap.implementation","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_IMPLEMENTATION"},{"path":"authentication_backend.ldap.url","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_URL"},{"path":"authentication_backend.ldap.timeout","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TIMEOUT"},{"path":"authentication_backend.ldap.start_tls","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_START_TLS"},{"path":"authentication_backend.ldap.tls.minimum_version","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_MINIMUM_VERSION"},{"path":"authentication_backend.ldap.tls.maximum_version","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_MAXIMUM_VERSION"},{"path":"authentication_backend.ldap.tls.skip_verify","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SKIP_VERIFY"},{"path":"authentication_backend.ldap.tls.server_name","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SERVER_NAME"},{"path":"authentication_backend.ldap.tls.private_key","secret":true,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_PRIVATE_KEY_FILE"},{"path":"authentication_backend.ldap.tls.certificate_chain","secret":true,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_CERTIFICATE_CHAIN_FILE"},{"path":"authentication_backend.ldap.base_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_BASE_DN"},{"path":"authentication_backend.ldap.additional_users_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_USERS_DN"},{"path":"authentication_backend.ldap.users_filter","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USERS_FILTER"},{"path":"authentication_backend.ldap.additional_groups_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_GROUPS_DN"},{"path":"authentication_backend.ldap.groups_filter","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUPS_FILTER"},{"path":"authentication_backend.ldap.group_name_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUP_NAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.username_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USERNAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.mail_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_MAIL_ATTRIBUTE"},{"path":"authentication_backend.ldap.display_name_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_DISPLAY_NAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.permit_referrals","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_REFERRALS"},{"path":"authentication_backend.ldap.permit_unauthenticated_bind","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_UNAUTHENTICATED_BIND"},{"path":"authentication_backend.ldap.permit_feature_detection_failure","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_FEATURE_DETECTION_FAILURE"},{"path":"authentication_backend.ldap.user","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USER"},{"path":"authentication_backend.ldap.password","secret":true,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE"},{"path":"session.name","secret":false,"env":"AUTHELIA_SESSION_NAME"},{"path":"session.domain","secret":false,"env":"AUTHELIA_SESSION_DOMAIN"},{"path":"session.same_site","secret":false,"env":"AUTHELIA_SESSION_SAME_SITE"},{"path":"session.secret","secret":true,"env":"AUTHELIA_SESSION_SECRET_FILE"},{"path":"session.expiration","secret":false,"env":"AUTHELIA_SESSION_EXPIRATION"},{"path":"session.inactivity","secret":false,"env":"AUTHELIA_SESSION_INACTIVITY"},{"path":"session.remember_me_duration","secret":false,"env":"AUTHELIA_SESSION_REMEMBER_ME_DURATION"},{"path":"session.redis.host","secret":false,"env":"AUTHELIA_SESSION_REDIS_HOST"},{"path":"session.redis.port","secret":false,"env":"AUTHELIA_SESSION_REDIS_PORT"},{"path":"session.redis.username","secret":false,"env":"AUTHELIA_SESSION_REDIS_USERNAME"},{"path":"session.redis.password","secret":true,"env":"AUTHELIA_SESSION_REDIS_PASSWORD_FILE"},{"path":"session.redis.database_index","secret":false,"env":"AUTHELIA_SESSION_REDIS_DATABASE_INDEX"},{"path":"session.redis.maximum_active_connections","secret":false,"env":"AUTHELIA_SESSION_REDIS_MAXIMUM_ACTIVE_CONNECTIONS"},{"path":"session.redis.minimum_idle_connections","secret":false,"env":"AUTHELIA_SESSION_REDIS_MINIMUM_IDLE_CONNECTIONS"},{"path":"session.redis.tls.minimum_version","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_MINIMUM_VERSION"},{"path":"session.redis.tls.maximum_version","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_MAXIMUM_VERSION"},{"path":"session.redis.tls.skip_verify","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_SKIP_VERIFY"},{"path":"session.redis.tls.server_name","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_SERVER_NAME"},{"path":"session.redis.tls.private_key","secret":true,"env":"AUTHELIA_SESSION_REDIS_TLS_PRIVATE_KEY_FILE"},{"path":"session.redis.tls.certificate_chain","secret":true,"env":"AUTHELIA_SESSION_REDIS_TLS_CERTIFICATE_CHAIN_FILE"},{"path":"session.redis.high_availability.sentinel_name","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_NAME"},{"path":"session.redis.high_availability.sentinel_username","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_USERNAME"},{"path":"session.redis.high_availability.sentinel_password","secret":true,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE"},{"path":"session.redis.high_availability.nodes","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_NODES"},{"path":"session.redis.high_availability.route_by_latency","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_BY_LATENCY"},{"path":"session.redis.high_availability.route_randomly","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_RANDOMLY"},{"path":"totp.disable","secret":false,"env":"AUTHELIA_TOTP_DISABLE"},{"path":"totp.issuer","secret":false,"env":"AUTHELIA_TOTP_ISSUER"},{"path":"totp.algorithm","secret":false,"env":"AUTHELIA_TOTP_ALGORITHM"},{"path":"totp.digits","secret":false,"env":"AUTHELIA_TOTP_DIGITS"},{"path":"totp.period","secret":false,"env":"AUTHELIA_TOTP_PERIOD"},{"path":"totp.skew","secret":false,"env":"AUTHELIA_TOTP_SKEW"},{"path":"totp.secret_size","secret":false,"env":"AUTHELIA_TOTP_SECRET_SIZE"},{"path":"duo_api.disable","secret":false,"env":"AUTHELIA_DUO_API_DISABLE"},{"path":"duo_api.hostname","secret":false,"env":"AUTHELIA_DUO_API_HOSTNAME"},{"path":"duo_api.integration_key","secret":true,"env":"AUTHELIA_DUO_API_INTEGRATION_KEY_FILE"},{"path":"duo_api.secret_key","secret":true,"env":"AUTHELIA_DUO_API_SECRET_KEY_FILE"},{"path":"duo_api.enable_self_enrollment","secret":false,"env":"AUTHELIA_DUO_API_ENABLE_SELF_ENROLLMENT"},{"path":"access_control.default_policy","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_DEFAULT_POLICY"},{"path":"access_control.networks","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_NETWORKS"},{"path":"access_control.rules","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_RULES"},{"path":"ntp.address","secret":false,"env":"AUTHELIA_NTP_ADDRESS"},{"path":"ntp.version","secret":false,"env":"AUTHELIA_NTP_VERSION"},{"path":"ntp.max_desync","secret":false,"env":"AUTHELIA_NTP_MAX_DESYNC"},{"path":"ntp.disable_startup_check","secret":false,"env":"AUTHELIA_NTP_DISABLE_STARTUP_CHECK"},{"path":"ntp.disable_failure","secret":false,"env":"AUTHELIA_NTP_DISABLE_FAILURE"},{"path":"regulation.max_retries","secret":false,"env":"AUTHELIA_REGULATION_MAX_RETRIES"},{"path":"regulation.find_time","secret":false,"env":"AUTHELIA_REGULATION_FIND_TIME"},{"path":"regulation.ban_time","secret":false,"env":"AUTHELIA_REGULATION_BAN_TIME"},{"path":"storage.local.path","secret":false,"env":"AUTHELIA_STORAGE_LOCAL_PATH"},{"path":"storage.mysql.host","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_HOST"},{"path":"storage.mysql.port","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_PORT"},{"path":"storage.mysql.database","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_DATABASE"},{"path":"storage.mysql.username","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_USERNAME"},{"path":"storage.mysql.password","secret":true,"env":"AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE"},{"path":"storage.mysql.timeout","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TIMEOUT"},{"path":"storage.mysql.tls.minimum_version","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TLS_MINIMUM_VERSION"},{"path":"storage.mysql.tls.maximum_version","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TLS_MAXIMUM_VERSION"},{"path":"storage.mysql.tls.skip_verify","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TLS_SKIP_VERIFY"},{"path":"storage.mysql.tls.server_name","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TLS_SERVER_NAME"},{"path":"storage.mysql.tls.private_key","secret":true,"env":"AUTHELIA_STORAGE_MYSQL_TLS_PRIVATE_KEY_FILE"},{"path":"storage.mysql.tls.certificate_chain","secret":true,"env":"AUTHELIA_STORAGE_MYSQL_TLS_CERTIFICATE_CHAIN_FILE"},{"path":"storage.postgres.host","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_HOST"},{"path":"storage.postgres.port","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_PORT"},{"path":"storage.postgres.database","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_DATABASE"},{"path":"storage.postgres.username","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_USERNAME"},{"path":"storage.postgres.password","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE"},{"path":"storage.postgres.timeout","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TIMEOUT"},{"path":"storage.postgres.schema","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SCHEMA"},{"path":"storage.postgres.tls.minimum_version","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_MINIMUM_VERSION"},{"path":"storage.postgres.tls.maximum_version","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_MAXIMUM_VERSION"},{"path":"storage.postgres.tls.skip_verify","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_SKIP_VERIFY"},{"path":"storage.postgres.tls.server_name","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_SERVER_NAME"},{"path":"storage.postgres.tls.private_key","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_PRIVATE_KEY_FILE"},{"path":"storage.postgres.tls.certificate_chain","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_CERTIFICATE_CHAIN_FILE"},{"path":"storage.postgres.ssl.mode","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_MODE"},{"path":"storage.postgres.ssl.root_certificate","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_ROOT_CERTIFICATE"},{"path":"storage.postgres.ssl.certificate","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_CERTIFICATE"},{"path":"storage.postgres.ssl.key","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_KEY_FILE"},{"path":"storage.encryption_key","secret":true,"env":"AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE"},{"path":"notifier.disable_startup_check","secret":false,"env":"AUTHELIA_NOTIFIER_DISABLE_STARTUP_CHECK"},{"path":"notifier.filesystem.filename","secret":false,"env":"AUTHELIA_NOTIFIER_FILESYSTEM_FILENAME"},{"path":"notifier.smtp.host","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_HOST"},{"path":"notifier.smtp.port","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_PORT"},{"path":"notifier.smtp.timeout","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TIMEOUT"},{"path":"notifier.smtp.username","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_USERNAME"},{"path":"notifier.smtp.password","secret":true,"env":"AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE"},{"path":"notifier.smtp.identifier","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_IDENTIFIER"},{"path":"notifier.smtp.sender","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_SENDER"},{"path":"notifier.smtp.subject","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_SUBJECT"},{"path":"notifier.smtp.startup_check_address","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_STARTUP_CHECK_ADDRESS"},{"path":"notifier.smtp.disable_require_tls","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_REQUIRE_TLS"},{"path":"notifier.smtp.disable_html_emails","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_HTML_EMAILS"},{"path":"notifier.smtp.disable_starttls","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_STARTTLS"},{"path":"notifier.smtp.tls.minimum_version","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_MINIMUM_VERSION"},{"path":"notifier.smtp.tls.maximum_version","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_MAXIMUM_VERSION"},{"path":"notifier.smtp.tls.skip_verify","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_SKIP_VERIFY"},{"path":"notifier.smtp.tls.server_name","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_SERVER_NAME"},{"path":"notifier.smtp.tls.private_key","secret":true,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_PRIVATE_KEY_FILE"},{"path":"notifier.smtp.tls.certificate_chain","secret":true,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_CERTIFICATE_CHAIN_FILE"},{"path":"notifier.template_path","secret":false,"env":"AUTHELIA_NOTIFIER_TEMPLATE_PATH"},{"path":"server.host","secret":false,"env":"AUTHELIA_SERVER_HOST"},{"path":"server.port","secret":false,"env":"AUTHELIA_SERVER_PORT"},{"path":"server.path","secret":false,"env":"AUTHELIA_SERVER_PATH"},{"path":"server.asset_path","secret":false,"env":"AUTHELIA_SERVER_ASSET_PATH"},{"path":"server.enable_pprof","secret":false,"env":"AUTHELIA_SERVER_ENABLE_PPROF"},{"path":"server.enable_expvars","secret":false,"env":"AUTHELIA_SERVER_ENABLE_EXPVARS"},{"path":"server.disable_healthcheck","secret":false,"env":"AUTHELIA_SERVER_DISABLE_HEALTHCHECK"},{"path":"server.tls.certificate","secret":false,"env":"AUTHELIA_SERVER_TLS_CERTIFICATE"},{"path":"server.tls.key","secret":true,"env":"AUTHELIA_SERVER_TLS_KEY_FILE"},{"path":"server.tls.client_certificates","secret":false,"env":"AUTHELIA_SERVER_TLS_CLIENT_CERTIFICATES"},{"path":"server.headers.csp_template","secret":false,"env":"AUTHELIA_SERVER_HEADERS_CSP_TEMPLATE"},{"path":"server.buffers.read","secret":false,"env":"AUTHELIA_SERVER_BUFFERS_READ"},{"path":"server.buffers.write","secret":false,"env":"AUTHELIA_SERVER_BUFFERS_WRITE"},{"path":"server.timeouts.read","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_READ"},{"path":"server.timeouts.write","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_WRITE"},{"path":"server.timeouts.idle","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_IDLE"},{"path":"telemetry.metrics.enabled","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_ENABLED"},{"path":"telemetry.metrics.address","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_ADDRESS"},{"path":"telemetry.metrics.buffers.read","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_BUFFERS_READ"},{"path":"telemetry.metrics.buffers.write","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_BUFFERS_WRITE"},{"path":"telemetry.metrics.timeouts.read","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_READ"},{"path":"telemetry.metrics.timeouts.write","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_WRITE"},{"path":"telemetry.metrics.timeouts.idle","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_IDLE"},{"path":"webauthn.disable","secret":false,"env":"AUTHELIA_WEBAUTHN_DISABLE"},{"path":"webauthn.display_name","secret":false,"env":"AUTHELIA_WEBAUTHN_DISPLAY_NAME"},{"path":"webauthn.attestation_conveyance_preference","secret":false,"env":"AUTHELIA_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE"},{"path":"webauthn.user_verification","secret":false,"env":"AUTHELIA_WEBAUTHN_USER_VERIFICATION"},{"path":"webauthn.timeout","secret":false,"env":"AUTHELIA_WEBAUTHN_TIMEOUT"},{"path":"password_policy.standard.enabled","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_ENABLED"},{"path":"password_policy.standard.min_length","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_MIN_LENGTH"},{"path":"password_policy.standard.max_length","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_MAX_LENGTH"},{"path":"password_policy.standard.require_uppercase","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_UPPERCASE"},{"path":"password_policy.standard.require_lowercase","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_LOWERCASE"},{"path":"password_policy.standard.require_number","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_NUMBER"},{"path":"password_policy.standard.require_special","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_SPECIAL"},{"path":"password_policy.zxcvbn.enabled","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_ZXCVBN_ENABLED"},{"path":"password_policy.zxcvbn.min_score","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_ZXCVBN_MIN_SCORE"}]
\ No newline at end of file
+[{"path":"theme","secret":false,"env":"AUTHELIA_THEME"},{"path":"certificates_directory","secret":false,"env":"AUTHELIA_CERTIFICATES_DIRECTORY"},{"path":"jwt_secret","secret":true,"env":"AUTHELIA_JWT_SECRET_FILE"},{"path":"default_redirection_url","secret":false,"env":"AUTHELIA_DEFAULT_REDIRECTION_URL"},{"path":"default_2fa_method","secret":false,"env":"AUTHELIA_DEFAULT_2FA_METHOD"},{"path":"log.level","secret":false,"env":"AUTHELIA_LOG_LEVEL"},{"path":"log.format","secret":false,"env":"AUTHELIA_LOG_FORMAT"},{"path":"log.file_path","secret":false,"env":"AUTHELIA_LOG_FILE_PATH"},{"path":"log.keep_stdout","secret":false,"env":"AUTHELIA_LOG_KEEP_STDOUT"},{"path":"identity_providers.oidc.hmac_secret","secret":true,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE"},{"path":"identity_providers.oidc.issuer_certificate_chain","secret":true,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_CERTIFICATE_CHAIN_FILE"},{"path":"identity_providers.oidc.issuer_private_key","secret":true,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE"},{"path":"identity_providers.oidc.access_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ACCESS_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.authorize_code_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_AUTHORIZE_CODE_LIFESPAN"},{"path":"identity_providers.oidc.id_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ID_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.refresh_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_REFRESH_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.enable_client_debug_messages","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_CLIENT_DEBUG_MESSAGES"},{"path":"identity_providers.oidc.minimum_parameter_entropy","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_MINIMUM_PARAMETER_ENTROPY"},{"path":"identity_providers.oidc.enforce_pkce","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENFORCE_PKCE"},{"path":"identity_providers.oidc.enable_pkce_plain_challenge","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_PKCE_PLAIN_CHALLENGE"},{"path":"identity_providers.oidc.cors.endpoints","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ENDPOINTS"},{"path":"identity_providers.oidc.cors.allowed_origins","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ALLOWED_ORIGINS"},{"path":"identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ALLOWED_ORIGINS_FROM_CLIENT_REDIRECT_URIS"},{"path":"identity_providers.oidc.clients","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CLIENTS"},{"path":"authentication_backend.password_reset.disable","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_DISABLE"},{"path":"authentication_backend.password_reset.custom_url","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_CUSTOM_URL"},{"path":"authentication_backend.refresh_interval","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_REFRESH_INTERVAL"},{"path":"authentication_backend.file.path","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PATH"},{"path":"authentication_backend.file.watch","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_WATCH"},{"path":"authentication_backend.file.password.algorithm","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ALGORITHM"},{"path":"authentication_backend.file.password.argon2.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_VARIANT"},{"path":"authentication_backend.file.password.argon2.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_ITERATIONS"},{"path":"authentication_backend.file.password.argon2.memory","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_MEMORY"},{"path":"authentication_backend.file.password.argon2.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_PARALLELISM"},{"path":"authentication_backend.file.password.argon2.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_KEY_LENGTH"},{"path":"authentication_backend.file.password.argon2.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_SALT_LENGTH"},{"path":"authentication_backend.file.password.sha2crypt.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_VARIANT"},{"path":"authentication_backend.file.password.sha2crypt.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_ITERATIONS"},{"path":"authentication_backend.file.password.sha2crypt.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_SALT_LENGTH"},{"path":"authentication_backend.file.password.pbkdf2.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_VARIANT"},{"path":"authentication_backend.file.password.pbkdf2.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_ITERATIONS"},{"path":"authentication_backend.file.password.pbkdf2.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_SALT_LENGTH"},{"path":"authentication_backend.file.password.bcrypt.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_VARIANT"},{"path":"authentication_backend.file.password.bcrypt.cost","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_COST"},{"path":"authentication_backend.file.password.scrypt.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_ITERATIONS"},{"path":"authentication_backend.file.password.scrypt.block_size","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_BLOCK_SIZE"},{"path":"authentication_backend.file.password.scrypt.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_PARALLELISM"},{"path":"authentication_backend.file.password.scrypt.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_KEY_LENGTH"},{"path":"authentication_backend.file.password.scrypt.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_SALT_LENGTH"},{"path":"authentication_backend.file.password.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ITERATIONS"},{"path":"authentication_backend.file.password.memory","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_MEMORY"},{"path":"authentication_backend.file.password.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PARALLELISM"},{"path":"authentication_backend.file.password.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_KEY_LENGTH"},{"path":"authentication_backend.file.password.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SALT_LENGTH"},{"path":"authentication_backend.file.search.email","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_EMAIL"},{"path":"authentication_backend.file.search.case_insensitive","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_CASE_INSENSITIVE"},{"path":"authentication_backend.ldap.implementation","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_IMPLEMENTATION"},{"path":"authentication_backend.ldap.url","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_URL"},{"path":"authentication_backend.ldap.timeout","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TIMEOUT"},{"path":"authentication_backend.ldap.start_tls","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_START_TLS"},{"path":"authentication_backend.ldap.tls.minimum_version","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_MINIMUM_VERSION"},{"path":"authentication_backend.ldap.tls.maximum_version","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_MAXIMUM_VERSION"},{"path":"authentication_backend.ldap.tls.skip_verify","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SKIP_VERIFY"},{"path":"authentication_backend.ldap.tls.server_name","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SERVER_NAME"},{"path":"authentication_backend.ldap.tls.private_key","secret":true,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_PRIVATE_KEY_FILE"},{"path":"authentication_backend.ldap.tls.certificate_chain","secret":true,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_CERTIFICATE_CHAIN_FILE"},{"path":"authentication_backend.ldap.base_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_BASE_DN"},{"path":"authentication_backend.ldap.additional_users_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_USERS_DN"},{"path":"authentication_backend.ldap.users_filter","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USERS_FILTER"},{"path":"authentication_backend.ldap.additional_groups_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_GROUPS_DN"},{"path":"authentication_backend.ldap.groups_filter","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUPS_FILTER"},{"path":"authentication_backend.ldap.group_name_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUP_NAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.username_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USERNAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.mail_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_MAIL_ATTRIBUTE"},{"path":"authentication_backend.ldap.display_name_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_DISPLAY_NAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.permit_referrals","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_REFERRALS"},{"path":"authentication_backend.ldap.permit_unauthenticated_bind","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_UNAUTHENTICATED_BIND"},{"path":"authentication_backend.ldap.permit_feature_detection_failure","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_FEATURE_DETECTION_FAILURE"},{"path":"authentication_backend.ldap.user","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USER"},{"path":"authentication_backend.ldap.password","secret":true,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE"},{"path":"session.secret","secret":true,"env":"AUTHELIA_SESSION_SECRET_FILE"},{"path":"session.name","secret":false,"env":"AUTHELIA_SESSION_NAME"},{"path":"session.domain","secret":false,"env":"AUTHELIA_SESSION_DOMAIN"},{"path":"session.same_site","secret":false,"env":"AUTHELIA_SESSION_SAME_SITE"},{"path":"session.expiration","secret":false,"env":"AUTHELIA_SESSION_EXPIRATION"},{"path":"session.inactivity","secret":false,"env":"AUTHELIA_SESSION_INACTIVITY"},{"path":"session.remember_me","secret":false,"env":"AUTHELIA_SESSION_REMEMBER_ME"},{"path":"session","secret":false,"env":"AUTHELIA_SESSION"},{"path":"session.cookies","secret":false,"env":"AUTHELIA_SESSION_COOKIES"},{"path":"session.redis.host","secret":false,"env":"AUTHELIA_SESSION_REDIS_HOST"},{"path":"session.redis.port","secret":false,"env":"AUTHELIA_SESSION_REDIS_PORT"},{"path":"session.redis.username","secret":false,"env":"AUTHELIA_SESSION_REDIS_USERNAME"},{"path":"session.redis.password","secret":true,"env":"AUTHELIA_SESSION_REDIS_PASSWORD_FILE"},{"path":"session.redis.database_index","secret":false,"env":"AUTHELIA_SESSION_REDIS_DATABASE_INDEX"},{"path":"session.redis.maximum_active_connections","secret":false,"env":"AUTHELIA_SESSION_REDIS_MAXIMUM_ACTIVE_CONNECTIONS"},{"path":"session.redis.minimum_idle_connections","secret":false,"env":"AUTHELIA_SESSION_REDIS_MINIMUM_IDLE_CONNECTIONS"},{"path":"session.redis.tls.minimum_version","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_MINIMUM_VERSION"},{"path":"session.redis.tls.maximum_version","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_MAXIMUM_VERSION"},{"path":"session.redis.tls.skip_verify","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_SKIP_VERIFY"},{"path":"session.redis.tls.server_name","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_SERVER_NAME"},{"path":"session.redis.tls.private_key","secret":true,"env":"AUTHELIA_SESSION_REDIS_TLS_PRIVATE_KEY_FILE"},{"path":"session.redis.tls.certificate_chain","secret":true,"env":"AUTHELIA_SESSION_REDIS_TLS_CERTIFICATE_CHAIN_FILE"},{"path":"session.redis.high_availability.sentinel_name","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_NAME"},{"path":"session.redis.high_availability.sentinel_username","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_USERNAME"},{"path":"session.redis.high_availability.sentinel_password","secret":true,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE"},{"path":"session.redis.high_availability.nodes","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_NODES"},{"path":"session.redis.high_availability.route_by_latency","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_BY_LATENCY"},{"path":"session.redis.high_availability.route_randomly","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_RANDOMLY"},{"path":"totp.disable","secret":false,"env":"AUTHELIA_TOTP_DISABLE"},{"path":"totp.issuer","secret":false,"env":"AUTHELIA_TOTP_ISSUER"},{"path":"totp.algorithm","secret":false,"env":"AUTHELIA_TOTP_ALGORITHM"},{"path":"totp.digits","secret":false,"env":"AUTHELIA_TOTP_DIGITS"},{"path":"totp.period","secret":false,"env":"AUTHELIA_TOTP_PERIOD"},{"path":"totp.skew","secret":false,"env":"AUTHELIA_TOTP_SKEW"},{"path":"totp.secret_size","secret":false,"env":"AUTHELIA_TOTP_SECRET_SIZE"},{"path":"duo_api.disable","secret":false,"env":"AUTHELIA_DUO_API_DISABLE"},{"path":"duo_api.hostname","secret":false,"env":"AUTHELIA_DUO_API_HOSTNAME"},{"path":"duo_api.integration_key","secret":true,"env":"AUTHELIA_DUO_API_INTEGRATION_KEY_FILE"},{"path":"duo_api.secret_key","secret":true,"env":"AUTHELIA_DUO_API_SECRET_KEY_FILE"},{"path":"duo_api.enable_self_enrollment","secret":false,"env":"AUTHELIA_DUO_API_ENABLE_SELF_ENROLLMENT"},{"path":"access_control.default_policy","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_DEFAULT_POLICY"},{"path":"access_control.networks","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_NETWORKS"},{"path":"access_control.rules","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_RULES"},{"path":"ntp.address","secret":false,"env":"AUTHELIA_NTP_ADDRESS"},{"path":"ntp.version","secret":false,"env":"AUTHELIA_NTP_VERSION"},{"path":"ntp.max_desync","secret":false,"env":"AUTHELIA_NTP_MAX_DESYNC"},{"path":"ntp.disable_startup_check","secret":false,"env":"AUTHELIA_NTP_DISABLE_STARTUP_CHECK"},{"path":"ntp.disable_failure","secret":false,"env":"AUTHELIA_NTP_DISABLE_FAILURE"},{"path":"regulation.max_retries","secret":false,"env":"AUTHELIA_REGULATION_MAX_RETRIES"},{"path":"regulation.find_time","secret":false,"env":"AUTHELIA_REGULATION_FIND_TIME"},{"path":"regulation.ban_time","secret":false,"env":"AUTHELIA_REGULATION_BAN_TIME"},{"path":"storage.local.path","secret":false,"env":"AUTHELIA_STORAGE_LOCAL_PATH"},{"path":"storage.mysql.host","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_HOST"},{"path":"storage.mysql.port","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_PORT"},{"path":"storage.mysql.database","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_DATABASE"},{"path":"storage.mysql.username","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_USERNAME"},{"path":"storage.mysql.password","secret":true,"env":"AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE"},{"path":"storage.mysql.timeout","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TIMEOUT"},{"path":"storage.mysql.tls.minimum_version","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TLS_MINIMUM_VERSION"},{"path":"storage.mysql.tls.maximum_version","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TLS_MAXIMUM_VERSION"},{"path":"storage.mysql.tls.skip_verify","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TLS_SKIP_VERIFY"},{"path":"storage.mysql.tls.server_name","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TLS_SERVER_NAME"},{"path":"storage.mysql.tls.private_key","secret":true,"env":"AUTHELIA_STORAGE_MYSQL_TLS_PRIVATE_KEY_FILE"},{"path":"storage.mysql.tls.certificate_chain","secret":true,"env":"AUTHELIA_STORAGE_MYSQL_TLS_CERTIFICATE_CHAIN_FILE"},{"path":"storage.postgres.host","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_HOST"},{"path":"storage.postgres.port","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_PORT"},{"path":"storage.postgres.database","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_DATABASE"},{"path":"storage.postgres.username","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_USERNAME"},{"path":"storage.postgres.password","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE"},{"path":"storage.postgres.timeout","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TIMEOUT"},{"path":"storage.postgres.schema","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SCHEMA"},{"path":"storage.postgres.tls.minimum_version","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_MINIMUM_VERSION"},{"path":"storage.postgres.tls.maximum_version","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_MAXIMUM_VERSION"},{"path":"storage.postgres.tls.skip_verify","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_SKIP_VERIFY"},{"path":"storage.postgres.tls.server_name","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_SERVER_NAME"},{"path":"storage.postgres.tls.private_key","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_PRIVATE_KEY_FILE"},{"path":"storage.postgres.tls.certificate_chain","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_TLS_CERTIFICATE_CHAIN_FILE"},{"path":"storage.postgres.ssl.mode","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_MODE"},{"path":"storage.postgres.ssl.root_certificate","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_ROOT_CERTIFICATE"},{"path":"storage.postgres.ssl.certificate","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_CERTIFICATE"},{"path":"storage.postgres.ssl.key","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_KEY_FILE"},{"path":"storage.encryption_key","secret":true,"env":"AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE"},{"path":"notifier.disable_startup_check","secret":false,"env":"AUTHELIA_NOTIFIER_DISABLE_STARTUP_CHECK"},{"path":"notifier.filesystem.filename","secret":false,"env":"AUTHELIA_NOTIFIER_FILESYSTEM_FILENAME"},{"path":"notifier.smtp.host","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_HOST"},{"path":"notifier.smtp.port","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_PORT"},{"path":"notifier.smtp.timeout","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TIMEOUT"},{"path":"notifier.smtp.username","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_USERNAME"},{"path":"notifier.smtp.password","secret":true,"env":"AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE"},{"path":"notifier.smtp.identifier","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_IDENTIFIER"},{"path":"notifier.smtp.sender","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_SENDER"},{"path":"notifier.smtp.subject","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_SUBJECT"},{"path":"notifier.smtp.startup_check_address","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_STARTUP_CHECK_ADDRESS"},{"path":"notifier.smtp.disable_require_tls","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_REQUIRE_TLS"},{"path":"notifier.smtp.disable_html_emails","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_HTML_EMAILS"},{"path":"notifier.smtp.disable_starttls","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_STARTTLS"},{"path":"notifier.smtp.tls.minimum_version","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_MINIMUM_VERSION"},{"path":"notifier.smtp.tls.maximum_version","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_MAXIMUM_VERSION"},{"path":"notifier.smtp.tls.skip_verify","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_SKIP_VERIFY"},{"path":"notifier.smtp.tls.server_name","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_SERVER_NAME"},{"path":"notifier.smtp.tls.private_key","secret":true,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_PRIVATE_KEY_FILE"},{"path":"notifier.smtp.tls.certificate_chain","secret":true,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_CERTIFICATE_CHAIN_FILE"},{"path":"notifier.template_path","secret":false,"env":"AUTHELIA_NOTIFIER_TEMPLATE_PATH"},{"path":"server.host","secret":false,"env":"AUTHELIA_SERVER_HOST"},{"path":"server.port","secret":false,"env":"AUTHELIA_SERVER_PORT"},{"path":"server.path","secret":false,"env":"AUTHELIA_SERVER_PATH"},{"path":"server.asset_path","secret":false,"env":"AUTHELIA_SERVER_ASSET_PATH"},{"path":"server.enable_pprof","secret":false,"env":"AUTHELIA_SERVER_ENABLE_PPROF"},{"path":"server.enable_expvars","secret":false,"env":"AUTHELIA_SERVER_ENABLE_EXPVARS"},{"path":"server.disable_healthcheck","secret":false,"env":"AUTHELIA_SERVER_DISABLE_HEALTHCHECK"},{"path":"server.tls.certificate","secret":false,"env":"AUTHELIA_SERVER_TLS_CERTIFICATE"},{"path":"server.tls.key","secret":true,"env":"AUTHELIA_SERVER_TLS_KEY_FILE"},{"path":"server.tls.client_certificates","secret":false,"env":"AUTHELIA_SERVER_TLS_CLIENT_CERTIFICATES"},{"path":"server.headers.csp_template","secret":false,"env":"AUTHELIA_SERVER_HEADERS_CSP_TEMPLATE"},{"path":"server.buffers.read","secret":false,"env":"AUTHELIA_SERVER_BUFFERS_READ"},{"path":"server.buffers.write","secret":false,"env":"AUTHELIA_SERVER_BUFFERS_WRITE"},{"path":"server.timeouts.read","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_READ"},{"path":"server.timeouts.write","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_WRITE"},{"path":"server.timeouts.idle","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_IDLE"},{"path":"telemetry.metrics.enabled","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_ENABLED"},{"path":"telemetry.metrics.address","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_ADDRESS"},{"path":"telemetry.metrics.buffers.read","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_BUFFERS_READ"},{"path":"telemetry.metrics.buffers.write","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_BUFFERS_WRITE"},{"path":"telemetry.metrics.timeouts.read","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_READ"},{"path":"telemetry.metrics.timeouts.write","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_WRITE"},{"path":"telemetry.metrics.timeouts.idle","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_IDLE"},{"path":"webauthn.disable","secret":false,"env":"AUTHELIA_WEBAUTHN_DISABLE"},{"path":"webauthn.display_name","secret":false,"env":"AUTHELIA_WEBAUTHN_DISPLAY_NAME"},{"path":"webauthn.attestation_conveyance_preference","secret":false,"env":"AUTHELIA_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE"},{"path":"webauthn.user_verification","secret":false,"env":"AUTHELIA_WEBAUTHN_USER_VERIFICATION"},{"path":"webauthn.timeout","secret":false,"env":"AUTHELIA_WEBAUTHN_TIMEOUT"},{"path":"password_policy.standard.enabled","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_ENABLED"},{"path":"password_policy.standard.min_length","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_MIN_LENGTH"},{"path":"password_policy.standard.max_length","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_MAX_LENGTH"},{"path":"password_policy.standard.require_uppercase","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_UPPERCASE"},{"path":"password_policy.standard.require_lowercase","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_LOWERCASE"},{"path":"password_policy.standard.require_number","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_NUMBER"},{"path":"password_policy.standard.require_special","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_SPECIAL"},{"path":"password_policy.zxcvbn.enabled","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_ZXCVBN_ENABLED"},{"path":"password_policy.zxcvbn.min_score","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_ZXCVBN_MIN_SCORE"}]
\ No newline at end of file
diff --git a/docs/package.json b/docs/package.json
index 371ee9e01..7d00b8444 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -39,7 +39,7 @@
},
"devDependencies": {
"@babel/cli": "7.20.7",
- "@babel/core": "7.20.7",
+ "@babel/core": "7.20.12",
"@babel/preset-env": "7.20.2",
"@fullhuman/postcss-purgecss": "5.0.0",
"@hyas/images": "0.3.2",
@@ -49,7 +49,7 @@
"bootstrap": "5.2.3",
"bootstrap-icons": "1.10.3",
"clipboard": "2.0.11",
- "eslint": "8.31.0",
+ "eslint": "8.32.0",
"exec-bin": "1.0.0",
"flexsearch": "0.7.31",
"highlight.js": "11.7.0",
@@ -60,7 +60,7 @@
"markdownlint-cli2": "0.6.0",
"netlify-plugin-submit-sitemap": "0.4.0",
"node-fetch": "3.3.0",
- "postcss": "8.4.20",
+ "postcss": "8.4.21",
"postcss-cli": "10.1.0",
"purgecss-whitelister": "2.4.0",
"shx": "0.3.4",
@@ -68,6 +68,6 @@
"stylelint-config-standard-scss": "6.1.0"
},
"otherDependencies": {
- "hugo": "0.109.0"
+ "hugo": "0.110.0"
}
}
diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml
index eaf5ad761..fd5fffe23 100644
--- a/docs/pnpm-lock.yaml
+++ b/docs/pnpm-lock.yaml
@@ -2,7 +2,7 @@ lockfileVersion: 5.4
specifiers:
'@babel/cli': 7.20.7
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/preset-env': 7.20.2
'@fullhuman/postcss-purgecss': 5.0.0
'@hyas/images': 0.3.2
@@ -12,7 +12,7 @@ specifiers:
bootstrap: 5.2.3
bootstrap-icons: 1.10.3
clipboard: 2.0.11
- eslint: 8.31.0
+ eslint: 8.32.0
exec-bin: 1.0.0
flexsearch: 0.7.31
highlight.js: 11.7.0
@@ -23,7 +23,7 @@ specifiers:
markdownlint-cli2: 0.6.0
netlify-plugin-submit-sitemap: 0.4.0
node-fetch: 3.3.0
- postcss: 8.4.20
+ postcss: 8.4.21
postcss-cli: 10.1.0
purgecss-whitelister: 2.4.0
shx: 0.3.4
@@ -31,18 +31,18 @@ specifiers:
stylelint-config-standard-scss: 6.1.0
devDependencies:
- '@babel/cli': 7.20.7_@babel+core@7.20.7
- '@babel/core': 7.20.7
- '@babel/preset-env': 7.20.2_@babel+core@7.20.7
- '@fullhuman/postcss-purgecss': 5.0.0_postcss@8.4.20
+ '@babel/cli': 7.20.7_@babel+core@7.20.12
+ '@babel/core': 7.20.12
+ '@babel/preset-env': 7.20.2_@babel+core@7.20.12
+ '@fullhuman/postcss-purgecss': 5.0.0_postcss@8.4.21
'@hyas/images': 0.3.2
'@popperjs/core': 2.11.6
auto-changelog: 2.4.0
- autoprefixer: 10.4.13_postcss@8.4.20
+ autoprefixer: 10.4.13_postcss@8.4.21
bootstrap: 5.2.3_@popperjs+core@2.11.6
bootstrap-icons: 1.10.3
clipboard: 2.0.11
- eslint: 8.31.0
+ eslint: 8.32.0
exec-bin: 1.0.0
flexsearch: 0.7.31
highlight.js: 11.7.0
@@ -53,12 +53,12 @@ devDependencies:
markdownlint-cli2: 0.6.0
netlify-plugin-submit-sitemap: 0.4.0
node-fetch: 3.3.0
- postcss: 8.4.20
- postcss-cli: 10.1.0_postcss@8.4.20
+ postcss: 8.4.21
+ postcss-cli: 10.1.0_postcss@8.4.21
purgecss-whitelister: 2.4.0
shx: 0.3.4
stylelint: 14.16.1
- stylelint-config-standard-scss: 6.1.0_vitr26fcqo6sphdfxyxll4n2gy
+ stylelint-config-standard-scss: 6.1.0_w5gtdy6oq4ictd5o4eu6befejy
packages:
@@ -70,14 +70,14 @@ packages:
'@jridgewell/trace-mapping': 0.3.13
dev: true
- /@babel/cli/7.20.7_@babel+core@7.20.7:
+ /@babel/cli/7.20.7_@babel+core@7.20.12:
resolution: {integrity: sha512-WylgcELHB66WwQqItxNILsMlaTd8/SO6SgTTjMp4uCI7P4QyH1r3nqgFmO3BfM4AtfniHgFMH3EpYFj/zynBkQ==}
engines: {node: '>=6.9.0'}
hasBin: true
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@jridgewell/trace-mapping': 0.3.13
commander: 4.1.1
convert-source-map: 1.8.0
@@ -107,24 +107,24 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
- /@babel/core/7.20.7:
- resolution: {integrity: sha512-t1ZjCluspe5DW24bn2Rr1CDb2v9rn/hROtg9a2tmd0+QYf4bsloYfLQzjG4qHPNMhWtKdGC33R5AxGR2Af2cBw==}
+ /@babel/core/7.20.12:
+ resolution: {integrity: sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==}
engines: {node: '>=6.9.0'}
dependencies:
'@ampproject/remapping': 2.2.0
'@babel/code-frame': 7.18.6
'@babel/generator': 7.20.7
- '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.7
- '@babel/helper-module-transforms': 7.20.7
+ '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12
+ '@babel/helper-module-transforms': 7.20.11
'@babel/helpers': 7.20.7
'@babel/parser': 7.20.7
'@babel/template': 7.20.7
- '@babel/traverse': 7.20.10
+ '@babel/traverse': 7.20.12
'@babel/types': 7.20.7
convert-source-map: 1.8.0
debug: 4.3.4
gensync: 1.0.0-beta.2
- json5: 2.2.1
+ json5: 2.2.3
semver: 6.3.0
transitivePeerDependencies:
- supports-color
@@ -154,40 +154,40 @@ packages:
'@babel/types': 7.20.5
dev: true
- /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.7:
+ /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.12:
resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/compat-data': 7.20.1
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-validator-option': 7.18.6
browserslist: 4.21.4
semver: 6.3.0
dev: true
- /@babel/helper-compilation-targets/7.20.7_@babel+core@7.20.7:
+ /@babel/helper-compilation-targets/7.20.7_@babel+core@7.20.12:
resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/compat-data': 7.20.10
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-validator-option': 7.18.6
browserslist: 4.21.4
lru-cache: 5.1.1
semver: 6.3.0
dev: true
- /@babel/helper-create-class-features-plugin/7.18.6_@babel+core@7.20.7:
+ /@babel/helper-create-class-features-plugin/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-YfDzdnoxHGV8CzqHGyCbFvXg5QESPFkXlHtvdCkesLjjVMT2Adxe4FGUR5ChIb3DxSaXO12iIOCWoXdsUVwnqw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-annotate-as-pure': 7.18.6
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.19.0
@@ -199,24 +199,24 @@ packages:
- supports-color
dev: true
- /@babel/helper-create-regexp-features-plugin/7.19.0_@babel+core@7.20.7:
+ /@babel/helper-create-regexp-features-plugin/7.19.0_@babel+core@7.20.12:
resolution: {integrity: sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-annotate-as-pure': 7.18.6
regexpu-core: 5.1.0
dev: true
- /@babel/helper-define-polyfill-provider/0.3.3_@babel+core@7.20.7:
+ /@babel/helper-define-polyfill-provider/0.3.3_@babel+core@7.20.12:
resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==}
peerDependencies:
'@babel/core': ^7.4.0-0
dependencies:
- '@babel/core': 7.20.7
- '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
debug: 4.3.4
lodash.debounce: 4.0.8
@@ -267,6 +267,22 @@ packages:
'@babel/types': 7.20.5
dev: true
+ /@babel/helper-module-transforms/7.20.11:
+ resolution: {integrity: sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-environment-visitor': 7.18.9
+ '@babel/helper-module-imports': 7.18.6
+ '@babel/helper-simple-access': 7.20.2
+ '@babel/helper-split-export-declaration': 7.18.6
+ '@babel/helper-validator-identifier': 7.19.1
+ '@babel/template': 7.20.7
+ '@babel/traverse': 7.20.12
+ '@babel/types': 7.20.7
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/@babel/helper-module-transforms/7.20.2:
resolution: {integrity: sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==}
engines: {node: '>=6.9.0'}
@@ -283,22 +299,6 @@ packages:
- supports-color
dev: true
- /@babel/helper-module-transforms/7.20.7:
- resolution: {integrity: sha512-FNdu7r67fqMUSVuQpFQGE6BPdhJIhitoxhGzDbAXNcA07uoVG37fOiMk3OSV8rEICuyG6t8LGkd9EE64qIEoIA==}
- engines: {node: '>=6.9.0'}
- dependencies:
- '@babel/helper-environment-visitor': 7.18.9
- '@babel/helper-module-imports': 7.18.6
- '@babel/helper-simple-access': 7.20.2
- '@babel/helper-split-export-declaration': 7.18.6
- '@babel/helper-validator-identifier': 7.19.1
- '@babel/template': 7.20.7
- '@babel/traverse': 7.20.10
- '@babel/types': 7.20.7
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/@babel/helper-optimise-call-expression/7.18.6:
resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==}
engines: {node: '>=6.9.0'}
@@ -311,13 +311,13 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
- /@babel/helper-remap-async-to-generator/7.18.9_@babel+core@7.20.7:
+ /@babel/helper-remap-async-to-generator/7.18.9_@babel+core@7.20.12:
resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-annotate-as-pure': 7.18.6
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-wrap-function': 7.18.11
@@ -405,7 +405,7 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.20.7
- '@babel/traverse': 7.20.10
+ '@babel/traverse': 7.20.12
'@babel/types': 7.20.7
transitivePeerDependencies:
- supports-color
@@ -428,404 +428,404 @@ packages:
'@babel/types': 7.20.7
dev: true
- /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.18.9_@babel+core@7.20.7:
+ /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.18.9_@babel+core@7.20.12:
resolution: {integrity: sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.13.0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
'@babel/helper-skip-transparent-expression-wrappers': 7.18.9
- '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.20.7
+ '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.20.12
dev: true
- /@babel/plugin-proposal-async-generator-functions/7.20.1_@babel+core@7.20.7:
+ /@babel/plugin-proposal-async-generator-functions/7.20.1_@babel+core@7.20.12:
resolution: {integrity: sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-plugin-utils': 7.20.2
- '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.7
- '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.7
+ '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.12
+ '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
- '@babel/helper-create-class-features-plugin': 7.18.6_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-create-class-features-plugin': 7.18.6_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/plugin-proposal-class-static-block/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-proposal-class-static-block/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.12.0
dependencies:
- '@babel/core': 7.20.7
- '@babel/helper-create-class-features-plugin': 7.18.6_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-create-class-features-plugin': 7.18.6_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
- '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.7
+ '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.12
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/plugin-proposal-dynamic-import/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-proposal-dynamic-import/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
- '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.7
+ '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12
dev: true
- /@babel/plugin-proposal-export-namespace-from/7.18.9_@babel+core@7.20.7:
+ /@babel/plugin-proposal-export-namespace-from/7.18.9_@babel+core@7.20.12:
resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
- '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.7
+ '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.12
dev: true
- /@babel/plugin-proposal-json-strings/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-proposal-json-strings/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
- '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.7
+ '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12
dev: true
- /@babel/plugin-proposal-logical-assignment-operators/7.18.9_@babel+core@7.20.7:
+ /@babel/plugin-proposal-logical-assignment-operators/7.18.9_@babel+core@7.20.12:
resolution: {integrity: sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
- '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.7
+ '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12
dev: true
- /@babel/plugin-proposal-nullish-coalescing-operator/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-proposal-nullish-coalescing-operator/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
- '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.7
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12
dev: true
- /@babel/plugin-proposal-numeric-separator/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-proposal-numeric-separator/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
- '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.7
+ '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12
dev: true
- /@babel/plugin-proposal-object-rest-spread/7.20.2_@babel+core@7.20.7:
+ /@babel/plugin-proposal-object-rest-spread/7.20.2_@babel+core@7.20.12:
resolution: {integrity: sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/compat-data': 7.20.1
- '@babel/core': 7.20.7
- '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
- '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.7
- '@babel/plugin-transform-parameters': 7.20.3_@babel+core@7.20.7
+ '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12
+ '@babel/plugin-transform-parameters': 7.20.3_@babel+core@7.20.12
dev: true
- /@babel/plugin-proposal-optional-catch-binding/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-proposal-optional-catch-binding/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
- '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.7
+ '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12
dev: true
- /@babel/plugin-proposal-optional-chaining/7.18.9_@babel+core@7.20.7:
+ /@babel/plugin-proposal-optional-chaining/7.18.9_@babel+core@7.20.12:
resolution: {integrity: sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
'@babel/helper-skip-transparent-expression-wrappers': 7.18.9
- '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.7
+ '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12
dev: true
- /@babel/plugin-proposal-private-methods/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-proposal-private-methods/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
- '@babel/helper-create-class-features-plugin': 7.18.6_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-create-class-features-plugin': 7.18.6_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/plugin-proposal-private-property-in-object/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-proposal-private-property-in-object/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-annotate-as-pure': 7.18.6
- '@babel/helper-create-class-features-plugin': 7.18.6_@babel+core@7.20.7
+ '@babel/helper-create-class-features-plugin': 7.18.6_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
- '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.7
+ '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.12
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/plugin-proposal-unicode-property-regex/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-proposal-unicode-property-regex/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==}
engines: {node: '>=4'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
- '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.20.7:
+ /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.20.12:
resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.20.7:
+ /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.20.12:
resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.20.7:
+ /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.20.12:
resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.20.7:
+ /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.20.12:
resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.20.7:
+ /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.20.12:
resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-import-assertions/7.20.0_@babel+core@7.20.7:
+ /@babel/plugin-syntax-import-assertions/7.20.0_@babel+core@7.20.12:
resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.20.7:
+ /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.20.12:
resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.20.7:
+ /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.20.12:
resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.20.7:
+ /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.20.12:
resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.20.7:
+ /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.20.12:
resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.20.7:
+ /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.20.12:
resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.20.7:
+ /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.20.12:
resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.20.7:
+ /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.20.12:
resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.20.7:
+ /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.20.12:
resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.20.7:
+ /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.20.12:
resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-arrow-functions/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-arrow-functions/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-async-to-generator/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-async-to-generator/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-module-imports': 7.18.6
'@babel/helper-plugin-utils': 7.20.2
- '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.7
+ '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.12
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/plugin-transform-block-scoped-functions/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-block-scoped-functions/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-block-scoping/7.20.2_@babel+core@7.20.7:
+ /@babel/plugin-transform-block-scoping/7.20.2_@babel+core@7.20.12:
resolution: {integrity: sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-classes/7.20.2_@babel+core@7.20.7:
+ /@babel/plugin-transform-classes/7.20.2_@babel+core@7.20.12:
resolution: {integrity: sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-annotate-as-pure': 7.18.6
- '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.7
+ '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.12
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.19.0
'@babel/helper-optimise-call-expression': 7.18.6
@@ -837,120 +837,120 @@ packages:
- supports-color
dev: true
- /@babel/plugin-transform-computed-properties/7.18.9_@babel+core@7.20.7:
+ /@babel/plugin-transform-computed-properties/7.18.9_@babel+core@7.20.12:
resolution: {integrity: sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-destructuring/7.20.2_@babel+core@7.20.7:
+ /@babel/plugin-transform-destructuring/7.20.2_@babel+core@7.20.12:
resolution: {integrity: sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-dotall-regex/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-dotall-regex/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
- '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-duplicate-keys/7.18.9_@babel+core@7.20.7:
+ /@babel/plugin-transform-duplicate-keys/7.18.9_@babel+core@7.20.12:
resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-exponentiation-operator/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-exponentiation-operator/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-builder-binary-assignment-operator-visitor': 7.18.6
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-for-of/7.18.8_@babel+core@7.20.7:
+ /@babel/plugin-transform-for-of/7.18.8_@babel+core@7.20.12:
resolution: {integrity: sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-function-name/7.18.9_@babel+core@7.20.7:
+ /@babel/plugin-transform-function-name/7.18.9_@babel+core@7.20.12:
resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
- '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.12
'@babel/helper-function-name': 7.19.0
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-literals/7.18.9_@babel+core@7.20.7:
+ /@babel/plugin-transform-literals/7.18.9_@babel+core@7.20.12:
resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-member-expression-literals/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-member-expression-literals/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-modules-amd/7.19.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-modules-amd/7.19.6_@babel+core@7.20.12:
resolution: {integrity: sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-module-transforms': 7.20.2
'@babel/helper-plugin-utils': 7.20.2
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/plugin-transform-modules-commonjs/7.19.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-modules-commonjs/7.19.6_@babel+core@7.20.12:
resolution: {integrity: sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-module-transforms': 7.20.2
'@babel/helper-plugin-utils': 7.20.2
'@babel/helper-simple-access': 7.20.2
@@ -958,13 +958,13 @@ packages:
- supports-color
dev: true
- /@babel/plugin-transform-modules-systemjs/7.19.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-modules-systemjs/7.19.6_@babel+core@7.20.12:
resolution: {integrity: sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-module-transforms': 7.20.2
'@babel/helper-plugin-utils': 7.20.2
@@ -973,261 +973,261 @@ packages:
- supports-color
dev: true
- /@babel/plugin-transform-modules-umd/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-modules-umd/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-module-transforms': 7.20.2
'@babel/helper-plugin-utils': 7.20.2
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/plugin-transform-named-capturing-groups-regex/7.19.1_@babel+core@7.20.7:
+ /@babel/plugin-transform-named-capturing-groups-regex/7.19.1_@babel+core@7.20.12:
resolution: {integrity: sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
- '@babel/core': 7.20.7
- '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-new-target/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-new-target/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-object-super/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-object-super/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
'@babel/helper-replace-supers': 7.18.9
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/plugin-transform-parameters/7.20.3_@babel+core@7.20.7:
+ /@babel/plugin-transform-parameters/7.20.3_@babel+core@7.20.12:
resolution: {integrity: sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-property-literals/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-property-literals/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-regenerator/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-regenerator/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
regenerator-transform: 0.15.0
dev: true
- /@babel/plugin-transform-reserved-words/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-reserved-words/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-shorthand-properties/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-shorthand-properties/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-spread/7.19.0_@babel+core@7.20.7:
+ /@babel/plugin-transform-spread/7.19.0_@babel+core@7.20.12:
resolution: {integrity: sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
'@babel/helper-skip-transparent-expression-wrappers': 7.18.9
dev: true
- /@babel/plugin-transform-sticky-regex/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-sticky-regex/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-template-literals/7.18.9_@babel+core@7.20.7:
+ /@babel/plugin-transform-template-literals/7.18.9_@babel+core@7.20.12:
resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-typeof-symbol/7.18.9_@babel+core@7.20.7:
+ /@babel/plugin-transform-typeof-symbol/7.18.9_@babel+core@7.20.12:
resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-unicode-escapes/7.18.10_@babel+core@7.20.7:
+ /@babel/plugin-transform-unicode-escapes/7.18.10_@babel+core@7.20.12:
resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/plugin-transform-unicode-regex/7.18.6_@babel+core@7.20.7:
+ /@babel/plugin-transform-unicode-regex/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
- '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/preset-env/7.20.2_@babel+core@7.20.7:
+ /@babel/preset-env/7.20.2_@babel+core@7.20.12:
resolution: {integrity: sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/compat-data': 7.20.1
- '@babel/core': 7.20.7
- '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.12
'@babel/helper-plugin-utils': 7.20.2
'@babel/helper-validator-option': 7.18.6
- '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.18.9_@babel+core@7.20.7
- '@babel/plugin-proposal-async-generator-functions': 7.20.1_@babel+core@7.20.7
- '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-proposal-class-static-block': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-proposal-export-namespace-from': 7.18.9_@babel+core@7.20.7
- '@babel/plugin-proposal-json-strings': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-proposal-logical-assignment-operators': 7.18.9_@babel+core@7.20.7
- '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-proposal-object-rest-spread': 7.20.2_@babel+core@7.20.7
- '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.20.7
- '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-proposal-private-property-in-object': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.7
- '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.7
- '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.7
- '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.7
- '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.7
- '@babel/plugin-syntax-import-assertions': 7.20.0_@babel+core@7.20.7
- '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.7
- '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.7
- '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.7
- '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.7
- '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.7
- '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.7
- '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.7
- '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.7
- '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.7
- '@babel/plugin-transform-arrow-functions': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-async-to-generator': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-block-scoping': 7.20.2_@babel+core@7.20.7
- '@babel/plugin-transform-classes': 7.20.2_@babel+core@7.20.7
- '@babel/plugin-transform-computed-properties': 7.18.9_@babel+core@7.20.7
- '@babel/plugin-transform-destructuring': 7.20.2_@babel+core@7.20.7
- '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.20.7
- '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-for-of': 7.18.8_@babel+core@7.20.7
- '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.20.7
- '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.20.7
- '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-modules-amd': 7.19.6_@babel+core@7.20.7
- '@babel/plugin-transform-modules-commonjs': 7.19.6_@babel+core@7.20.7
- '@babel/plugin-transform-modules-systemjs': 7.19.6_@babel+core@7.20.7
- '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-named-capturing-groups-regex': 7.19.1_@babel+core@7.20.7
- '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-parameters': 7.20.3_@babel+core@7.20.7
- '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-regenerator': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-spread': 7.19.0_@babel+core@7.20.7
- '@babel/plugin-transform-sticky-regex': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.20.7
- '@babel/plugin-transform-typeof-symbol': 7.18.9_@babel+core@7.20.7
- '@babel/plugin-transform-unicode-escapes': 7.18.10_@babel+core@7.20.7
- '@babel/plugin-transform-unicode-regex': 7.18.6_@babel+core@7.20.7
- '@babel/preset-modules': 0.1.5_@babel+core@7.20.7
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.18.9_@babel+core@7.20.12
+ '@babel/plugin-proposal-async-generator-functions': 7.20.1_@babel+core@7.20.12
+ '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-proposal-class-static-block': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-proposal-export-namespace-from': 7.18.9_@babel+core@7.20.12
+ '@babel/plugin-proposal-json-strings': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-proposal-logical-assignment-operators': 7.18.9_@babel+core@7.20.12
+ '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-proposal-object-rest-spread': 7.20.2_@babel+core@7.20.12
+ '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.20.12
+ '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-proposal-private-property-in-object': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12
+ '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.12
+ '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.12
+ '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12
+ '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.12
+ '@babel/plugin-syntax-import-assertions': 7.20.0_@babel+core@7.20.12
+ '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12
+ '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12
+ '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12
+ '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12
+ '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12
+ '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12
+ '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.12
+ '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.12
+ '@babel/plugin-transform-arrow-functions': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-async-to-generator': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-block-scoping': 7.20.2_@babel+core@7.20.12
+ '@babel/plugin-transform-classes': 7.20.2_@babel+core@7.20.12
+ '@babel/plugin-transform-computed-properties': 7.18.9_@babel+core@7.20.12
+ '@babel/plugin-transform-destructuring': 7.20.2_@babel+core@7.20.12
+ '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.20.12
+ '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-for-of': 7.18.8_@babel+core@7.20.12
+ '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.20.12
+ '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.20.12
+ '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-modules-amd': 7.19.6_@babel+core@7.20.12
+ '@babel/plugin-transform-modules-commonjs': 7.19.6_@babel+core@7.20.12
+ '@babel/plugin-transform-modules-systemjs': 7.19.6_@babel+core@7.20.12
+ '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.19.1_@babel+core@7.20.12
+ '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-parameters': 7.20.3_@babel+core@7.20.12
+ '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-regenerator': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-spread': 7.19.0_@babel+core@7.20.12
+ '@babel/plugin-transform-sticky-regex': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.20.12
+ '@babel/plugin-transform-typeof-symbol': 7.18.9_@babel+core@7.20.12
+ '@babel/plugin-transform-unicode-escapes': 7.18.10_@babel+core@7.20.12
+ '@babel/plugin-transform-unicode-regex': 7.18.6_@babel+core@7.20.12
+ '@babel/preset-modules': 0.1.5_@babel+core@7.20.12
'@babel/types': 7.20.2
- babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.20.7
- babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.20.7
- babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.20.7
+ babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.20.12
+ babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.20.12
+ babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.20.12
core-js-compat: 3.25.2
semver: 6.3.0
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/preset-modules/0.1.5_@babel+core@7.20.7:
+ /@babel/preset-modules/0.1.5_@babel+core@7.20.12:
resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
+ '@babel/core': 7.20.12
'@babel/helper-plugin-utils': 7.20.2
- '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.7
- '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.7
+ '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.12
+ '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.12
'@babel/types': 7.20.5
esutils: 2.0.3
dev: true
@@ -1275,6 +1275,24 @@ packages:
- supports-color
dev: true
+ /@babel/traverse/7.20.12:
+ resolution: {integrity: sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/code-frame': 7.18.6
+ '@babel/generator': 7.20.7
+ '@babel/helper-environment-visitor': 7.18.9
+ '@babel/helper-function-name': 7.19.0
+ '@babel/helper-hoist-variables': 7.18.6
+ '@babel/helper-split-export-declaration': 7.18.6
+ '@babel/parser': 7.20.7
+ '@babel/types': 7.20.7
+ debug: 4.3.4
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/@babel/traverse/7.20.5:
resolution: {integrity: sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==}
engines: {node: '>=6.9.0'}
@@ -1320,14 +1338,14 @@ packages:
to-fast-properties: 2.0.0
dev: true
- /@csstools/selector-specificity/2.0.2_2xshye3abirqjlplmebvmaxyna:
+ /@csstools/selector-specificity/2.0.2_wajs5nedgkikc5pcuwett7legi:
resolution: {integrity: sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==}
engines: {node: ^12 || ^14 || >=16}
peerDependencies:
postcss: ^8.2
postcss-selector-parser: ^6.0.10
dependencies:
- postcss: 8.4.20
+ postcss: 8.4.21
postcss-selector-parser: 6.0.11
dev: true
@@ -1348,12 +1366,12 @@ packages:
- supports-color
dev: true
- /@fullhuman/postcss-purgecss/5.0.0_postcss@8.4.20:
+ /@fullhuman/postcss-purgecss/5.0.0_postcss@8.4.21:
resolution: {integrity: sha512-onDS/b/2pMRzqSoj4qOs2tYFmOpaspjTAgvACIHMPiicu1ptajiBruTrjBzTKdxWdX0ldaBb7wj8nEaTLyFkJw==}
peerDependencies:
postcss: ^8.0.0
dependencies:
- postcss: 8.4.20
+ postcss: 8.4.21
purgecss: 5.0.0
dev: true
@@ -1607,7 +1625,7 @@ packages:
- encoding
dev: true
- /autoprefixer/10.4.13_postcss@8.4.20:
+ /autoprefixer/10.4.13_postcss@8.4.21:
resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==}
engines: {node: ^10 || ^12 || >=14}
hasBin: true
@@ -1619,42 +1637,42 @@ packages:
fraction.js: 4.2.0
normalize-range: 0.1.2
picocolors: 1.0.0
- postcss: 8.4.20
+ postcss: 8.4.21
postcss-value-parser: 4.2.0
dev: true
- /babel-plugin-polyfill-corejs2/0.3.3_@babel+core@7.20.7:
+ /babel-plugin-polyfill-corejs2/0.3.3_@babel+core@7.20.12:
resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/compat-data': 7.20.1
- '@babel/core': 7.20.7
- '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12
semver: 6.3.0
transitivePeerDependencies:
- supports-color
dev: true
- /babel-plugin-polyfill-corejs3/0.6.0_@babel+core@7.20.7:
+ /babel-plugin-polyfill-corejs3/0.6.0_@babel+core@7.20.12:
resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
- '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12
core-js-compat: 3.25.2
transitivePeerDependencies:
- supports-color
dev: true
- /babel-plugin-polyfill-regenerator/0.4.1_@babel+core@7.20.7:
+ /babel-plugin-polyfill-regenerator/0.4.1_@babel+core@7.20.12:
resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.20.7
- '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.7
+ '@babel/core': 7.20.12
+ '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12
transitivePeerDependencies:
- supports-color
dev: true
@@ -1920,7 +1938,7 @@ packages:
dev: true
/concat-map/0.0.1:
- resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
/convert-source-map/1.8.0:
@@ -2159,13 +2177,13 @@ packages:
estraverse: 5.3.0
dev: true
- /eslint-utils/3.0.0_eslint@8.31.0:
+ /eslint-utils/3.0.0_eslint@8.32.0:
resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
peerDependencies:
eslint: '>=5'
dependencies:
- eslint: 8.31.0
+ eslint: 8.32.0
eslint-visitor-keys: 2.1.0
dev: true
@@ -2179,8 +2197,8 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
- /eslint/8.31.0:
- resolution: {integrity: sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==}
+ /eslint/8.32.0:
+ resolution: {integrity: sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
@@ -2195,7 +2213,7 @@ packages:
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.1.1
- eslint-utils: 3.0.0_eslint@8.31.0
+ eslint-utils: 3.0.0_eslint@8.32.0
eslint-visitor-keys: 3.3.0
espree: 9.4.0
esquery: 1.4.0
@@ -2888,8 +2906,8 @@ packages:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
dev: true
- /json5/2.2.1:
- resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==}
+ /json5/2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
hasBin: true
dev: true
@@ -3404,7 +3422,7 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
- /postcss-cli/10.1.0_postcss@8.4.20:
+ /postcss-cli/10.1.0_postcss@8.4.21:
resolution: {integrity: sha512-Zu7PLORkE9YwNdvOeOVKPmWghprOtjFQU3srMUGbdz3pHJiFh7yZ4geiZFMkjMfB0mtTFR3h8RemR62rPkbOPA==}
engines: {node: '>=14'}
hasBin: true
@@ -3417,9 +3435,9 @@ packages:
get-stdin: 9.0.0
globby: 13.1.2
picocolors: 1.0.0
- postcss: 8.4.20
- postcss-load-config: 4.0.1_postcss@8.4.20
- postcss-reporter: 7.0.5_postcss@8.4.20
+ postcss: 8.4.21
+ postcss-load-config: 4.0.1_postcss@8.4.21
+ postcss-reporter: 7.0.5_postcss@8.4.21
pretty-hrtime: 1.0.3
read-cache: 1.0.0
slash: 5.0.0
@@ -3428,7 +3446,7 @@ packages:
- ts-node
dev: true
- /postcss-load-config/4.0.1_postcss@8.4.20:
+ /postcss-load-config/4.0.1_postcss@8.4.21:
resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==}
engines: {node: '>= 14'}
peerDependencies:
@@ -3441,7 +3459,7 @@ packages:
optional: true
dependencies:
lilconfig: 2.0.5
- postcss: 8.4.20
+ postcss: 8.4.21
yaml: 2.2.1
dev: true
@@ -3449,14 +3467,14 @@ packages:
resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==}
dev: true
- /postcss-reporter/7.0.5_postcss@8.4.20:
+ /postcss-reporter/7.0.5_postcss@8.4.21:
resolution: {integrity: sha512-glWg7VZBilooZGOFPhN9msJ3FQs19Hie7l5a/eE6WglzYqVeH3ong3ShFcp9kDWJT1g2Y/wd59cocf9XxBtkWA==}
engines: {node: '>=10'}
peerDependencies:
postcss: ^8.1.0
dependencies:
picocolors: 1.0.0
- postcss: 8.4.20
+ postcss: 8.4.21
thenby: 1.3.4
dev: true
@@ -3464,22 +3482,22 @@ packages:
resolution: {integrity: sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==}
dev: true
- /postcss-safe-parser/6.0.0_postcss@8.4.20:
+ /postcss-safe-parser/6.0.0_postcss@8.4.21:
resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==}
engines: {node: '>=12.0'}
peerDependencies:
postcss: ^8.3.3
dependencies:
- postcss: 8.4.20
+ postcss: 8.4.21
dev: true
- /postcss-scss/4.0.4_postcss@8.4.20:
+ /postcss-scss/4.0.4_postcss@8.4.21:
resolution: {integrity: sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg==}
engines: {node: '>=12.0'}
peerDependencies:
postcss: ^8.3.3
dependencies:
- postcss: 8.4.20
+ postcss: 8.4.21
dev: true
/postcss-selector-parser/6.0.10:
@@ -3502,8 +3520,8 @@ packages:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
dev: true
- /postcss/8.4.20:
- resolution: {integrity: sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==}
+ /postcss/8.4.21:
+ resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.4
@@ -3551,7 +3569,7 @@ packages:
dependencies:
commander: 9.4.1
glob: 8.0.3
- postcss: 8.4.20
+ postcss: 8.4.21
postcss-selector-parser: 6.0.10
dev: true
@@ -3939,7 +3957,7 @@ packages:
resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==}
dev: true
- /stylelint-config-recommended-scss/8.0.0_vitr26fcqo6sphdfxyxll4n2gy:
+ /stylelint-config-recommended-scss/8.0.0_w5gtdy6oq4ictd5o4eu6befejy:
resolution: {integrity: sha512-BxjxEzRaZoQb7Iinc3p92GS6zRdRAkIuEu2ZFLTxJK2e1AIcCb5B5MXY9KOXdGTnYFZ+KKx6R4Fv9zU6CtMYPQ==}
peerDependencies:
postcss: ^8.3.3
@@ -3948,8 +3966,8 @@ packages:
postcss:
optional: true
dependencies:
- postcss: 8.4.20
- postcss-scss: 4.0.4_postcss@8.4.20
+ postcss: 8.4.21
+ postcss-scss: 4.0.4_postcss@8.4.21
stylelint: 14.16.1
stylelint-config-recommended: 9.0.0_stylelint@14.16.1
stylelint-scss: 4.2.0_stylelint@14.16.1
@@ -3963,7 +3981,7 @@ packages:
stylelint: 14.16.1
dev: true
- /stylelint-config-standard-scss/6.1.0_vitr26fcqo6sphdfxyxll4n2gy:
+ /stylelint-config-standard-scss/6.1.0_w5gtdy6oq4ictd5o4eu6befejy:
resolution: {integrity: sha512-iZ2B5kQT2G3rUzx+437cEpdcnFOQkwnwqXuY8Z0QUwIHQVE8mnYChGAquyKFUKZRZ0pRnrciARlPaR1RBtPb0Q==}
peerDependencies:
postcss: ^8.3.3
@@ -3972,9 +3990,9 @@ packages:
postcss:
optional: true
dependencies:
- postcss: 8.4.20
+ postcss: 8.4.21
stylelint: 14.16.1
- stylelint-config-recommended-scss: 8.0.0_vitr26fcqo6sphdfxyxll4n2gy
+ stylelint-config-recommended-scss: 8.0.0_w5gtdy6oq4ictd5o4eu6befejy
stylelint-config-standard: 29.0.0_stylelint@14.16.1
dev: true
@@ -4005,7 +4023,7 @@ packages:
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
hasBin: true
dependencies:
- '@csstools/selector-specificity': 2.0.2_2xshye3abirqjlplmebvmaxyna
+ '@csstools/selector-specificity': 2.0.2_wajs5nedgkikc5pcuwett7legi
balanced-match: 2.0.0
colord: 2.9.3
cosmiconfig: 7.1.0
@@ -4028,10 +4046,10 @@ packages:
micromatch: 4.0.5
normalize-path: 3.0.0
picocolors: 1.0.0
- postcss: 8.4.20
+ postcss: 8.4.21
postcss-media-query-parser: 0.2.3
postcss-resolve-nested-selector: 0.1.1
- postcss-safe-parser: 6.0.0_postcss@8.4.20
+ postcss-safe-parser: 6.0.0_postcss@8.4.21
postcss-selector-parser: 6.0.11
postcss-value-parser: 4.2.0
resolve-from: 5.0.0
diff --git a/examples/compose/lite/authelia/configuration.yml b/examples/compose/lite/authelia/configuration.yml
index 39db78500..d9841d310 100644
--- a/examples/compose/lite/authelia/configuration.yml
+++ b/examples/compose/lite/authelia/configuration.yml
@@ -3,6 +3,7 @@
# Authelia configuration #
###############################################################
+# This secret can also be set using the env variables AUTHELIA_JWT_SECRET_FILE
jwt_secret: a_very_important_secret
default_redirection_url: https://public.example.com
@@ -12,7 +13,6 @@ server:
log:
level: debug
-# This secret can also be set using the env variables AUTHELIA_JWT_SECRET_FILE
totp:
issuer: authelia.com
@@ -39,12 +39,14 @@ access_control:
policy: two_factor
session:
- name: authelia_session
# This secret can also be set using the env variables AUTHELIA_SESSION_SECRET_FILE
secret: unsecure_session_secret
- expiration: 3600 # 1 hour
- inactivity: 300 # 5 minutes
- domain: example.com # Should match whatever your root protected domain is
+
+ cookies:
+ - name: authelia_session
+ domain: example.com # Should match whatever your root protected domain is
+ expiration: 3600 # 1 hour
+ inactivity: 300 # 5 minutes
redis:
host: redis
diff --git a/examples/compose/local/authelia/configuration.yml b/examples/compose/local/authelia/configuration.yml
index 84b8039c3..42b770e6c 100644
--- a/examples/compose/local/authelia/configuration.yml
+++ b/examples/compose/local/authelia/configuration.yml
@@ -31,11 +31,13 @@ access_control:
policy: two_factor
session:
- name: authelia_session
secret: unsecure_session_secret
- expiration: 3600 # 1 hour
- inactivity: 300 # 5 minutes
- domain: example.com # Should match whatever your root protected domain is
+
+ cookies:
+ - name: authelia_session
+ domain: example.com # Should match whatever your root protected domain is
+ expiration: 3600 # 1 hour
+ inactivity: 300 # 5 minutes
regulation:
max_retries: 3
diff --git a/go.mod b/go.mod
index 390edcd5b..9326bf9b1 100644
--- a/go.mod
+++ b/go.mod
@@ -7,28 +7,28 @@ require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/deckarep/golang-set/v2 v2.1.0
github.com/duosecurity/duo_api_golang v0.0.0-20221117185402-091daa09e19d
- github.com/fasthttp/router v1.4.14
- github.com/fasthttp/session/v2 v2.4.13
+ github.com/fasthttp/router v1.4.15
+ github.com/fasthttp/session/v2 v2.4.16
github.com/fsnotify/fsnotify v1.6.0
github.com/go-asn1-ber/asn1-ber v1.5.4
github.com/go-crypt/crypt v0.2.3
github.com/go-ldap/ldap/v3 v3.4.4
- github.com/go-rod/rod v0.112.2
+ github.com/go-rod/rod v0.112.3
github.com/go-sql-driver/mysql v1.7.0
- github.com/go-webauthn/webauthn v0.5.0
+ github.com/go-webauthn/webauthn v0.6.0
github.com/golang-jwt/jwt/v4 v4.4.3
github.com/golang/mock v1.6.0
github.com/google/uuid v1.3.0
github.com/hashicorp/go-retryablehttp v0.7.2
github.com/jackc/pgx/v5 v5.2.0
github.com/jmoiron/sqlx v1.3.5
- github.com/knadh/koanf v1.4.5
+ github.com/knadh/koanf v1.5.0
github.com/mattn/go-sqlite3 v1.14.16
github.com/mitchellh/mapstructure v1.5.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/ory/fosite v0.44.0
github.com/ory/herodot v0.9.13
- github.com/ory/x v0.0.528
+ github.com/ory/x v0.0.533
github.com/otiai10/copy v1.9.0
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0
@@ -38,8 +38,8 @@ require (
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.1
github.com/trustelem/zxcvbn v1.0.1
- github.com/valyala/fasthttp v1.43.0
- github.com/wneessen/go-mail v0.3.7
+ github.com/valyala/fasthttp v1.44.0
+ github.com/wneessen/go-mail v0.3.8
golang.org/x/sync v0.1.0
golang.org/x/term v0.4.0
golang.org/x/text v0.6.0
@@ -88,31 +88,30 @@ require (
github.com/ory/viper v1.7.5 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
- github.com/philhofer/fwd v1.1.1 // indirect
+ github.com/philhofer/fwd v1.1.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
- github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d // indirect
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/test-go/testify v1.1.4 // indirect
- github.com/tinylib/msgp v1.1.6 // indirect
+ github.com/tinylib/msgp v1.1.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/ysmood/goob v0.4.0 // indirect
github.com/ysmood/gson v0.7.3 // indirect
github.com/ysmood/leakless v0.8.0 // indirect
- golang.org/x/crypto v0.1.0 // indirect
- golang.org/x/mod v0.6.0 // indirect
+ golang.org/x/crypto v0.4.0 // indirect
+ golang.org/x/mod v0.7.0 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
golang.org/x/sys v0.4.0 // indirect
- golang.org/x/tools v0.2.0 // indirect
+ golang.org/x/tools v0.4.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect
google.golang.org/grpc v1.50.1 // indirect
diff --git a/go.sum b/go.sum
index 42f9deb28..97b3768bc 100644
--- a/go.sum
+++ b/go.sum
@@ -75,7 +75,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
-github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
+github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -144,10 +144,10 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
-github.com/fasthttp/router v1.4.14 h1:+W65VCKgyI4BZszhDiCRfONoFieePZIoQ7D8vGhiuzM=
-github.com/fasthttp/router v1.4.14/go.mod h1:+svLaOvqF9Lc0yjX9aHAD4NUMf+mggLPOT4UMdS6fjM=
-github.com/fasthttp/session/v2 v2.4.13 h1:I/j3w8UrXX1haXE+iraAbQuGihNVeTq6b8sp6L3ZJ6Q=
-github.com/fasthttp/session/v2 v2.4.13/go.mod h1:bAE6Bjl6ofQbkOpqcSuOVt/1R1LnbNLnFMHjGQcYP5M=
+github.com/fasthttp/router v1.4.15 h1:ERaILezYX6ks1I+Z2v5qY4vqiQKnujauo9nV6M+HIOg=
+github.com/fasthttp/router v1.4.15/go.mod h1:NFNlTCilbRVkeLc+E5JDkcxUdkpiJGKDL8Zy7Ey2JTI=
+github.com/fasthttp/session/v2 v2.4.16 h1:JRvuEqr/+/cNMBkhGZN118FurLh6paUGscwJr26TxAQ=
+github.com/fasthttp/session/v2 v2.4.16/go.mod h1:nv8SD6pAx3n3KjJsEt4k1p0vstqclbNcrCwjc1OjuCI=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
@@ -182,8 +182,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
-github.com/go-rod/rod v0.112.2 h1:dwauKYC/H2em8/BcGk3gC0LTzZHf5MIDKf2DVM4z9gU=
-github.com/go-rod/rod v0.112.2/go.mod h1:ElViL9ABbcshNQw93+11FrYRH92RRhMKleuILo6+5V0=
+github.com/go-rod/rod v0.112.3 h1:xbSaA9trZ8v/+eJRGOM6exK1RCsLPwwnzA78vpES0gk=
+github.com/go-rod/rod v0.112.3/go.mod h1:ElViL9ABbcshNQw93+11FrYRH92RRhMKleuILo6+5V0=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
@@ -192,8 +192,8 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-webauthn/revoke v0.1.6 h1:3tv+itza9WpX5tryRQx4GwxCCBrCIiJ8GIkOhxiAmmU=
github.com/go-webauthn/revoke v0.1.6/go.mod h1:TB4wuW4tPlwgF3znujA96F70/YSQXHPPWl7vgY09Iy8=
-github.com/go-webauthn/webauthn v0.5.0 h1:Tbmp37AGIhYbQmcy2hEffo3U3cgPClqvxJ7cLUnF7Rc=
-github.com/go-webauthn/webauthn v0.5.0/go.mod h1:0CBq/jNfPS9l033j4AxMk8K8MluiMsde9uGNSPFLEVE=
+github.com/go-webauthn/webauthn v0.6.0 h1:uLInMApSvBfP+vEFasNE0rnVPG++fjp7lmAIvNhe+UU=
+github.com/go-webauthn/webauthn v0.6.0/go.mod h1:7edMRZXwuM6JIVjN68G24Bzt+bPCvTmjiL0j+cAmXtY=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@@ -363,11 +363,10 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
-github.com/knadh/koanf v1.4.5 h1:yKWFswTrqFc0u7jBAoERUz30+N1b1yPXU01gAPr8IrY=
-github.com/knadh/koanf v1.4.5/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs=
+github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs=
+github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
@@ -396,7 +395,6 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/goveralls v0.0.11 h1:eJXea6R6IFlL1QMKNMzDvvHv/hwGrnvyig4N+0+XiMM=
@@ -461,8 +459,8 @@ github.com/ory/herodot v0.9.13 h1:cN/Z4eOkErl/9W7hDIDLb79IO/bfsH+8yscBjRpB4IU=
github.com/ory/herodot v0.9.13/go.mod h1:IWDs9kSvFQqw/cQ8zi5ksyYvITiUU4dI7glUrhZcJYo=
github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE=
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
-github.com/ory/x v0.0.528 h1:26fXxJ5Zl5XDFjiCt5jdWQOI89Q2XogB1EnUeqx7P+M=
-github.com/ory/x v0.0.528/go.mod h1:XBqhPZRppPHTxtsE0l0oI/B2Onf1QJtMRGPh3CpEpA0=
+github.com/ory/x v0.0.533 h1:F7kJ1C5jQv4PCmbOppK8H3V0mV2yQeOy97zCgDIHb4U=
+github.com/ory/x v0.0.533/go.mod h1:6mu7krkQuaDXxxeVb/SplbrXSlItcklT51nKBihmUl4=
github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4=
github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
@@ -482,8 +480,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
-github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
-github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
+github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -540,9 +538,6 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
-github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d h1:ICMDEgNgR5xFW6ZDeMKTtmh07YiLr7GkDw897I2DwKg=
-github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d/go.mod h1:jrsy/bTK2n5uybo7bAvtLGzmuzAbxp+nKS8bzgrZURE=
-github.com/savsgio/gotils v0.0.0-20220401102855-e56b59f40436/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d h1:Q+gqLBOPkFGHyCJxXMRqtUgUbTjI8/Ze8vu8GGyNFwo=
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
@@ -595,8 +590,8 @@ github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
-github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=
-github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
+github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
+github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/trustelem/zxcvbn v1.0.1 h1:mp4JFtzdDYGj9WYSD3KQSkwwUumWNFzXaAjckaTYpsc=
github.com/trustelem/zxcvbn v1.0.1/go.mod h1:zonUyKeh7sw6psPf/e3DtRqkRyZvAbOfjNz/aO7YQ5s=
@@ -604,13 +599,11 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
-github.com/valyala/fasthttp v1.42.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
-github.com/valyala/fasthttp v1.43.0 h1:Gy4sb32C98fbzVWZlTM1oTMdLWGyvxR03VhM6cBIU4g=
-github.com/valyala/fasthttp v1.43.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
+github.com/valyala/fasthttp v1.44.0 h1:R+gLUhldIsfg1HokMuQjdQ5bh9nuXHPIfvkYUu9eR5Q=
+github.com/valyala/fasthttp v1.44.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
-github.com/wneessen/go-mail v0.3.7 h1:loEAGLvsDZLSiE6c+keBfg0gpias/R3ocFU8Eoh3Pq4=
-github.com/wneessen/go-mail v0.3.7/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
+github.com/wneessen/go-mail v0.3.8 h1:ja5D/o/RVwrtRIYFlrO7GmtcjDNeMakGQuwQRZYv0JM=
+github.com/wneessen/go-mail v0.3.8/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
@@ -631,6 +624,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
@@ -658,11 +652,12 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
-golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
+golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
+golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -698,8 +693,9 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
-golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
+golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -746,7 +742,9 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -772,6 +770,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -843,16 +842,19 @@ golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -865,6 +867,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -916,7 +919,6 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
-golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -928,8 +930,9 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
-golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
-golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
+golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/internal/commands/context.go b/internal/commands/context.go
index a0f2df2b2..9a9e367d1 100644
--- a/internal/commands/context.go
+++ b/internal/commands/context.go
@@ -172,7 +172,7 @@ func (ctx *CmdCtx) LoadProviders() (warns, errs []error) {
ctx.providers.Notifier = notification.NewFileNotifier(*ctx.config.Notifier.FileSystem)
}
- if ctx.providers.OpenIDConnect, err = oidc.NewOpenIDConnectProvider(ctx.config.IdentityProviders.OIDC, ctx.providers.StorageProvider); err != nil {
+ if ctx.providers.OpenIDConnect, err = oidc.NewOpenIDConnectProvider(ctx.config.IdentityProviders.OIDC, ctx.providers.StorageProvider, ctx.providers.Templates); err != nil {
errs = append(errs, err)
}
diff --git a/internal/configuration/config.template.yml b/internal/configuration/config.template.yml
index 6dc5a39eb..0d966b038 100644
--- a/internal/configuration/config.template.yml
+++ b/internal/configuration/config.template.yml
@@ -662,38 +662,76 @@ access_control:
## The session cookies identify the user once logged in.
## The available providers are: `memory`, `redis`. Memory is the provider unless redis is defined.
session:
- ## The name of the session cookie.
- name: authelia_session
-
- ## The domain to protect.
- ## Note: the authenticator must also be in that domain.
- ## If empty, the cookie is restricted to the subdomain of the issuer.
- domain: example.com
-
- ## Sets the Cookie SameSite value. Possible options are none, lax, or strict.
- ## Please read https://www.authelia.com/c/session#same_site
- same_site: lax
-
## The secret to encrypt the session data. This is only used with Redis / Redis Sentinel.
## Secret can also be set using a secret: https://www.authelia.com/c/secrets
- secret: insecure_session_secret
+ secret: 'insecure_session_secret'
- ## The value for expiration, inactivity, and remember_me_duration are in seconds or the duration notation format.
+ ## Cookies configures the list of allowed cookie domains for sessions to be created on.
+ ## Undefined values will default to the values below.
+ # cookies:
+ # -
+ ## The name of the session cookie.
+ # name: 'authelia_session'
+
+ ## The domain to protect.
+ ## Note: the Authelia portal must also be in that domain.
+ # domain: 'example.com'
+
+ ## Optional. The fully qualified URI of the portal to redirect users to on proxies that support redirections.
+ ## Rules:
+ ## - MUST use the secure scheme 'https://'
+ ## - The above domain MUST either:
+ ## - Match the host portion of this URI.
+ ## - Match the suffix of the host portion when prefixed with '.'.
+ # authelia_url: 'https://auth.example.com'
+
+ ## Sets the Cookie SameSite value. Possible options are none, lax, or strict.
+ ## Please read https://www.authelia.com/c/session#same_site
+ # same_site: 'lax'
+
+ ## The value for inactivity, expiration, and remember_me are in seconds or the duration notation format.
+ ## See: https://www.authelia.com/c/common#duration-notation-format
+ ## All three of these values affect the cookie/session validity period. Longer periods are considered less secure
+ ## because a stolen cookie will last longer giving attackers more time to spy or attack.
+
+ ## The inactivity time before the session is reset. If expiration is set to 1h, and this is set to 5m, if the user
+ ## does not select the remember me option their session will get destroyed after 1h, or after 5m since the last
+ ## time Authelia detected user activity.
+ # inactivity: '5m'
+
+ ## The time before the session cookie expires and the session is destroyed if remember me IS NOT selected by the
+ ## user.
+ # expiration: '1h'
+
+ ## The time before the cookie expires and the session is destroyed if remember me IS selected by the user. Setting
+ ## this value to -1 disables remember me for this session cookie domain.
+ # remember_me: '1M'
+
+ ## Cookie Session Domain default 'name' value. The name of the session cookie.
+ name: 'authelia_session'
+
+ ## Cookie Session Domain default 'same_site' value. Sets the Cookie SameSite value. Possible options are none, lax,
+ ## or strict. Please read https://www.authelia.com/c/session#same_site
+ same_site: 'lax'
+
+ ## The value for inactivity, expiration, and remember_me are in seconds or the duration notation format.
## See: https://www.authelia.com/c/common#duration-notation-format
## All three of these values affect the cookie/session validity period. Longer periods are considered less secure
## because a stolen cookie will last longer giving attackers more time to spy or attack.
- ## The time before the cookie expires and the session is destroyed if remember me IS NOT selected.
- expiration: 1h
+ ## Cookie Session Domain default 'inactivity' value. The inactivity time before the session is reset. If expiration is
+ ## set to 1h, and this is set to 5m, if the user does not select the remember me option their session will get
+ ## destroyed after 1h, or after 5m since the last time Authelia detected user activity.
+ inactivity: '5m'
- ## The inactivity time before the session is reset. If expiration is set to 1h, and this is set to 5m, if the user
- ## does not select the remember me option their session will get destroyed after 1h, or after 5m since the last time
- ## Authelia detected user activity.
- inactivity: 5m
+ ## Cookie Session Domain default 'expiration' value. The time before the session cookie expires and the session is
+ ## destroyed if remember me IS NOT selected by the user.
+ expiration: '1h'
- ## The time before the cookie expires and the session is destroyed if remember me IS selected.
- ## Value of -1 disables remember me.
- remember_me_duration: 1M
+ ## Cookie Session Domain default 'remember_me' value. The time before the cookie expires and the session is destroyed
+ ## if remember me IS selected by the user. Setting this value to -1 disables remember me for all session cookie
+ ## domains which do not have a specific 'remember_me' value.
+ remember_me: '1M'
##
## Redis Provider
diff --git a/internal/configuration/deprecation.go b/internal/configuration/deprecation.go
index fbe881ef1..e37750996 100644
--- a/internal/configuration/deprecation.go
+++ b/internal/configuration/deprecation.go
@@ -134,4 +134,11 @@ var deprecations = map[string]Deprecation{
AutoMap: true,
MapFunc: nil,
},
+ "session.remember_me_duration": {
+ Version: model.SemanticVersion{Major: 4, Minor: 38},
+ Key: "session.remember_me_duration",
+ NewKey: "session.remember_me",
+ AutoMap: true,
+ MapFunc: nil,
+ },
}
diff --git a/internal/configuration/provider_test.go b/internal/configuration/provider_test.go
index 04ae71890..975fccdd4 100644
--- a/internal/configuration/provider_test.go
+++ b/internal/configuration/provider_test.go
@@ -323,7 +323,7 @@ func TestShouldDecodeSMTPSenderWithName(t *testing.T) {
assert.Equal(t, "Admin", config.Notifier.SMTP.Sender.Name)
assert.Equal(t, "admin@example.com", config.Notifier.SMTP.Sender.Address)
- assert.Equal(t, schema.RememberMeDisabled, config.Session.RememberMeDuration)
+ assert.Equal(t, schema.RememberMeDisabled, config.Session.RememberMe)
}
func TestShouldParseRegex(t *testing.T) {
diff --git a/internal/configuration/schema/keys.go b/internal/configuration/schema/keys.go
index 07967dc88..d1d616b44 100644
--- a/internal/configuration/schema/keys.go
+++ b/internal/configuration/schema/keys.go
@@ -105,13 +105,23 @@ var Keys = []string{
"authentication_backend.ldap.permit_feature_detection_failure",
"authentication_backend.ldap.user",
"authentication_backend.ldap.password",
+ "session.secret",
"session.name",
"session.domain",
"session.same_site",
- "session.secret",
"session.expiration",
"session.inactivity",
- "session.remember_me_duration",
+ "session.remember_me",
+ "session",
+ "session.cookies",
+ "session.cookies[].name",
+ "session.cookies[].domain",
+ "session.cookies[].same_site",
+ "session.cookies[].expiration",
+ "session.cookies[].inactivity",
+ "session.cookies[].remember_me",
+ "session.cookies[]",
+ "session.cookies[].authelia_url",
"session.redis.host",
"session.redis.port",
"session.redis.username",
diff --git a/internal/configuration/schema/session.go b/internal/configuration/schema/session.go
index 2a935ee9a..66719e202 100644
--- a/internal/configuration/schema/session.go
+++ b/internal/configuration/schema/session.go
@@ -2,6 +2,7 @@ package schema
import (
"crypto/tls"
+ "net/url"
"time"
)
@@ -36,24 +37,42 @@ type RedisSessionConfiguration struct {
// SessionConfiguration represents the configuration related to user sessions.
type SessionConfiguration struct {
- Name string `koanf:"name"`
- Domain string `koanf:"domain"`
- SameSite string `koanf:"same_site"`
- Secret string `koanf:"secret"`
- Expiration time.Duration `koanf:"expiration"`
- Inactivity time.Duration `koanf:"inactivity"`
- RememberMeDuration time.Duration `koanf:"remember_me_duration"`
+ Secret string `koanf:"secret"`
+
+ SessionCookieCommonConfiguration `koanf:",squash"`
+
+ Cookies []SessionCookieConfiguration `koanf:"cookies"`
Redis *RedisSessionConfiguration `koanf:"redis"`
}
+type SessionCookieCommonConfiguration struct {
+ Name string `koanf:"name"`
+ Domain string `koanf:"domain"`
+ SameSite string `koanf:"same_site"`
+ Expiration time.Duration `koanf:"expiration"`
+ Inactivity time.Duration `koanf:"inactivity"`
+ RememberMe time.Duration `koanf:"remember_me"`
+
+ DisableRememberMe bool
+}
+
+// SessionCookieConfiguration represents the configuration for a cookie domain.
+type SessionCookieConfiguration struct {
+ SessionCookieCommonConfiguration `koanf:",squash"`
+
+ AutheliaURL *url.URL `koanf:"authelia_url"`
+}
+
// DefaultSessionConfiguration is the default session configuration.
var DefaultSessionConfiguration = SessionConfiguration{
- Name: "authelia_session",
- Expiration: time.Hour,
- Inactivity: time.Minute * 5,
- RememberMeDuration: time.Hour * 24 * 30,
- SameSite: "lax",
+ SessionCookieCommonConfiguration: SessionCookieCommonConfiguration{
+ Name: "authelia_session",
+ Expiration: time.Hour,
+ Inactivity: time.Minute * 5,
+ RememberMe: time.Hour * 24 * 30,
+ SameSite: "lax",
+ },
}
// DefaultRedisConfiguration is the default redis configuration.
diff --git a/internal/configuration/test_resources/config_alt.yml b/internal/configuration/test_resources/config_alt.yml
index 95c8fb89c..7f20e787e 100644
--- a/internal/configuration/test_resources/config_alt.yml
+++ b/internal/configuration/test_resources/config_alt.yml
@@ -97,7 +97,7 @@ session:
name: authelia_session
expiration: 3600000 # 1 hour
inactivity: 300000 # 5 minutes
- remember_me_duration: -1
+ remember_me: -1
domain: example.com
redis:
host: 127.0.0.1
diff --git a/internal/configuration/validator/configuration_test.go b/internal/configuration/validator/configuration_test.go
index 18ff2ae53..0abf16372 100644
--- a/internal/configuration/validator/configuration_test.go
+++ b/internal/configuration/validator/configuration_test.go
@@ -25,9 +25,15 @@ func newDefaultConfig() schema.Configuration {
DefaultPolicy: "two_factor",
}
config.Session = schema.SessionConfiguration{
- Domain: examplecom,
- Name: "authelia_session",
Secret: "secret",
+ Cookies: []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "authelia_session",
+ Domain: exampleDotCom,
+ },
+ },
+ },
}
config.Storage.EncryptionKey = testEncryptionKey
config.Storage.Local = &schema.LocalStorageConfiguration{
@@ -49,6 +55,8 @@ func TestShouldEnsureNotifierConfigIsProvided(t *testing.T) {
ValidateConfiguration(&config, validator)
require.Len(t, validator.Errors(), 0)
+ config = newDefaultConfig()
+
config.Notifier.SMTP = nil
config.Notifier.FileSystem = nil
@@ -135,6 +143,8 @@ func TestShouldRaiseErrorOnInvalidCertificatesDirectory(t *testing.T) {
assert.EqualError(t, validator.Warnings()[0], "access control: no rules have been specified so the 'default_policy' of 'two_factor' is going to be applied to all requests")
+ config = newDefaultConfig()
+
validator = schema.NewStructValidator()
config.CertificatesDirectory = "const.go"
diff --git a/internal/configuration/validator/const.go b/internal/configuration/validator/const.go
index cf1a8eea0..9f68a32cb 100644
--- a/internal/configuration/validator/const.go
+++ b/internal/configuration/validator/const.go
@@ -248,7 +248,7 @@ const (
// Session error constants.
const (
errFmtSessionOptionRequired = "session: option '%s' is required"
- errFmtSessionDomainMustBeRoot = "session: option 'domain' must be the domain you wish to protect not a wildcard domain but it is configured as '%s'"
+ errFmtSessionLegacyAndWarning = "session: option 'domain' and option 'cookies' can't be specified at the same time"
errFmtSessionSameSite = "session: option 'same_site' must be one of '%s' but is configured as '%s'"
errFmtSessionSecretRequired = "session: option 'secret' is required when using the '%s' provider"
errFmtSessionRedisPortRange = "session: redis: option 'port' must be between 1 and 65535 but is configured as '%d'"
@@ -258,6 +258,16 @@ const (
errFmtSessionRedisSentinelMissingName = "session: redis: high_availability: option 'sentinel_name' is required"
errFmtSessionRedisSentinelNodeHostMissing = "session: redis: high_availability: option 'nodes': option 'host' is required for each node but one or more nodes are missing this"
+
+ errFmtSessionDomainMustBeRoot = "session: domain config %s: option 'domain' must be the domain you wish to protect not a wildcard domain but it is configured as '%s'"
+ errFmtSessionDomainSameSite = "session: domain config %s: option 'same_site' must be one of '%s' but is configured as '%s'"
+ errFmtSessionDomainRequired = "session: domain config %s: option 'domain' is required"
+ errFmtSessionDomainHasPeriodPrefix = "session: domain config %s: option 'domain' has a prefix of '.' which is not supported or intended behaviour: you can use this at your own risk but we recommend removing it"
+ errFmtSessionDomainDuplicate = "session: domain config %s: option 'domain' is a duplicate value for another configured session domain"
+ errFmtSessionDomainDuplicateCookieScope = "session: domain config %s: option 'domain' shares the same cookie domain scope as another configured session domain"
+ errFmtSessionDomainPortalURLInsecure = "session: domain config %s: option 'authelia_url' does not have a secure scheme with a value of '%s'"
+ errFmtSessionDomainPortalURLNotInCookieScope = "session: domain config %s: option 'authelia_url' does not share a cookie scope with domain '%s' with a value of '%s'"
+ errFmtSessionDomainInvalidDomain = "session: domain config %s: option 'domain' is not a valid domain"
)
// Regulation Error Consts.
@@ -363,7 +373,10 @@ var (
validOIDCClientConsentModes = []string{"auto", oidc.ClientConsentModeImplicit.String(), oidc.ClientConsentModeExplicit.String(), oidc.ClientConsentModePreConfigured.String()}
)
-var reKeyReplacer = regexp.MustCompile(`\[\d+]`)
+var (
+ reKeyReplacer = regexp.MustCompile(`\[\d+]`)
+ reDomainCharacters = regexp.MustCompile(`^[a-z0-9-]+(\.[a-z0-9-]+)+[a-z0-9]$`)
+)
var replacedKeys = map[string]string{
"authentication_backend.ldap.skip_verify": "authentication_backend.ldap.tls.skip_verify",
diff --git a/internal/configuration/validator/const_test.go b/internal/configuration/validator/const_test.go
index ab398a187..1eea835ba 100644
--- a/internal/configuration/validator/const_test.go
+++ b/internal/configuration/validator/const_test.go
@@ -12,5 +12,5 @@ const (
)
const (
- examplecom = "example.com"
+ exampleDotCom = "example.com"
)
diff --git a/internal/configuration/validator/identity_providers_test.go b/internal/configuration/validator/identity_providers_test.go
index cd3bc0012..d0e345e97 100644
--- a/internal/configuration/validator/identity_providers_test.go
+++ b/internal/configuration/validator/identity_providers_test.go
@@ -257,7 +257,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
RedirectURIs: []string{
"https://google.com",
},
- SectorIdentifier: mustParseURL(examplecom),
+ SectorIdentifier: mustParseURL(exampleDotCom),
},
},
},
@@ -289,12 +289,12 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
},
},
Errors: []string{
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "scheme", "https"),
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "path", "/path"),
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "query", "query=abc"),
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "fragment", "fragment"),
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "username", "user"),
- fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifierWithoutValue, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "password"),
+ fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "scheme", "https"),
+ fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "path", "/path"),
+ fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "query", "query=abc"),
+ fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "fragment", "fragment"),
+ fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "username", "user"),
+ fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifierWithoutValue, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", exampleDotCom, "password"),
},
},
{
diff --git a/internal/configuration/validator/notifier_test.go b/internal/configuration/validator/notifier_test.go
index 7ba756711..c41776a29 100644
--- a/internal/configuration/validator/notifier_test.go
+++ b/internal/configuration/validator/notifier_test.go
@@ -23,7 +23,7 @@ func (suite *NotifierSuite) SetupTest() {
Username: "john",
Password: "password",
Sender: mail.Address{Name: "Authelia", Address: "authelia@example.com"},
- Host: examplecom,
+ Host: exampleDotCom,
Port: 25,
}
suite.config.FileSystem = nil
@@ -78,7 +78,7 @@ func (suite *NotifierSuite) TestSMTPShouldSetTLSDefaults() {
suite.Assert().Len(suite.validator.Warnings(), 0)
suite.Assert().Len(suite.validator.Errors(), 0)
- suite.Assert().Equal(examplecom, suite.config.SMTP.TLS.ServerName)
+ suite.Assert().Equal(exampleDotCom, suite.config.SMTP.TLS.ServerName)
suite.Assert().Equal(uint16(tls.VersionTLS12), suite.config.SMTP.TLS.MinimumVersion.Value)
suite.Assert().False(suite.config.SMTP.TLS.SkipVerify)
}
@@ -111,7 +111,7 @@ func (suite *NotifierSuite) TestSMTPShouldDefaultTLSServerNameToHost() {
}
func (suite *NotifierSuite) TestSMTPShouldErrorOnSSL30() {
- suite.config.SMTP.Host = examplecom
+ suite.config.SMTP.Host = exampleDotCom
suite.config.SMTP.TLS = &schema.TLSConfig{
MinimumVersion: schema.TLSVersion{Value: tls.VersionSSL30}, //nolint:staticcheck
}
@@ -125,7 +125,7 @@ func (suite *NotifierSuite) TestSMTPShouldErrorOnSSL30() {
}
func (suite *NotifierSuite) TestSMTPShouldErrorOnTLSMinVerGreaterThanMaxVer() {
- suite.config.SMTP.Host = examplecom
+ suite.config.SMTP.Host = exampleDotCom
suite.config.SMTP.TLS = &schema.TLSConfig{
MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13},
MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS10},
@@ -140,7 +140,7 @@ func (suite *NotifierSuite) TestSMTPShouldErrorOnTLSMinVerGreaterThanMaxVer() {
}
func (suite *NotifierSuite) TestSMTPShouldWarnOnDisabledSTARTTLS() {
- suite.config.SMTP.Host = examplecom
+ suite.config.SMTP.Host = exampleDotCom
suite.config.SMTP.DisableStartTLS = true
ValidateNotifier(&suite.config, suite.validator)
diff --git a/internal/configuration/validator/session.go b/internal/configuration/validator/session.go
index 17917661d..08fdfeb0e 100644
--- a/internal/configuration/validator/session.go
+++ b/internal/configuration/validator/session.go
@@ -35,18 +35,11 @@ func validateSession(config *schema.SessionConfiguration, validator *schema.Stru
config.Inactivity = schema.DefaultSessionConfiguration.Inactivity // 5 min.
}
- if config.RememberMeDuration <= 0 && config.RememberMeDuration != schema.RememberMeDisabled {
- config.RememberMeDuration = schema.DefaultSessionConfiguration.RememberMeDuration // 1 month.
- }
-
- if config.Domain == "" {
- validator.Push(fmt.Errorf(errFmtSessionOptionRequired, "domain"))
- } else if strings.HasPrefix(config.Domain, ".") {
- validator.PushWarning(fmt.Errorf("session: option 'domain' has a prefix of '.' which is not supported or intended behaviour: you can use this at your own risk but we recommend removing it"))
- }
-
- if strings.HasPrefix(config.Domain, "*.") {
- validator.Push(fmt.Errorf(errFmtSessionDomainMustBeRoot, config.Domain))
+ switch {
+ case config.RememberMe == schema.RememberMeDisabled:
+ config.DisableRememberMe = true
+ case config.RememberMe <= 0:
+ config.RememberMe = schema.DefaultSessionConfiguration.RememberMe // 1 month.
}
if config.SameSite == "" {
@@ -54,6 +47,137 @@ func validateSession(config *schema.SessionConfiguration, validator *schema.Stru
} else if !utils.IsStringInSlice(config.SameSite, validSessionSameSiteValues) {
validator.Push(fmt.Errorf(errFmtSessionSameSite, strings.Join(validSessionSameSiteValues, "', '"), config.SameSite))
}
+
+ cookies := len(config.Cookies)
+
+ switch {
+ case cookies == 0 && config.Domain != "":
+ // Add legacy configuration to the domains list.
+ config.Cookies = append(config.Cookies, schema.SessionCookieConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: config.Name,
+ Domain: config.Domain,
+ SameSite: config.SameSite,
+ Expiration: config.Expiration,
+ Inactivity: config.Inactivity,
+ RememberMe: config.RememberMe,
+ DisableRememberMe: config.DisableRememberMe,
+ },
+ })
+ case cookies != 0 && config.Domain != "":
+ validator.Push(fmt.Errorf(errFmtSessionLegacyAndWarning))
+ }
+
+ validateSessionCookieDomains(config, validator)
+}
+
+func validateSessionCookieDomains(config *schema.SessionConfiguration, validator *schema.StructValidator) {
+ if len(config.Cookies) == 0 {
+ validator.Push(fmt.Errorf(errFmtSessionOptionRequired, "domain"))
+ }
+
+ domains := make([]string, 0)
+
+ for i, d := range config.Cookies {
+ validateSessionDomainName(i, config, validator)
+
+ validateSessionUniqueCookieDomain(i, config, domains, validator)
+
+ validateSessionCookieName(i, config)
+
+ validateSessionSafeRedirection(i, config, validator)
+
+ validateSessionExpiration(i, config)
+
+ validateSessionRememberMe(i, config)
+
+ validateSessionSameSite(i, config, validator)
+
+ domains = append(domains, d.Domain)
+ }
+}
+
+// validateSessionDomainName returns error if the domain name is invalid.
+func validateSessionDomainName(i int, config *schema.SessionConfiguration, validator *schema.StructValidator) {
+ var d = config.Cookies[i]
+
+ switch {
+ case d.Domain == "":
+ validator.Push(fmt.Errorf(errFmtSessionDomainRequired, sessionDomainDescriptor(i, d)))
+ case strings.HasPrefix(d.Domain, "*."):
+ validator.Push(fmt.Errorf(errFmtSessionDomainMustBeRoot, sessionDomainDescriptor(i, d), d.Domain))
+ case strings.HasPrefix(d.Domain, "."):
+ validator.PushWarning(fmt.Errorf(errFmtSessionDomainHasPeriodPrefix, sessionDomainDescriptor(i, d)))
+ case !reDomainCharacters.MatchString(d.Domain):
+ validator.Push(fmt.Errorf(errFmtSessionDomainInvalidDomain, sessionDomainDescriptor(i, d)))
+ }
+}
+
+func validateSessionCookieName(i int, config *schema.SessionConfiguration) {
+ if config.Cookies[i].Name == "" {
+ config.Cookies[i].Name = config.Name
+ }
+}
+
+func validateSessionExpiration(i int, config *schema.SessionConfiguration) {
+ if config.Cookies[i].Expiration <= 0 {
+ config.Cookies[i].Expiration = config.Expiration
+ }
+
+ if config.Cookies[i].Inactivity <= 0 {
+ config.Cookies[i].Inactivity = config.Inactivity
+ }
+}
+
+// validateSessionUniqueCookieDomain Check the current domains do not share a root domain with previous domains.
+func validateSessionUniqueCookieDomain(i int, config *schema.SessionConfiguration, domains []string, validator *schema.StructValidator) {
+ var d = config.Cookies[i]
+ if utils.IsStringInSliceF(d.Domain, domains, utils.HasDomainSuffix) {
+ if utils.IsStringInSlice(d.Domain, domains) {
+ validator.Push(fmt.Errorf(errFmtSessionDomainDuplicate, sessionDomainDescriptor(i, d)))
+ } else {
+ validator.Push(fmt.Errorf(errFmtSessionDomainDuplicateCookieScope, sessionDomainDescriptor(i, d)))
+ }
+ }
+}
+
+// validateSessionSafeRedirection validates that AutheliaURL is safe for redirection.
+func validateSessionSafeRedirection(index int, config *schema.SessionConfiguration, validator *schema.StructValidator) {
+ var d = config.Cookies[index]
+
+ if d.AutheliaURL != nil && d.Domain != "" && !utils.IsURISafeRedirection(d.AutheliaURL, d.Domain) {
+ if utils.IsURISecure(d.AutheliaURL) {
+ validator.Push(fmt.Errorf(errFmtSessionDomainPortalURLNotInCookieScope, sessionDomainDescriptor(index, d), d.Domain, d.AutheliaURL))
+ } else {
+ validator.Push(fmt.Errorf(errFmtSessionDomainPortalURLInsecure, sessionDomainDescriptor(index, d), d.AutheliaURL))
+ }
+ }
+}
+
+func validateSessionRememberMe(i int, config *schema.SessionConfiguration) {
+ if config.Cookies[i].RememberMe <= 0 && config.Cookies[i].RememberMe != schema.RememberMeDisabled {
+ config.Cookies[i].RememberMe = config.RememberMe
+ }
+
+ if config.Cookies[i].RememberMe == schema.RememberMeDisabled {
+ config.Cookies[i].DisableRememberMe = true
+ }
+}
+
+func validateSessionSameSite(i int, config *schema.SessionConfiguration, validator *schema.StructValidator) {
+ if config.Cookies[i].SameSite == "" {
+ if utils.IsStringInSlice(config.SameSite, validSessionSameSiteValues) {
+ config.Cookies[i].SameSite = config.SameSite
+ } else {
+ config.Cookies[i].SameSite = schema.DefaultSessionConfiguration.SameSite
+ }
+ } else if !utils.IsStringInSlice(config.Cookies[i].SameSite, validSessionSameSiteValues) {
+ validator.Push(fmt.Errorf(errFmtSessionDomainSameSite, sessionDomainDescriptor(i, config.Cookies[i]), strings.Join(validSessionSameSiteValues, "', '"), config.Cookies[i].SameSite))
+ }
+}
+
+func sessionDomainDescriptor(position int, domain schema.SessionCookieConfiguration) string {
+ return fmt.Sprintf("#%d (domain '%s')", position+1, domain.Domain)
}
func validateRedisCommon(config *schema.SessionConfiguration, validator *schema.StructValidator) {
diff --git a/internal/configuration/validator/session_test.go b/internal/configuration/validator/session_test.go
index 5a08f988a..d677df581 100644
--- a/internal/configuration/validator/session_test.go
+++ b/internal/configuration/validator/session_test.go
@@ -3,7 +3,9 @@ package validator
import (
"crypto/tls"
"fmt"
+ "net/url"
"testing"
+ "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -14,7 +16,8 @@ import (
func newDefaultSessionConfig() schema.SessionConfiguration {
config := schema.SessionConfiguration{}
config.Secret = testJWTSecret
- config.Domain = examplecom
+ config.Domain = exampleDotCom
+ config.Cookies = []schema.SessionCookieConfiguration{}
return config
}
@@ -30,15 +33,144 @@ func TestShouldSetDefaultSessionValues(t *testing.T) {
assert.Equal(t, schema.DefaultSessionConfiguration.Name, config.Name)
assert.Equal(t, schema.DefaultSessionConfiguration.Inactivity, config.Inactivity)
assert.Equal(t, schema.DefaultSessionConfiguration.Expiration, config.Expiration)
- assert.Equal(t, schema.DefaultSessionConfiguration.RememberMeDuration, config.RememberMeDuration)
+ assert.Equal(t, schema.DefaultSessionConfiguration.RememberMe, config.RememberMe)
assert.Equal(t, schema.DefaultSessionConfiguration.SameSite, config.SameSite)
}
+func TestShouldSetDefaultSessionDomainsValues(t *testing.T) {
+ testCases := []struct {
+ name string
+ have schema.SessionConfiguration
+ expected schema.SessionConfiguration
+ errs []string
+ }{
+ {
+ "ShouldSetGoodDefaultValues",
+ schema.SessionConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: exampleDotCom, SameSite: "lax", Expiration: time.Hour, Inactivity: time.Minute, RememberMe: time.Hour * 2,
+ },
+ },
+ schema.SessionConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "authelia_session", Domain: exampleDotCom, SameSite: "lax", Expiration: time.Hour, Inactivity: time.Minute, RememberMe: time.Hour * 2,
+ },
+ Cookies: []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "authelia_session", Domain: exampleDotCom, SameSite: "lax", Expiration: time.Hour,
+ Inactivity: time.Minute, RememberMe: time.Hour * 2,
+ },
+ },
+ },
+ },
+ nil,
+ },
+ {
+ "ShouldNotSetBadDefaultValues",
+ schema.SessionConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ SameSite: "BAD VALUE", Expiration: time.Hour, Inactivity: time.Minute, RememberMe: time.Hour * 2,
+ },
+ Cookies: []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "authelia_session", Domain: exampleDotCom,
+ Expiration: time.Hour, Inactivity: time.Minute, RememberMe: time.Hour * 2,
+ },
+ },
+ },
+ },
+ schema.SessionConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "authelia_session", SameSite: "BAD VALUE", Expiration: time.Hour, Inactivity: time.Minute, RememberMe: time.Hour * 2,
+ },
+ Cookies: []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "authelia_session", Domain: exampleDotCom, SameSite: schema.DefaultSessionConfiguration.SameSite,
+ Expiration: time.Hour, Inactivity: time.Minute, RememberMe: time.Hour * 2,
+ },
+ },
+ },
+ },
+ []string{
+ "session: option 'same_site' must be one of 'none', 'lax', 'strict' but is configured as 'BAD VALUE'",
+ },
+ },
+ {
+ "ShouldSetDefaultValuesForEachConfig",
+ schema.SessionConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "default_session", SameSite: "lax", Expiration: time.Hour, Inactivity: time.Minute,
+ RememberMe: schema.RememberMeDisabled,
+ },
+ Cookies: []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: exampleDotCom,
+ },
+ },
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: "example2.com", Name: "authelia_session", SameSite: "strict",
+ },
+ },
+ },
+ },
+ schema.SessionConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "default_session", SameSite: "lax", Expiration: time.Hour, Inactivity: time.Minute,
+ RememberMe: schema.RememberMeDisabled, DisableRememberMe: true,
+ },
+ Cookies: []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "default_session", Domain: exampleDotCom, SameSite: "lax",
+ Expiration: time.Hour, Inactivity: time.Minute, RememberMe: schema.RememberMeDisabled, DisableRememberMe: true,
+ },
+ },
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "authelia_session", Domain: "example2.com", SameSite: "strict",
+ Expiration: time.Hour, Inactivity: time.Minute, RememberMe: schema.RememberMeDisabled, DisableRememberMe: true,
+ },
+ },
+ },
+ },
+ nil,
+ },
+ }
+
+ validator := schema.NewStructValidator()
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ validator.Clear()
+
+ have := tc.have
+
+ ValidateSession(&have, validator)
+
+ assert.Len(t, validator.Warnings(), 0)
+
+ errs := validator.Errors()
+ require.Len(t, validator.Errors(), len(tc.errs))
+
+ for i, err := range errs {
+ assert.EqualError(t, err, tc.errs[i])
+ }
+
+ assert.Equal(t, tc.expected, have)
+ })
+ }
+}
+
func TestShouldSetDefaultSessionValuesWhenNegative(t *testing.T) {
validator := schema.NewStructValidator()
config := newDefaultSessionConfig()
- config.Expiration, config.Inactivity, config.RememberMeDuration = -1, -1, -2
+ config.Expiration, config.Inactivity, config.RememberMe = -1, -1, -2
ValidateSession(&config, validator)
@@ -46,7 +178,7 @@ func TestShouldSetDefaultSessionValuesWhenNegative(t *testing.T) {
assert.Len(t, validator.Errors(), 0)
assert.Equal(t, schema.DefaultSessionConfiguration.Inactivity, config.Inactivity)
assert.Equal(t, schema.DefaultSessionConfiguration.Expiration, config.Expiration)
- assert.Equal(t, schema.DefaultSessionConfiguration.RememberMeDuration, config.RememberMeDuration)
+ assert.Equal(t, schema.DefaultSessionConfiguration.RememberMe, config.RememberMe)
}
func TestShouldWarnSessionValuesWhenPotentiallyInvalid(t *testing.T) {
@@ -60,7 +192,7 @@ func TestShouldWarnSessionValuesWhenPotentiallyInvalid(t *testing.T) {
require.Len(t, validator.Warnings(), 1)
assert.Len(t, validator.Errors(), 0)
- assert.EqualError(t, validator.Warnings()[0], "session: option 'domain' has a prefix of '.' which is not supported or intended behaviour: you can use this at your own risk but we recommend removing it")
+ assert.EqualError(t, validator.Warnings()[0], "session: domain config #1 (domain '.example.com'): option 'domain' has a prefix of '.' which is not supported or intended behaviour: you can use this at your own risk but we recommend removing it")
}
func TestShouldHandleRedisConfigSuccessfully(t *testing.T) {
@@ -72,6 +204,8 @@ func TestShouldHandleRedisConfigSuccessfully(t *testing.T) {
assert.Len(t, validator.Errors(), 0)
validator.Clear()
+ config = newDefaultSessionConfig()
+
// Set redis config because password must be set only when redis is used.
config.Redis = &schema.RedisSessionConfiguration{
Host: "redis.localhost",
@@ -81,8 +215,8 @@ func TestShouldHandleRedisConfigSuccessfully(t *testing.T) {
ValidateSession(&config, validator)
- assert.False(t, validator.HasWarnings())
- assert.False(t, validator.HasErrors())
+ assert.Len(t, validator.Warnings(), 0)
+ assert.Len(t, validator.Errors(), 0)
assert.Equal(t, 8, config.Redis.MaximumActiveConnections)
}
@@ -98,7 +232,7 @@ func TestShouldRaiseErrorWithInvalidRedisPortLow(t *testing.T) {
ValidateSession(&config, validator)
- assert.False(t, validator.HasWarnings())
+ require.Len(t, validator.Warnings(), 0)
require.Len(t, validator.Errors(), 1)
assert.EqualError(t, validator.Errors()[0], fmt.Sprintf(errFmtSessionRedisPortRange, -1))
@@ -131,6 +265,9 @@ func TestShouldRaiseErrorWhenRedisIsUsedAndSecretNotSet(t *testing.T) {
assert.Len(t, validator.Errors(), 0)
validator.Clear()
+ config = newDefaultSessionConfig()
+ config.Secret = ""
+
// Set redis config because password must be set only when redis is used.
config.Redis = &schema.RedisSessionConfiguration{
Host: "redis.localhost",
@@ -153,6 +290,8 @@ func TestShouldRaiseErrorWhenRedisHasHostnameButNoPort(t *testing.T) {
assert.Len(t, validator.Errors(), 0)
validator.Clear()
+ config = newDefaultSessionConfig()
+
// Set redis config because password must be set only when redis is used.
config.Redis = &schema.RedisSessionConfiguration{
Host: "redis.localhost",
@@ -287,7 +426,24 @@ func TestShouldRaiseErrorsWhenRedisSentinelOptionsIncorrectlyConfigured(t *testi
validator.Clear()
- config.Redis.Port = -1
+ config = newDefaultSessionConfig()
+
+ config.Secret = ""
+ config.Redis = &schema.RedisSessionConfiguration{
+ Port: -1,
+ HighAvailability: &schema.RedisHighAvailabilityConfiguration{
+ SentinelName: "sentinel",
+ SentinelPassword: "abc123",
+ Nodes: []schema.RedisNode{
+ {
+ Host: "node1",
+ Port: 26379,
+ },
+ },
+ RouteByLatency: true,
+ RouteRandomly: true,
+ },
+ }
ValidateSession(&config, validator)
@@ -434,6 +590,7 @@ func TestShouldRaiseErrorWhenDomainNotSet(t *testing.T) {
validator := schema.NewStructValidator()
config := newDefaultSessionConfig()
config.Domain = ""
+ config.Cookies = []schema.SessionCookieConfiguration{}
ValidateSession(&config, validator)
@@ -449,9 +606,141 @@ func TestShouldRaiseErrorWhenDomainIsWildcard(t *testing.T) {
ValidateSession(&config, validator)
+ assert.Len(t, validator.Warnings(), 0)
+ require.Len(t, validator.Errors(), 1)
+
+ assert.EqualError(t, validator.Errors()[0], "session: domain config #1 (domain '*.example.com'): option 'domain' must be the domain you wish to protect not a wildcard domain but it is configured as '*.example.com'")
+}
+
+func TestShouldRaiseErrorWhenDomainNameIsInvalid(t *testing.T) {
+ validator := schema.NewStructValidator()
+ config := newDefaultSessionConfig()
+ config.Domain = "example!.com"
+
+ ValidateSession(&config, validator)
+
+ assert.Len(t, validator.Warnings(), 0)
+ require.Len(t, validator.Errors(), 1)
+
+ assert.EqualError(t, validator.Errors()[0], "session: domain config #1 (domain 'example!.com'): option 'domain' is not a valid domain")
+}
+
+func TestShouldRaiseErrorWhenHaveDuplicatedDomainName(t *testing.T) {
+ validator := schema.NewStructValidator()
+ config := newDefaultSessionConfig()
+ config.Domain = ""
+ config.Cookies = append(config.Cookies, schema.SessionCookieConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: exampleDotCom,
+ },
+ AutheliaURL: MustParseURL("https://login.example.com"),
+ })
+ config.Cookies = append(config.Cookies, schema.SessionCookieConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: exampleDotCom,
+ },
+ AutheliaURL: MustParseURL("https://login.example.com"),
+ })
+
+ ValidateSession(&config, validator)
assert.False(t, validator.HasWarnings())
assert.Len(t, validator.Errors(), 1)
- assert.EqualError(t, validator.Errors()[0], "session: option 'domain' must be the domain you wish to protect not a wildcard domain but it is configured as '*.example.com'")
+ assert.EqualError(t, validator.Errors()[0], fmt.Sprintf(errFmtSessionDomainDuplicate, sessionDomainDescriptor(1, schema.SessionCookieConfiguration{SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{Domain: exampleDotCom}})))
+}
+
+func TestShouldRaiseErrorWhenSubdomainConflicts(t *testing.T) {
+ validator := schema.NewStructValidator()
+ config := newDefaultSessionConfig()
+ config.Domain = ""
+ config.Cookies = append(config.Cookies, schema.SessionCookieConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: exampleDotCom,
+ },
+ AutheliaURL: MustParseURL("https://login.example.com"),
+ })
+ config.Cookies = append(config.Cookies, schema.SessionCookieConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: "internal.example.com",
+ },
+ AutheliaURL: MustParseURL("https://login.internal.example.com"),
+ })
+
+ ValidateSession(&config, validator)
+ assert.False(t, validator.HasWarnings())
+ assert.Len(t, validator.Errors(), 1)
+ assert.EqualError(t, validator.Errors()[0], "session: domain config #2 (domain 'internal.example.com'): option 'domain' shares the same cookie domain scope as another configured session domain")
+}
+
+func TestShouldRaiseErrorWhenDomainIsInvalid(t *testing.T) {
+ testCases := []struct {
+ name string
+ have string
+ expected []string
+ }{
+ {"ShouldRaiseErrorOnMissingDomain", "", []string{"session: domain config #1 (domain ''): option 'domain' is required"}},
+ {"ShouldNotRaiseErrorOnValidDomain", exampleDotCom, nil},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ validator := schema.NewStructValidator()
+ config := newDefaultSessionConfig()
+ config.Domain = ""
+
+ config.Cookies = []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: tc.have,
+ },
+ AutheliaURL: MustParseURL("https://auth.example.com")},
+ }
+
+ ValidateSession(&config, validator)
+
+ assert.Len(t, validator.Warnings(), 0)
+ require.Len(t, validator.Errors(), len(tc.expected))
+
+ for i, expected := range tc.expected {
+ assert.EqualError(t, validator.Errors()[i], expected)
+ }
+ })
+ }
+}
+
+func TestShouldRaiseErrorWhenPortalURLIsInvalid(t *testing.T) {
+ testCases := []struct {
+ name string
+ have string
+ expected []string
+ }{
+ {"ShouldRaiseErrorOnInvalidScope", "https://example2.com/login", []string{"session: domain config #1 (domain 'example.com'): option 'authelia_url' does not share a cookie scope with domain 'example.com' with a value of 'https://example2.com/login'"}},
+ {"ShouldRaiseErrorOnInvalidScheme", "http://example.com/login", []string{"session: domain config #1 (domain 'example.com'): option 'authelia_url' does not have a secure scheme with a value of 'http://example.com/login'"}},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ validator := schema.NewStructValidator()
+ config := newDefaultSessionConfig()
+ config.Domain = ""
+ config.Cookies = []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "authelia_session",
+ Domain: exampleDotCom,
+ },
+ AutheliaURL: MustParseURL(tc.have)},
+ }
+
+ ValidateSession(&config, validator)
+
+ assert.Len(t, validator.Warnings(), 0)
+ require.Len(t, validator.Errors(), len(tc.expected))
+
+ for i, expected := range tc.expected {
+ assert.EqualError(t, validator.Errors()[i], expected)
+ }
+ })
+ }
}
func TestShouldRaiseErrorWhenSameSiteSetIncorrectly(t *testing.T) {
@@ -462,22 +751,28 @@ func TestShouldRaiseErrorWhenSameSiteSetIncorrectly(t *testing.T) {
ValidateSession(&config, validator)
assert.False(t, validator.HasWarnings())
- assert.Len(t, validator.Errors(), 1)
+ require.Len(t, validator.Errors(), 2)
+
assert.EqualError(t, validator.Errors()[0], "session: option 'same_site' must be one of 'none', 'lax', 'strict' but is configured as 'NOne'")
+ assert.EqualError(t, validator.Errors()[1], "session: domain config #1 (domain 'example.com'): option 'same_site' must be one of 'none', 'lax', 'strict' but is configured as 'NOne'")
}
func TestShouldNotRaiseErrorWhenSameSiteSetCorrectly(t *testing.T) {
validator := schema.NewStructValidator()
- config := newDefaultSessionConfig()
+
+ var config schema.SessionConfiguration
validOptions := []string{"none", "lax", "strict"}
for _, opt := range validOptions {
+ validator.Clear()
+
+ config = newDefaultSessionConfig()
config.SameSite = opt
ValidateSession(&config, validator)
- assert.False(t, validator.HasWarnings())
+ assert.Len(t, validator.Warnings(), 0)
assert.Len(t, validator.Errors(), 0)
}
}
@@ -487,7 +782,7 @@ func TestShouldSetDefaultWhenNegativeAndNotOverrideDisabledRememberMe(t *testing
config := newDefaultSessionConfig()
config.Inactivity = -1
config.Expiration = -1
- config.RememberMeDuration = schema.RememberMeDisabled
+ config.RememberMe = schema.RememberMeDisabled
ValidateSession(&config, validator)
@@ -496,7 +791,8 @@ func TestShouldSetDefaultWhenNegativeAndNotOverrideDisabledRememberMe(t *testing
assert.Equal(t, schema.DefaultSessionConfiguration.Inactivity, config.Inactivity)
assert.Equal(t, schema.DefaultSessionConfiguration.Expiration, config.Expiration)
- assert.Equal(t, schema.RememberMeDisabled, config.RememberMeDuration)
+ assert.Equal(t, schema.RememberMeDisabled, config.RememberMe)
+ assert.True(t, config.DisableRememberMe)
}
func TestShouldSetDefaultRememberMeDuration(t *testing.T) {
@@ -505,7 +801,41 @@ func TestShouldSetDefaultRememberMeDuration(t *testing.T) {
ValidateSession(&config, validator)
- assert.False(t, validator.HasWarnings())
- assert.False(t, validator.HasErrors())
- assert.Equal(t, config.RememberMeDuration, schema.DefaultSessionConfiguration.RememberMeDuration)
+ assert.Len(t, validator.Warnings(), 0)
+ assert.Len(t, validator.Errors(), 0)
+
+ assert.Equal(t, config.RememberMe, schema.DefaultSessionConfiguration.RememberMe)
+}
+
+func TestShouldNotAllowLegacyAndModernCookiesConfig(t *testing.T) {
+ validator := schema.NewStructValidator()
+ config := newDefaultSessionConfig()
+
+ config.Cookies = append(config.Cookies, schema.SessionCookieConfiguration{
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: config.Name,
+ Domain: config.Domain,
+ SameSite: config.SameSite,
+ Expiration: config.Expiration,
+ Inactivity: config.Inactivity,
+ RememberMe: config.RememberMe,
+ },
+ })
+
+ ValidateSession(&config, validator)
+
+ assert.Len(t, validator.Warnings(), 0)
+ require.Len(t, validator.Errors(), 1)
+
+ assert.EqualError(t, validator.Errors()[0], "session: option 'domain' and option 'cookies' can't be specified at the same time")
+}
+
+func MustParseURL(uri string) *url.URL {
+ u, err := url.ParseRequestURI(uri)
+
+ if err != nil {
+ panic(err)
+ }
+
+ return u
}
diff --git a/internal/handlers/const.go b/internal/handlers/const.go
index ebd15e790..a34d81ed6 100644
--- a/internal/handlers/const.go
+++ b/internal/handlers/const.go
@@ -113,6 +113,7 @@ const (
testInactivity = time.Second * 10
testRedirectionURL = "http://redirection.local"
testUsername = "john"
+ exampleDotCom = "example.com"
)
// Duo constants.
diff --git a/internal/handlers/handler_checks_safe_redirection.go b/internal/handlers/handler_checks_safe_redirection.go
index 96b66114e..be1eef04d 100644
--- a/internal/handlers/handler_checks_safe_redirection.go
+++ b/internal/handlers/handler_checks_safe_redirection.go
@@ -2,9 +2,9 @@ package handlers
import (
"fmt"
+ "net/url"
"github.com/authelia/authelia/v4/internal/middlewares"
- "github.com/authelia/authelia/v4/internal/utils"
)
// CheckSafeRedirectionPOST handler checking whether the redirection to a given URL provided in body is safe.
@@ -16,24 +16,23 @@ func CheckSafeRedirectionPOST(ctx *middlewares.AutheliaCtx) {
return
}
- var reqBody checkURIWithinDomainRequestBody
+ var (
+ bodyJSON checkURIWithinDomainRequestBody
+ targetURI *url.URL
+ err error
+ )
- err := ctx.ParseBody(&reqBody)
- if err != nil {
+ if err = ctx.ParseBody(&bodyJSON); err != nil {
ctx.Error(fmt.Errorf("unable to parse request body: %w", err), messageOperationFailed)
return
}
- safe, err := utils.IsURIStringSafeRedirection(reqBody.URI, ctx.Configuration.Session.Domain)
- if err != nil {
- ctx.Error(fmt.Errorf("unable to determine if uri %s is safe to redirect to: %w", reqBody.URI, err), messageOperationFailed)
+ if targetURI, err = url.ParseRequestURI(bodyJSON.URI); err != nil {
+ ctx.Error(fmt.Errorf("unable to determine if uri %s is safe to redirect to: failed to parse URI '%s': %w", bodyJSON.URI, bodyJSON.URI, err), messageOperationFailed)
return
}
- err = ctx.SetJSONBody(checkURIWithinDomainResponseBody{
- OK: safe,
- })
- if err != nil {
+ if err = ctx.SetJSONBody(checkURIWithinDomainResponseBody{OK: ctx.IsSafeRedirectionTargetURI(targetURI)}); err != nil {
ctx.Error(fmt.Errorf("unable to create response body: %w", err), messageOperationFailed)
return
}
diff --git a/internal/handlers/handler_checks_safe_redirection_test.go b/internal/handlers/handler_checks_safe_redirection_test.go
index a77fefabb..cb5ec41de 100644
--- a/internal/handlers/handler_checks_safe_redirection_test.go
+++ b/internal/handlers/handler_checks_safe_redirection_test.go
@@ -4,64 +4,78 @@ import (
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/valyala/fasthttp"
"github.com/authelia/authelia/v4/internal/authentication"
"github.com/authelia/authelia/v4/internal/mocks"
"github.com/authelia/authelia/v4/internal/session"
)
-var exampleDotComDomain = "example.com"
+func TestCheckSafeRedirection(t *testing.T) {
+ testCases := []struct {
+ name string
+ userSession session.UserSession
+ have string
+ expected int
+ ok bool
+ }{
+ {
+ "ShouldReturnUnauthorized",
+ session.UserSession{AuthenticationLevel: authentication.NotAuthenticated},
+ "http://myapp.example.com",
+ fasthttp.StatusUnauthorized,
+ false,
+ },
+ {
+ "ShouldReturnTrueOnGoodDomain",
+ session.UserSession{Username: "john", AuthenticationLevel: authentication.OneFactor},
+ "https://myapp.example.com",
+ fasthttp.StatusOK,
+ true,
+ },
+ {
+ "ShouldReturnFalseOnGoodDomainWithBadScheme",
+ session.UserSession{Username: "john", AuthenticationLevel: authentication.OneFactor},
+ "http://myapp.example.com",
+ fasthttp.StatusOK,
+ false,
+ },
+ {
+ "ShouldReturnFalseOnBadDomainWithGoodScheme",
+ session.UserSession{Username: "john", AuthenticationLevel: authentication.OneFactor},
+ "https://myapp.notgood.com",
+ fasthttp.StatusOK,
+ false,
+ },
+ {
+ "ShouldReturnFalseOnBadDomainWithBadScheme",
+ session.UserSession{Username: "john", AuthenticationLevel: authentication.OneFactor},
+ "http://myapp.notgood.com",
+ fasthttp.StatusOK,
+ false,
+ },
+ }
-func TestCheckSafeRedirection_ForbiddenCall(t *testing.T) {
- mock := mocks.NewMockAutheliaCtxWithUserSession(t, session.UserSession{
- Username: "john",
- AuthenticationLevel: authentication.NotAuthenticated,
- })
- defer mock.Close()
- mock.Ctx.Configuration.Session.Domain = exampleDotComDomain
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ mock := mocks.NewMockAutheliaCtxWithUserSession(t, tc.userSession)
+ defer mock.Close()
- mock.SetRequestBody(t, checkURIWithinDomainRequestBody{
- URI: "http://myapp.example.com",
- })
+ mock.SetRequestBody(t, checkURIWithinDomainRequestBody{
+ URI: tc.have,
+ })
- CheckSafeRedirectionPOST(mock.Ctx)
- assert.Equal(t, 401, mock.Ctx.Response.StatusCode())
-}
+ CheckSafeRedirectionPOST(mock.Ctx)
-func TestCheckSafeRedirection_UnsafeRedirection(t *testing.T) {
- mock := mocks.NewMockAutheliaCtxWithUserSession(t, session.UserSession{
- Username: "john",
- AuthenticationLevel: authentication.OneFactor,
- })
- defer mock.Close()
- mock.Ctx.Configuration.Session.Domain = exampleDotComDomain
+ assert.Equal(t, tc.expected, mock.Ctx.Response.StatusCode())
- mock.SetRequestBody(t, checkURIWithinDomainRequestBody{
- URI: "http://myapp.com",
- })
-
- CheckSafeRedirectionPOST(mock.Ctx)
- mock.Assert200OK(t, checkURIWithinDomainResponseBody{
- OK: false,
- })
-}
-
-func TestCheckSafeRedirection_SafeRedirection(t *testing.T) {
- mock := mocks.NewMockAutheliaCtxWithUserSession(t, session.UserSession{
- Username: "john",
- AuthenticationLevel: authentication.OneFactor,
- })
- defer mock.Close()
- mock.Ctx.Configuration.Session.Domain = exampleDotComDomain
-
- mock.SetRequestBody(t, checkURIWithinDomainRequestBody{
- URI: "https://myapp.example.com",
- })
-
- CheckSafeRedirectionPOST(mock.Ctx)
- mock.Assert200OK(t, checkURIWithinDomainResponseBody{
- OK: true,
- })
+ if tc.expected == fasthttp.StatusOK {
+ mock.Assert200OK(t, checkURIWithinDomainResponseBody{
+ OK: tc.ok,
+ })
+ }
+ })
+ }
}
func TestShouldFailOnInvalidBody(t *testing.T) {
@@ -70,7 +84,7 @@ func TestShouldFailOnInvalidBody(t *testing.T) {
AuthenticationLevel: authentication.OneFactor,
})
defer mock.Close()
- mock.Ctx.Configuration.Session.Domain = exampleDotComDomain
+ mock.Ctx.Configuration.Session.Domain = exampleDotCom
mock.SetRequestBody(t, "not a valid json")
@@ -84,7 +98,7 @@ func TestShouldFailOnInvalidURL(t *testing.T) {
AuthenticationLevel: authentication.OneFactor,
})
defer mock.Close()
- mock.Ctx.Configuration.Session.Domain = exampleDotComDomain
+ mock.Ctx.Configuration.Session.Domain = exampleDotCom
mock.SetRequestBody(t, checkURIWithinDomainRequestBody{
URI: "https//invalid-url",
diff --git a/internal/handlers/handler_firstfactor.go b/internal/handlers/handler_firstfactor.go
index f52119576..3f896fb28 100644
--- a/internal/handlers/handler_firstfactor.go
+++ b/internal/handlers/handler_firstfactor.go
@@ -4,7 +4,6 @@ import (
"errors"
"time"
- "github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/regulation"
"github.com/authelia/authelia/v4/internal/session"
@@ -72,7 +71,25 @@ func FirstFactorPOST(delayFunc middlewares.TimingAttackDelayFunc) middlewares.Re
return
}
- userSession := ctx.GetSession()
+ // TODO: write tests.
+ provider, err := ctx.GetSessionProvider()
+ if err != nil {
+ ctx.Logger.Errorf("%s", err)
+
+ respondUnauthorized(ctx, messageAuthenticationFailed)
+
+ return
+ }
+
+ userSession, err := provider.GetSession(ctx.RequestCtx)
+ if err != nil {
+ ctx.Logger.Errorf("%s", err)
+
+ respondUnauthorized(ctx, messageAuthenticationFailed)
+
+ return
+ }
+
newSession := session.NewDefaultUserSession()
// Reset all values from previous session except OIDC workflow before regenerating the cookie.
@@ -84,7 +101,7 @@ func FirstFactorPOST(delayFunc middlewares.TimingAttackDelayFunc) middlewares.Re
return
}
- if err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx); err != nil {
+ if err = ctx.RegenerateSession(); err != nil {
ctx.Logger.Errorf(logFmtErrSessionRegenerate, regulation.AuthType1FA, bodyJSON.Username, err)
respondUnauthorized(ctx, messageAuthenticationFailed)
@@ -93,11 +110,11 @@ func FirstFactorPOST(delayFunc middlewares.TimingAttackDelayFunc) middlewares.Re
}
// Check if bodyJSON.KeepMeLoggedIn can be deref'd and derive the value based on the configuration and JSON data.
- keepMeLoggedIn := ctx.Providers.SessionProvider.RememberMe != schema.RememberMeDisabled && bodyJSON.KeepMeLoggedIn != nil && *bodyJSON.KeepMeLoggedIn
+ keepMeLoggedIn := !provider.Config.DisableRememberMe && bodyJSON.KeepMeLoggedIn != nil && *bodyJSON.KeepMeLoggedIn
// Set the cookie to expire if remember me is enabled and the user has asked us to.
if keepMeLoggedIn {
- err = ctx.Providers.SessionProvider.UpdateExpiration(ctx.RequestCtx, ctx.Providers.SessionProvider.RememberMe)
+ err = provider.UpdateExpiration(ctx.RequestCtx, provider.Config.RememberMe)
if err != nil {
ctx.Logger.Errorf(logFmtErrSessionSave, "updated expiration", regulation.AuthType1FA, bodyJSON.Username, err)
diff --git a/internal/handlers/handler_logout.go b/internal/handlers/handler_logout.go
index 0f491dd87..7401c8baf 100644
--- a/internal/handlers/handler_logout.go
+++ b/internal/handlers/handler_logout.go
@@ -5,7 +5,6 @@ import (
"net/url"
"github.com/authelia/authelia/v4/internal/middlewares"
- "github.com/authelia/authelia/v4/internal/utils"
)
type logoutBody struct {
@@ -26,14 +25,14 @@ func LogoutPOST(ctx *middlewares.AutheliaCtx) {
ctx.Error(fmt.Errorf("unable to parse body during logout: %s", err), messageOperationFailed)
}
- err = ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx)
+ err = ctx.DestroySession()
if err != nil {
ctx.Error(fmt.Errorf("unable to destroy session during logout: %s", err), messageOperationFailed)
}
redirectionURL, err := url.ParseRequestURI(body.TargetURL)
if err == nil {
- responseBody.SafeTargetURL = utils.IsURISafeRedirection(redirectionURL, ctx.Configuration.Session.Domain)
+ responseBody.SafeTargetURL = ctx.IsSafeRedirectionTargetURI(redirectionURL)
}
if body.TargetURL != "" {
diff --git a/internal/handlers/handler_oidc_authorization.go b/internal/handlers/handler_oidc_authorization.go
index 73e821927..887f11088 100644
--- a/internal/handlers/handler_oidc_authorization.go
+++ b/internal/handlers/handler_oidc_authorization.go
@@ -111,5 +111,9 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
return
}
+ if requester.GetResponseMode() == oidc.ResponseModeFormPost {
+ ctx.SetUserValueBytes(middlewares.UserValueKeyFormPost, true)
+ }
+
ctx.Providers.OpenIDConnect.WriteAuthorizeResponse(ctx, rw, requester, responder)
}
diff --git a/internal/handlers/handler_sign_duo.go b/internal/handlers/handler_sign_duo.go
index 0ebe285b0..609edca76 100644
--- a/internal/handlers/handler_sign_duo.go
+++ b/internal/handlers/handler_sign_duo.go
@@ -246,7 +246,7 @@ func HandleAutoSelection(ctx *middlewares.AutheliaCtx, devices []DuoDevice, user
func HandleAllow(ctx *middlewares.AutheliaCtx, bodyJSON *bodySignDuoRequest) {
userSession := ctx.GetSession()
- err := ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx)
+ err := ctx.RegenerateSession()
if err != nil {
ctx.Logger.Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeDuo, userSession.Username, err)
diff --git a/internal/handlers/handler_sign_duo_test.go b/internal/handlers/handler_sign_duo_test.go
index c08c8f1ee..70765bf71 100644
--- a/internal/handlers/handler_sign_duo_test.go
+++ b/internal/handlers/handler_sign_duo_test.go
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
+ "github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/duo"
"github.com/authelia/authelia/v4/internal/mocks"
"github.com/authelia/authelia/v4/internal/model"
@@ -579,6 +580,18 @@ func (s *SecondFactorDuoPostSuite) TestShouldNotReturnRedirectURL() {
func (s *SecondFactorDuoPostSuite) TestShouldRedirectUserToSafeTargetURL() {
duoMock := mocks.NewMockAPI(s.mock.Ctrl)
+ s.mock.Ctx.Configuration.Session.Cookies = []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: "example.com",
+ },
+ },
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: "mydomain.local",
+ },
+ },
+ }
s.mock.StorageMock.EXPECT().
LoadPreferredDuoDevice(s.mock.Ctx, "john").
Return(&model.DuoDevice{ID: 1, Username: "john", Device: "12345ABCDEFGHIJ67890", Method: "push"}, nil)
diff --git a/internal/handlers/handler_sign_totp.go b/internal/handlers/handler_sign_totp.go
index 3d584fddd..1825dcb84 100644
--- a/internal/handlers/handler_sign_totp.go
+++ b/internal/handlers/handler_sign_totp.go
@@ -50,7 +50,7 @@ func TimeBasedOneTimePasswordPOST(ctx *middlewares.AutheliaCtx) {
return
}
- if err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx); err != nil {
+ if err = ctx.RegenerateSession(); err != nil {
ctx.Logger.Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeTOTP, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
diff --git a/internal/handlers/handler_sign_totp_test.go b/internal/handlers/handler_sign_totp_test.go
index 6ade2e0dc..b4cd12086 100644
--- a/internal/handlers/handler_sign_totp_test.go
+++ b/internal/handlers/handler_sign_totp_test.go
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
+ "github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/mocks"
"github.com/authelia/authelia/v4/internal/model"
"github.com/authelia/authelia/v4/internal/regulation"
@@ -143,6 +144,18 @@ func (s *HandlerSignTOTPSuite) TestShouldNotReturnRedirectURL() {
func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToSafeTargetURL() {
config := model.TOTPConfiguration{ID: 1, Username: "john", Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"}
+ s.mock.Ctx.Configuration.Session.Cookies = []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: "example.com",
+ },
+ },
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Domain: "mydomain.local",
+ },
+ },
+ }
s.mock.StorageMock.EXPECT().
LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()).
diff --git a/internal/handlers/handler_sign_webauthn.go b/internal/handlers/handler_sign_webauthn.go
index 89a980626..571f2d8cf 100644
--- a/internal/handlers/handler_sign_webauthn.go
+++ b/internal/handlers/handler_sign_webauthn.go
@@ -44,7 +44,7 @@ func WebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
extensions := map[string]any{}
if user.HasFIDOU2F() {
- extensions["appid"] = w.Config.RPOrigin
+ extensions["appid"] = w.Config.RPOrigins[0]
}
if len(extensions) != 0 {
@@ -171,7 +171,7 @@ func WebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
return
}
- if err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx); err != nil {
+ if err = ctx.RegenerateSession(); err != nil {
ctx.Logger.Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeWebauthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
diff --git a/internal/handlers/handler_user_info_test.go b/internal/handlers/handler_user_info_test.go
index 47bc9a6b8..1fb8e2a83 100644
--- a/internal/handlers/handler_user_info_test.go
+++ b/internal/handlers/handler_user_info_test.go
@@ -259,9 +259,11 @@ func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) {
}
mock := mocks.NewMockAutheliaCtx(t)
+ sessionConfig := mock.Ctx.Configuration.Session
if resp.config != nil {
mock.Ctx.Configuration = *resp.config
+ mock.Ctx.Configuration.Session = sessionConfig
}
// Set the initial user session.
diff --git a/internal/handlers/handler_verify.go b/internal/handlers/handler_verify.go
index 258aec61d..723a05c69 100644
--- a/internal/handlers/handler_verify.go
+++ b/internal/handlers/handler_verify.go
@@ -19,14 +19,6 @@ import (
"github.com/authelia/authelia/v4/internal/utils"
)
-func isURLUnderProtectedDomain(url *url.URL, domain string) bool {
- return strings.HasSuffix(url.Hostname(), domain)
-}
-
-func isSchemeHTTPS(url *url.URL) bool {
- return url.Scheme == "https"
-}
-
func isSchemeWSS(url *url.URL) bool {
return url.Scheme == "wss"
}
@@ -54,7 +46,7 @@ func parseBasicAuth(header []byte, auth string) (username, password string, err
}
// isTargetURLAuthorized check whether the given user is authorized to access the resource.
-func isTargetURLAuthorized(authorizer *authorization.Authorizer, targetURL url.URL,
+func isTargetURLAuthorized(authorizer *authorization.Authorizer, targetURL *url.URL,
username string, userGroups []string, clientIP net.IP, method []byte, authLevel authentication.Level) authorizationMatching {
hasSubject, level := authorizer.GetRequiredLevel(
authorization.Subject{
@@ -62,7 +54,7 @@ func isTargetURLAuthorized(authorizer *authorization.Authorizer, targetURL url.U
Groups: userGroups,
IP: clientIP,
},
- authorization.NewObjectRaw(&targetURL, method))
+ authorization.NewObjectRaw(targetURL, method))
switch {
case level == authorization.Bypass:
@@ -128,13 +120,18 @@ func setForwardedHeaders(headers *fasthttp.ResponseHeader, username, name string
}
func isSessionInactiveTooLong(ctx *middlewares.AutheliaCtx, userSession *session.UserSession, isUserAnonymous bool) (isInactiveTooLong bool) {
- if userSession.KeepMeLoggedIn || isUserAnonymous || int64(ctx.Providers.SessionProvider.Inactivity.Seconds()) == 0 {
+ domainSession, err := ctx.GetSessionProvider()
+ if err != nil {
return false
}
- isInactiveTooLong = time.Unix(userSession.LastActivity, 0).Add(ctx.Providers.SessionProvider.Inactivity).Before(ctx.Clock.Now())
+ if userSession.KeepMeLoggedIn || isUserAnonymous || int64(domainSession.Config.Inactivity.Seconds()) == 0 {
+ return false
+ }
- ctx.Logger.Tracef("Inactivity report for user '%s'. Current Time: %d, Last Activity: %d, Maximum Inactivity: %d.", userSession.Username, ctx.Clock.Now().Unix(), userSession.LastActivity, int(ctx.Providers.SessionProvider.Inactivity.Seconds()))
+ isInactiveTooLong = time.Unix(userSession.LastActivity, 0).Add(domainSession.Config.Inactivity).Before(ctx.Clock.Now())
+
+ ctx.Logger.Tracef("Inactivity report for user '%s'. Current Time: %d, Last Activity: %d, Maximum Inactivity: %d.", userSession.Username, ctx.Clock.Now().Unix(), userSession.LastActivity, int(domainSession.Config.Inactivity.Seconds()))
return isInactiveTooLong
}
@@ -151,7 +148,7 @@ func verifySessionCookie(ctx *middlewares.AutheliaCtx, targetURL *url.URL, userS
if isSessionInactiveTooLong(ctx, userSession, isUserAnonymous) {
// Destroy the session a new one will be regenerated on next request.
- if err = ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx); err != nil {
+ if err = ctx.DestroySession(); err != nil {
return "", "", nil, nil, authentication.NotAuthenticated, fmt.Errorf("unable to destroy session for user '%s' after the session has been inactive too long: %w", userSession.Username, err)
}
@@ -162,7 +159,7 @@ func verifySessionCookie(ctx *middlewares.AutheliaCtx, targetURL *url.URL, userS
if err = verifySessionHasUpToDateProfile(ctx, targetURL, userSession, refreshProfile, refreshProfileInterval); err != nil {
if err == authentication.ErrUserNotFound {
- if err = ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx); err != nil {
+ if err = ctx.DestroySession(); err != nil {
ctx.Logger.Errorf("Unable to destroy user session after provider refresh didn't find the user: %v", err)
}
@@ -177,7 +174,7 @@ func verifySessionCookie(ctx *middlewares.AutheliaCtx, targetURL *url.URL, userS
return userSession.Username, userSession.DisplayName, userSession.Groups, userSession.Emails, userSession.AuthenticationLevel, nil
}
-func handleUnauthorized(ctx *middlewares.AutheliaCtx, targetURL fmt.Stringer, isBasicAuth bool, username string, method []byte) {
+func handleUnauthorized(ctx *middlewares.AutheliaCtx, targetURL fmt.Stringer, cookieDomain string, isBasicAuth bool, username string, method []byte) {
var (
statusCode int
friendlyUsername string
@@ -211,8 +208,8 @@ func handleUnauthorized(ctx *middlewares.AutheliaCtx, targetURL fmt.Stringer, is
redirectionURL := ctxGetPortalURL(ctx)
if redirectionURL != nil {
- if !utils.IsURISafeRedirection(redirectionURL, ctx.Configuration.Session.Domain) {
- ctx.Logger.Errorf("Configured Portal URL '%s' does not appear to be able to write cookies for the '%s' domain", redirectionURL, ctx.Configuration.Session.Domain)
+ if !utils.IsURISafeRedirection(redirectionURL, cookieDomain) {
+ ctx.Logger.Errorf("Configured Portal URL '%s' does not appear to be able to write cookies for the '%s' domain", redirectionURL, cookieDomain)
ctx.ReplyUnauthorized()
@@ -414,6 +411,7 @@ func verifyAuth(ctx *middlewares.AutheliaCtx, targetURL *url.URL, refreshProfile
}
userSession := ctx.GetSession()
+
if username, name, groups, emails, authLevel, err = verifySessionCookie(ctx, targetURL, &userSession, refreshProfile, refreshProfileInterval); err != nil {
return isBasicAuth, username, name, groups, emails, authLevel, err
}
@@ -422,7 +420,7 @@ func verifyAuth(ctx *middlewares.AutheliaCtx, targetURL *url.URL, refreshProfile
if sessionUsername != nil && !strings.EqualFold(string(sessionUsername), username) {
ctx.Logger.Warnf("Possible cookie hijack or attempt to bypass security detected destroying the session and sending 401 response")
- if err = ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx); err != nil {
+ if err = ctx.DestroySession(); err != nil {
ctx.Logger.Errorf("Unable to destroy user session after handler could not match them to their %s header: %s", headerSessionUsername, err)
}
@@ -447,7 +445,7 @@ func VerifyGET(cfg schema.AuthenticationBackend) middlewares.RequestHandler {
return
}
- if !isSchemeHTTPS(targetURL) && !isSchemeWSS(targetURL) {
+ if !utils.IsURISecure(targetURL) {
ctx.Logger.Errorf("Scheme of target URL %s must be secure since cookies are "+
"only transported over a secure connection for security reasons", targetURL.String())
ctx.ReplyUnauthorized()
@@ -455,14 +453,32 @@ func VerifyGET(cfg schema.AuthenticationBackend) middlewares.RequestHandler {
return
}
- if !isURLUnderProtectedDomain(targetURL, ctx.Configuration.Session.Domain) {
- ctx.Logger.Errorf("Target URL %s is not under the protected domain %s",
- targetURL.String(), ctx.Configuration.Session.Domain)
+ cookieDomain := ctx.GetTargetURICookieDomain(targetURL)
+
+ if cookieDomain == "" {
+ l := len(ctx.Configuration.Session.Cookies)
+
+ if l == 1 {
+ ctx.Logger.Errorf("Target URL '%s' was not detected as a match to the '%s' session cookie domain",
+ targetURL.String(), ctx.Configuration.Session.Cookies[0].Domain)
+ } else {
+ domains := make([]string, 0, len(ctx.Configuration.Session.Cookies))
+
+ for i, domain := range ctx.Configuration.Session.Cookies {
+ domains[i] = domain.Domain
+ }
+
+ ctx.Logger.Errorf("Target URL '%s' was not detected as a match to any of the '%s' session cookie domains",
+ targetURL.String(), strings.Join(domains, "', '"))
+ }
+
ctx.ReplyUnauthorized()
return
}
+ ctx.Logger.Debugf("Target URL '%s' was detected as a match to the '%s' session cookie domain", targetURL.String(), cookieDomain)
+
method := ctx.XForwardedMethod()
isBasicAuth, username, name, groups, emails, authLevel, err := verifyAuth(ctx, targetURL, refreshProfile, refreshProfileInterval)
@@ -474,12 +490,12 @@ func VerifyGET(cfg schema.AuthenticationBackend) middlewares.RequestHandler {
return
}
- handleUnauthorized(ctx, targetURL, isBasicAuth, username, method)
+ handleUnauthorized(ctx, targetURL, cookieDomain, isBasicAuth, username, method)
return
}
- authorized := isTargetURLAuthorized(ctx.Providers.Authorizer, *targetURL, username,
+ authorized := isTargetURLAuthorized(ctx.Providers.Authorizer, targetURL, username,
groups, ctx.RemoteIP(), method, authLevel)
switch authorized {
@@ -487,7 +503,7 @@ func VerifyGET(cfg schema.AuthenticationBackend) middlewares.RequestHandler {
ctx.Logger.Infof("Access to %s is forbidden to user %s", targetURL.String(), username)
ctx.ReplyForbidden()
case NotAuthorized:
- handleUnauthorized(ctx, targetURL, isBasicAuth, username, method)
+ handleUnauthorized(ctx, targetURL, cookieDomain, isBasicAuth, username, method)
case Authorized:
setForwardedHeaders(&ctx.Response.Header, username, name, groups, emails)
}
diff --git a/internal/handlers/handler_verify_test.go b/internal/handlers/handler_verify_test.go
index 518ac7ec0..20cbf8f22 100644
--- a/internal/handlers/handler_verify_test.go
+++ b/internal/handlers/handler_verify_test.go
@@ -43,6 +43,8 @@ func TestShouldRaiseWhenTargetUrlIsMalformed(t *testing.T) {
func TestShouldRaiseWhenNoHeaderProvidedToDetectTargetURL(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
+ mock.Ctx.Request.Header.Del("X-Forwarded-Host")
+
defer mock.Close()
_, err := mock.Ctx.GetOriginalURL()
assert.Error(t, err)
@@ -53,6 +55,7 @@ func TestShouldRaiseWhenNoXForwardedHostHeaderProvidedToDetectTargetURL(t *testi
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
+ mock.Ctx.Request.Header.Del("X-Forwarded-Host")
mock.Ctx.Request.Header.Set("X-Forwarded-Proto", "https")
_, err := mock.Ctx.GetOriginalURL()
assert.Error(t, err)
@@ -162,7 +165,7 @@ func TestShouldCheckAuthorizationMatching(t *testing.T) {
username = testUsername
}
- matching := isTargetURLAuthorized(authorizer, *u, username, []string{}, net.ParseIP("127.0.0.1"), []byte("GET"), rule.AuthLevel)
+ matching := isTargetURLAuthorized(authorizer, u, username, []string{}, net.ParseIP("127.0.0.1"), []byte("GET"), rule.AuthLevel)
assert.Equal(t, rule.ExpectedMatching, matching, "policy=%s, authLevel=%v, expected=%v, actual=%v",
rule.Policy, rule.AuthLevel, rule.ExpectedMatching, matching)
}
@@ -510,7 +513,6 @@ func TestShouldNotCrashOnEmptyEmail(t *testing.T) {
userSession.AuthenticationLevel = authentication.OneFactor
userSession.RefreshTTL = mock.Clock.Now().Add(5 * time.Minute)
- fmt.Printf("Time is %v\n", userSession.RefreshTTL)
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
@@ -663,32 +665,33 @@ func TestShouldVerifyAuthorizationsUsingSessionCookie(t *testing.T) {
{"https://deny.example.com", "john", []string{"john.doe@example.com"}, authentication.TwoFactor, 403},
}
- for _, testCase := range testCases {
- testCase := testCase
- t.Run(testCase.String(), func(t *testing.T) {
+ for i, tc := range testCases {
+ t.Run(tc.String(), func(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Clock.Set(time.Now())
+ mock.Ctx.Request.Header.Set("X-Original-URL", tc.URL)
+
userSession := mock.Ctx.GetSession()
- userSession.Username = testCase.Username
- userSession.Emails = testCase.Emails
- userSession.AuthenticationLevel = testCase.AuthenticationLevel
+ userSession.Username = tc.Username
+ userSession.Emails = tc.Emails
+ userSession.AuthenticationLevel = tc.AuthenticationLevel
userSession.RefreshTTL = mock.Clock.Now().Add(5 * time.Minute)
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
- mock.Ctx.Request.Header.Set("X-Original-URL", testCase.URL)
-
VerifyGET(verifyGetCfg)(mock.Ctx)
- expStatus, actualStatus := testCase.ExpectedStatusCode, mock.Ctx.Response.StatusCode()
+ expStatus, actualStatus := tc.ExpectedStatusCode, mock.Ctx.Response.StatusCode()
assert.Equal(t, expStatus, actualStatus, "URL=%s -> AuthLevel=%d, StatusCode=%d != ExpectedStatusCode=%d",
- testCase.URL, testCase.AuthenticationLevel, actualStatus, expStatus)
+ tc.URL, tc.AuthenticationLevel, actualStatus, expStatus)
- if testCase.ExpectedStatusCode == 200 && testCase.Username != "" {
- assert.Equal(t, []byte(testCase.Username), mock.Ctx.Response.Header.Peek("Remote-User"))
+ fmt.Println(i)
+ if tc.ExpectedStatusCode == 200 && tc.Username != "" {
+ assert.Equal(t, tc.ExpectedStatusCode, mock.Ctx.Response.StatusCode())
+ assert.Equal(t, []byte(tc.Username), mock.Ctx.Response.Header.Peek("Remote-User"))
assert.Equal(t, []byte("john.doe@example.com"), mock.Ctx.Response.Header.Peek("Remote-Email"))
} else {
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek("Remote-User"))
@@ -706,10 +709,12 @@ func TestShouldDestroySessionWhenInactiveForTooLong(t *testing.T) {
clock.Set(time.Now())
past := clock.Now().Add(-1 * time.Hour)
- mock.Ctx.Configuration.Session.Inactivity = testInactivity
+ mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
// Reload the session provider since the configuration is indirect.
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
- assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
+ assert.Equal(t, time.Second*10, mock.Ctx.Configuration.Session.Cookies[0].Inactivity)
+
+ mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
@@ -719,8 +724,6 @@ func TestShouldDestroySessionWhenInactiveForTooLong(t *testing.T) {
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
- mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
-
VerifyGET(verifyGetCfg)(mock.Ctx)
// The session has been destroyed.
@@ -739,10 +742,10 @@ func TestShouldDestroySessionWhenInactiveForTooLongUsingDurationNotation(t *test
clock := utils.TestingClock{}
clock.Set(time.Now())
- mock.Ctx.Configuration.Session.Inactivity = time.Second * 10
+ mock.Ctx.Configuration.Session.Cookies[0].Inactivity = time.Second * 10
// Reload the session provider since the configuration is indirect.
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
- assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
+ assert.Equal(t, time.Second*10, mock.Ctx.Configuration.Session.Cookies[0].Inactivity)
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
@@ -768,7 +771,7 @@ func TestShouldKeepSessionWhenUserCheckedRememberMeAndIsInactiveForTooLong(t *te
mock.Clock.Set(time.Now())
- mock.Ctx.Configuration.Session.Inactivity = testInactivity
+ mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
@@ -800,7 +803,7 @@ func TestShouldKeepSessionWhenInactivityTimeoutHasNotBeenExceeded(t *testing.T)
mock.Clock.Set(time.Now())
- mock.Ctx.Configuration.Session.Inactivity = testInactivity
+ mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
past := mock.Clock.Now().Add(-1 * time.Hour)
@@ -836,10 +839,10 @@ func TestShouldRedirectWhenSessionInactiveForTooLongAndRDParamProvided(t *testin
clock := utils.TestingClock{}
clock.Set(time.Now())
- mock.Ctx.Configuration.Session.Inactivity = testInactivity
+ mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
// Reload the session provider since the configuration is indirect.
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
- assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
+ assert.Equal(t, time.Second*10, mock.Ctx.Configuration.Session.Cookies[0].Inactivity)
past := clock.Now().Add(-1 * time.Hour)
@@ -899,7 +902,7 @@ func TestShouldUpdateInactivityTimestampEvenWhenHittingForbiddenResources(t *tes
mock.Clock.Set(time.Now())
- mock.Ctx.Configuration.Session.Inactivity = testInactivity
+ mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
past := mock.Clock.Now().Add(-1 * time.Hour)
@@ -974,47 +977,6 @@ func TestShouldURLEncodeRedirectionHeader(t *testing.T) {
string(mock.Ctx.Response.Body()))
}
-func TestIsDomainProtected(t *testing.T) {
- GetURL := func(u string) *url.URL {
- x, err := url.ParseRequestURI(u)
- require.NoError(t, err)
-
- return x
- }
-
- assert.True(t, isURLUnderProtectedDomain(
- GetURL("http://mytest.example.com/abc/?query=abc"), "example.com"))
-
- assert.True(t, isURLUnderProtectedDomain(
- GetURL("http://example.com/abc/?query=abc"), "example.com"))
-
- assert.True(t, isURLUnderProtectedDomain(
- GetURL("https://mytest.example.com/abc/?query=abc"), "example.com"))
-
- // Cookies readable by a service on a machine is also readable by a service on the same machine
- // with a different port as mentioned in https://tools.ietf.org/html/rfc6265#section-8.5.
- assert.True(t, isURLUnderProtectedDomain(
- GetURL("https://mytest.example.com:8080/abc/?query=abc"), "example.com"))
-}
-
-func TestSchemeIsHTTPS(t *testing.T) {
- GetURL := func(u string) *url.URL {
- x, err := url.ParseRequestURI(u)
- require.NoError(t, err)
-
- return x
- }
-
- assert.False(t, isSchemeHTTPS(
- GetURL("http://mytest.example.com/abc/?query=abc")))
- assert.False(t, isSchemeHTTPS(
- GetURL("ws://mytest.example.com/abc/?query=abc")))
- assert.False(t, isSchemeHTTPS(
- GetURL("wss://mytest.example.com/abc/?query=abc")))
- assert.True(t, isSchemeHTTPS(
- GetURL("https://mytest.example.com/abc/?query=abc")))
-}
-
func TestSchemeIsWSS(t *testing.T) {
GetURL := func(u string) *url.URL {
x, err := url.ParseRequestURI(u)
@@ -1435,10 +1397,10 @@ func TestShouldNotRedirectRequestsForBypassACLWhenInactiveForTooLong(t *testing.
clock.Set(time.Now())
past := clock.Now().Add(-1 * time.Hour)
- mock.Ctx.Configuration.Session.Inactivity = testInactivity
+ mock.Ctx.Configuration.Session.Cookies[0].Inactivity = testInactivity
// Reload the session provider since the configuration is indirect.
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
- assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
+ assert.Equal(t, time.Second*10, mock.Ctx.Configuration.Session.Cookies[0].Inactivity)
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
@@ -1527,7 +1489,7 @@ func TestIsSessionInactiveTooLong(t *testing.T) {
defer ctx.Close()
- ctx.Ctx.Configuration.Session.Inactivity = tc.inactivity
+ ctx.Ctx.Configuration.Session.Cookies[0].Inactivity = tc.inactivity
ctx.Ctx.Providers.SessionProvider = session.NewProvider(ctx.Ctx.Configuration.Session, nil)
ctx.Clock.Set(tc.now)
diff --git a/internal/handlers/response.go b/internal/handlers/response.go
index 8669597f1..89e964630 100644
--- a/internal/handlers/response.go
+++ b/internal/handlers/response.go
@@ -13,7 +13,6 @@ import (
"github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/model"
"github.com/authelia/authelia/v4/internal/oidc"
- "github.com/authelia/authelia/v4/internal/utils"
)
// Handle1FAResponse handle the redirection upon 1FA authentication.
@@ -57,7 +56,7 @@ func Handle1FAResponse(ctx *middlewares.AutheliaCtx, targetURI, requestMethod st
return
}
- if !utils.IsURISafeRedirection(targetURL, ctx.Configuration.Session.Domain) {
+ if !ctx.IsSafeRedirectionTargetURI(targetURL) {
ctx.Logger.Debugf("Redirection URL %s is not safe", targetURI)
if !ctx.Providers.Authorizer.IsSecondFactorEnabled() && ctx.Configuration.DefaultRedirectionURL != "" {
@@ -98,14 +97,18 @@ func Handle2FAResponse(ctx *middlewares.AutheliaCtx, targetURI string) {
return
}
- var safe bool
-
- if safe, err = utils.IsURIStringSafeRedirection(targetURI, ctx.Configuration.Session.Domain); err != nil {
- ctx.Error(fmt.Errorf("unable to check target URL: %s", err), messageMFAValidationFailed)
+ var (
+ parsedURI *url.URL
+ safe bool
+ )
+ if parsedURI, err = url.ParseRequestURI(targetURI); err != nil {
+ ctx.Error(fmt.Errorf("unable to determine if URI '%s' is safe to redirect to: failed to parse URI '%s': %w", targetURI, targetURI, err), messageMFAValidationFailed)
return
}
+ safe = ctx.IsSafeRedirectionTargetURI(parsedURI)
+
if safe {
ctx.Logger.Debugf("Redirection URL %s is safe", targetURI)
diff --git a/internal/handlers/webauthn.go b/internal/handlers/webauthn.go
index bb7b872fd..81624e92e 100644
--- a/internal/handlers/webauthn.go
+++ b/internal/handlers/webauthn.go
@@ -3,6 +3,7 @@ package handlers
import (
"fmt"
"net/url"
+ "strings"
"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/webauthn"
@@ -44,7 +45,7 @@ func newWebauthn(ctx *middlewares.AutheliaCtx) (w *webauthn.WebAuthn, err error)
config := &webauthn.Config{
RPDisplayName: ctx.Configuration.Webauthn.DisplayName,
RPID: rpID,
- RPOrigin: origin,
+ RPOrigins: []string{origin},
RPIcon: "",
AttestationPreference: ctx.Configuration.Webauthn.ConveyancePreference,
@@ -57,7 +58,7 @@ func newWebauthn(ctx *middlewares.AutheliaCtx) (w *webauthn.WebAuthn, err error)
Timeout: int(ctx.Configuration.Webauthn.Timeout.Milliseconds()),
}
- ctx.Logger.Tracef("Creating new Webauthn RP instance with ID %s and Origin %s", config.RPID, config.RPOrigin)
+ ctx.Logger.Tracef("Creating new Webauthn RP instance with ID %s and Origins %s", config.RPID, strings.Join(config.RPOrigins, ", "))
return webauthn.New(config)
}
diff --git a/internal/handlers/webauthn_test.go b/internal/handlers/webauthn_test.go
index 70f68457c..7695adc3b 100644
--- a/internal/handlers/webauthn_test.go
+++ b/internal/handlers/webauthn_test.go
@@ -146,6 +146,7 @@ func TestWebauthnGetUserWithErr(t *testing.T) {
func TestWebauthnNewWebauthnShouldReturnErrWhenHeadersNotAvailable(t *testing.T) {
ctx := mocks.NewMockAutheliaCtx(t)
+ ctx.Ctx.Request.Header.Del("X-Forwarded-Host")
w, err := newWebauthn(ctx.Ctx)
diff --git a/internal/middlewares/authelia_context.go b/internal/middlewares/authelia_context.go
index 8dde8c49f..93e67aec5 100644
--- a/internal/middlewares/authelia_context.go
+++ b/internal/middlewares/authelia_context.go
@@ -238,9 +238,63 @@ func (ctx *AutheliaCtx) RootURLSlash() (issuerURL *url.URL) {
}
}
+// GetTargetURICookieDomain returns the session provider for the targetURI domain.
+func (ctx *AutheliaCtx) GetTargetURICookieDomain(targetURI *url.URL) string {
+ hostname := targetURI.Hostname()
+
+ for _, domain := range ctx.Configuration.Session.Cookies {
+ if utils.HasDomainSuffix(hostname, domain.Domain) {
+ return domain.Domain
+ }
+ }
+
+ return ""
+}
+
+// IsSafeRedirectionTargetURI returns true if the targetURI is within the scope of a cookie domain and secure.
+func (ctx *AutheliaCtx) IsSafeRedirectionTargetURI(targetURI *url.URL) bool {
+ if !utils.IsURISecure(targetURI) {
+ return false
+ }
+
+ return ctx.GetTargetURICookieDomain(targetURI) != ""
+}
+
+// GetCookieDomain returns the cookie domain for the current request.
+func (ctx *AutheliaCtx) GetCookieDomain() (domain string, err error) {
+ var targetURI *url.URL
+
+ if targetURI, err = ctx.GetOriginalURL(); err != nil {
+ return "", fmt.Errorf("unable to retrieve cookie domain: %s", err)
+ }
+
+ return ctx.GetTargetURICookieDomain(targetURI), nil
+}
+
+// GetSessionProvider returns the session provider for the Request's domain.
+func (ctx *AutheliaCtx) GetSessionProvider() (provider *session.Session, err error) {
+ var cookieDomain string
+
+ if cookieDomain, err = ctx.GetCookieDomain(); err != nil {
+ return nil, err
+ }
+
+ if cookieDomain == "" {
+ return nil, fmt.Errorf("unable to retrieve domain session: %s", err)
+ }
+
+ return ctx.Providers.SessionProvider.Get(cookieDomain)
+}
+
// GetSession return the user session. Any update will be saved in cache.
func (ctx *AutheliaCtx) GetSession() session.UserSession {
- userSession, err := ctx.Providers.SessionProvider.GetSession(ctx.RequestCtx)
+ provider, err := ctx.GetSessionProvider()
+ if err != nil {
+ ctx.Logger.Error("Unable to retrieve domain session")
+ return session.NewDefaultUserSession()
+ }
+
+ userSession, err := provider.GetSession(ctx.RequestCtx)
if err != nil {
ctx.Logger.Error("Unable to retrieve user session")
return session.NewDefaultUserSession()
@@ -251,7 +305,32 @@ func (ctx *AutheliaCtx) GetSession() session.UserSession {
// SaveSession save the content of the session.
func (ctx *AutheliaCtx) SaveSession(userSession session.UserSession) error {
- return ctx.Providers.SessionProvider.SaveSession(ctx.RequestCtx, userSession)
+ provider, err := ctx.GetSessionProvider()
+ if err != nil {
+ return fmt.Errorf("unable to save user session: %s", err)
+ }
+
+ return provider.SaveSession(ctx.RequestCtx, userSession)
+}
+
+// RegenerateSession regenerates user session.
+func (ctx *AutheliaCtx) RegenerateSession() error {
+ provider, err := ctx.GetSessionProvider()
+ if err != nil {
+ return fmt.Errorf("unable to regenerate user session: %s", err)
+ }
+
+ return provider.RegenerateSession(ctx.RequestCtx)
+}
+
+// DestroySession destroy user session.
+func (ctx *AutheliaCtx) DestroySession() error {
+ provider, err := ctx.GetSessionProvider()
+ if err != nil {
+ return fmt.Errorf("unable to destroy user session: %s", err)
+ }
+
+ return provider.DestroySession(ctx.RequestCtx)
}
// ReplyOK is a helper method to reply ok.
diff --git a/internal/middlewares/authelia_context_test.go b/internal/middlewares/authelia_context_test.go
index 57ac0f613..abf63b4dd 100644
--- a/internal/middlewares/authelia_context_test.go
+++ b/internal/middlewares/authelia_context_test.go
@@ -182,6 +182,8 @@ func TestShouldGetOriginalURLFromForwardedHeadersWithURI(t *testing.T) {
func TestShouldFallbackToNonXForwardedHeaders(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
+ mock.Ctx.Request.Header.Del("X-Forwarded-Host")
+
defer mock.Close()
mock.Ctx.RequestCtx.Request.SetRequestURI("/2fa/one-time-password")
@@ -196,6 +198,8 @@ func TestShouldOnlyFallbackToNonXForwardedHeadersWhenNil(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
+ mock.Ctx.Request.Header.Del("X-Forwarded-Host")
+
mock.Ctx.RequestCtx.Request.SetRequestURI("/2fa/one-time-password")
mock.Ctx.RequestCtx.Request.SetHost("localhost")
mock.Ctx.RequestCtx.Request.Header.Set(fasthttp.HeaderXForwardedHost, "auth.example.com:1234")
diff --git a/internal/middlewares/const.go b/internal/middlewares/const.go
index ee2c49955..dc5519f2e 100644
--- a/internal/middlewares/const.go
+++ b/internal/middlewares/const.go
@@ -47,14 +47,15 @@ var (
)
var (
- headerValueFalse = []byte("false")
- headerValueTrue = []byte("true")
- headerValueMaxAge = []byte("100")
- headerValueVary = []byte("Accept-Encoding, Origin")
- headerValueVaryWildcard = []byte("Accept-Encoding")
- headerValueOriginWildcard = []byte("*")
- headerValueZero = []byte("0")
- headerValueCSPNone = []byte("default-src 'none';")
+ headerValueFalse = []byte("false")
+ headerValueTrue = []byte("true")
+ headerValueMaxAge = []byte("100")
+ headerValueVary = []byte("Accept-Encoding, Origin")
+ headerValueVaryWildcard = []byte("Accept-Encoding")
+ headerValueOriginWildcard = []byte("*")
+ headerValueZero = []byte("0")
+ headerValueCSPNone = []byte("default-src 'none'")
+ headerValueCSPNoneFormPost = []byte("default-src 'none'; script-src 'sha256-skflBqA90WuHvoczvimLdj49ExKdizFjX2Itd6xKZdU='")
headerValueNoSniff = []byte("nosniff")
headerValueStrictOriginCrossOrigin = []byte("strict-origin-when-cross-origin")
@@ -83,6 +84,9 @@ var (
// UserValueKeyBaseURL is the User Value key where we store the Base URL.
UserValueKeyBaseURL = []byte("base_url")
+ // UserValueKeyFormPost is the User Value key where we indicate the form_post response mode.
+ UserValueKeyFormPost = []byte("form_post")
+
headerSeparator = []byte(", ")
contentTypeTextPlain = []byte("text/plain; charset=utf-8")
diff --git a/internal/middlewares/headers.go b/internal/middlewares/headers.go
index cd71440c2..176ad658e 100644
--- a/internal/middlewares/headers.go
+++ b/internal/middlewares/headers.go
@@ -26,6 +26,22 @@ func SecurityHeadersCSPNone(next fasthttp.RequestHandler) fasthttp.RequestHandle
}
}
+// SecurityHeadersCSPNoneOpenIDConnect middleware adds the Content-Security-Policy header with the value
+// "default-src 'none'" except in special circumstances.
+func SecurityHeadersCSPNoneOpenIDConnect(next fasthttp.RequestHandler) fasthttp.RequestHandler {
+ return func(ctx *fasthttp.RequestCtx) {
+ ctx.SetUserValueBytes(UserValueKeyFormPost, false)
+
+ next(ctx)
+
+ if modeFormPost, ok := ctx.UserValueBytes(UserValueKeyFormPost).(bool); ok && modeFormPost {
+ ctx.Response.Header.SetBytesKV(headerContentSecurityPolicy, headerValueCSPNoneFormPost)
+ } else {
+ ctx.Response.Header.SetBytesKV(headerContentSecurityPolicy, headerValueCSPNone)
+ }
+ }
+}
+
// SecurityHeadersNoStore middleware adds the Pragma no-cache and Cache-Control no-store headers.
func SecurityHeadersNoStore(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
diff --git a/internal/mocks/authelia_ctx.go b/internal/mocks/authelia_ctx.go
index b7fef421c..17f742648 100644
--- a/internal/mocks/authelia_ctx.go
+++ b/internal/mocks/authelia_ctx.go
@@ -51,9 +51,25 @@ func NewMockAutheliaCtx(t *testing.T) *MockAutheliaCtx {
mockAuthelia.Clock.Set(datetime)
config := schema.Configuration{}
- config.Session.RememberMeDuration = schema.DefaultSessionConfiguration.RememberMeDuration
- config.Session.Name = "authelia_session"
- config.Session.Domain = "example.com"
+ config.Session.Cookies = []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "authelia_session",
+ Domain: "example.com",
+ RememberMe: schema.DefaultSessionConfiguration.RememberMe,
+ Expiration: schema.DefaultSessionConfiguration.Expiration,
+ },
+ },
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: "authelia_session",
+ Domain: "example2.com",
+ RememberMe: schema.DefaultSessionConfiguration.RememberMe,
+ Expiration: schema.DefaultSessionConfiguration.Expiration,
+ },
+ },
+ }
+
config.AccessControl.DefaultPolicy = "deny"
config.AccessControl.Rules = []schema.ACLRule{{
Domains: []string{"bypass.example.com"},
@@ -114,6 +130,9 @@ func NewMockAutheliaCtx(t *testing.T) *MockAutheliaCtx {
// Set a cookie to identify this client throughout the test.
// request.Request.Header.SetCookie("authelia_session", "client_cookie").
+ // Set X-Forwarded-Host for compatibility with multi-root-domain implementation.
+ request.Request.Header.Set("X-Forwarded-Host", "example.com")
+
ctx := middlewares.NewAutheliaCtx(request, config, providers)
mockAuthelia.Ctx = ctx
diff --git a/internal/model/webauthn.go b/internal/model/webauthn.go
index f91185646..15eb22dec 100644
--- a/internal/model/webauthn.go
+++ b/internal/model/webauthn.go
@@ -213,7 +213,7 @@ func (d *WebauthnDevice) UpdateSignInInfo(config *webauthn.Config, now time.Time
switch d.AttestationType {
case attestationTypeFIDOU2F:
- d.RPID = config.RPOrigin
+ d.RPID = config.RPOrigins[0]
default:
d.RPID = config.RPID
}
diff --git a/internal/notification/smtp_notifier.go b/internal/notification/smtp_notifier.go
index c212b2c9c..a42fe62f4 100644
--- a/internal/notification/smtp_notifier.go
+++ b/internal/notification/smtp_notifier.go
@@ -139,8 +139,6 @@ func (n *SMTPNotifier) Send(ctx context.Context, recipient mail.Address, subject
var client *gomail.Client
- n.log.Debugf("creating client with %d options: %+v", len(n.opts), n.opts)
-
if client, err = gomail.NewClient(n.config.Host, n.opts...); err != nil {
return fmt.Errorf("notifier: smtp: failed to establish client: %w", err)
}
diff --git a/internal/ntp/const.go b/internal/ntp/const.go
index ed9c35216..8f60fc22f 100644
--- a/internal/ntp/const.go
+++ b/internal/ntp/const.go
@@ -1,15 +1,27 @@
package ntp
-const (
- ntpClientModeValue uint8 = 3 // 00000011.
- ntpLeapEnabledValue uint8 = 64 // 01000000.
- ntpVersion3Value uint8 = 24 // 00011000.
- ntpVersion4Value uint8 = 40 // 00101000.
-)
-
const ntpEpochOffset = 2208988800
const (
ntpV3 ntpVersion = iota
ntpV4
)
+
+const (
+ maskMode = 0xf8
+ maskVersion = 0xc7
+ maskLeap = 0x3f
+)
+
+const (
+ modeClient = 3
+)
+
+const (
+ version3 = 3
+ version4 = 4
+)
+
+const (
+ leapUnknown = 3
+)
diff --git a/internal/ntp/ntp.go b/internal/ntp/ntp.go
index 991eec9ce..fe8ba1964 100644
--- a/internal/ntp/ntp.go
+++ b/internal/ntp/ntp.go
@@ -40,7 +40,7 @@ func (p *Provider) StartupCheck() (err error) {
version = ntpV3
}
- req := &ntpPacket{LeapVersionMode: ntpLeapVersionClientMode(false, version)}
+ req := &ntpPacket{LeapVersionMode: ntpLeapVersionClientMode(version)}
if err := binary.Write(conn, binary.BigEndian, req); err != nil {
p.log.Warnf("Could not write to the NTP server socket to validate the system time is properly synchronized: %+v", err)
diff --git a/internal/ntp/util.go b/internal/ntp/util.go
index fd6b6a835..4e590c6d1 100644
--- a/internal/ntp/util.go
+++ b/internal/ntp/util.go
@@ -3,20 +3,18 @@ package ntp
import "time"
// ntpLeapVersionClientMode does the mathematics to configure the leap/version/mode value of an NTP client packet.
-func ntpLeapVersionClientMode(leap bool, version ntpVersion) (lvm uint8) {
- lvm = ntpClientModeValue
-
- if leap {
- lvm += ntpLeapEnabledValue
- }
+func ntpLeapVersionClientMode(version ntpVersion) (lvm uint8) {
+ lvm = (lvm & maskMode) | uint8(modeClient)
switch version {
case ntpV3:
- lvm += ntpVersion3Value
+ lvm = (lvm & maskVersion) | uint8(version3)<<3
case ntpV4:
- lvm += ntpVersion4Value
+ lvm = (lvm & maskVersion) | uint8(version4)<<3
}
+ lvm = (lvm & maskLeap) | uint8(leapUnknown)<<6
+
return lvm
}
diff --git a/internal/ntp/util_test.go b/internal/ntp/util_test.go
index caf07ca02..cff026679 100644
--- a/internal/ntp/util_test.go
+++ b/internal/ntp/util_test.go
@@ -29,13 +29,9 @@ func TestNtpPacketToTime(t *testing.T) {
}
func TestLeapVersionClientMode(t *testing.T) {
- v3Noleap := uint8(27)
- v4Noleap := uint8(43)
- v3leap := uint8(91)
- v4leap := uint8(107)
+ v3Noleap := uint8(0xdb)
+ v4Noleap := uint8(0xe3)
- assert.Equal(t, v3Noleap, ntpLeapVersionClientMode(false, ntpV3))
- assert.Equal(t, v4Noleap, ntpLeapVersionClientMode(false, ntpV4))
- assert.Equal(t, v3leap, ntpLeapVersionClientMode(true, ntpV3))
- assert.Equal(t, v4leap, ntpLeapVersionClientMode(true, ntpV4))
+ assert.Equal(t, v3Noleap, ntpLeapVersionClientMode(ntpV3))
+ assert.Equal(t, v4Noleap, ntpLeapVersionClientMode(ntpV4))
}
diff --git a/internal/oidc/config.go b/internal/oidc/config.go
index 0146899bf..7a5110b09 100644
--- a/internal/oidc/config.go
+++ b/internal/oidc/config.go
@@ -19,10 +19,11 @@ import (
"github.com/ory/fosite/token/jwt"
"github.com/authelia/authelia/v4/internal/configuration/schema"
+ "github.com/authelia/authelia/v4/internal/templates"
"github.com/authelia/authelia/v4/internal/utils"
)
-func NewConfig(config *schema.OpenIDConnectConfiguration) *Config {
+func NewConfig(config *schema.OpenIDConnectConfiguration, templates *templates.Provider) *Config {
c := &Config{
GlobalSecret: []byte(utils.HashSHA256FromString(config.HMACSecret)),
SendDebugMessagesToClients: config.EnableClientDebugMessages,
@@ -38,6 +39,7 @@ func NewConfig(config *schema.OpenIDConnectConfiguration) *Config {
EnforcePublicClients: config.EnforcePKCE != "never",
AllowPlainChallengeMethod: config.EnablePKCEPlainChallenge,
},
+ Templates: templates,
}
c.Strategy.Core = &HMACCoreStrategy{
@@ -85,6 +87,8 @@ type Config struct {
HTTPClient *retryablehttp.Client
FormPostHTMLTemplate *template.Template
MessageCatalog i18n.MessageCatalog
+
+ Templates *templates.Provider
}
type HashConfig struct {
@@ -502,7 +506,11 @@ func (c *Config) GetMessageCatalog(ctx context.Context) (catalog i18n.MessageCat
// GetFormPostHTMLTemplate returns the form post HTML template.
func (c *Config) GetFormPostHTMLTemplate(ctx context.Context) (tmpl *template.Template) {
- return c.FormPostHTMLTemplate
+ if c.Templates == nil {
+ return nil
+ }
+
+ return c.Templates.GetOpenIDConnectAuthorizeResponseFormPostTemplate()
}
// GetTokenURL returns the token URL.
diff --git a/internal/oidc/provider.go b/internal/oidc/provider.go
index 32b95a61a..fd2581661 100644
--- a/internal/oidc/provider.go
+++ b/internal/oidc/provider.go
@@ -9,10 +9,11 @@ import (
"github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/storage"
+ "github.com/authelia/authelia/v4/internal/templates"
)
// NewOpenIDConnectProvider new-ups a OpenIDConnectProvider.
-func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, store storage.Provider) (provider *OpenIDConnectProvider, err error) {
+func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, store storage.Provider, templates *templates.Provider) (provider *OpenIDConnectProvider, err error) {
if config == nil {
return nil, nil
}
@@ -20,7 +21,7 @@ func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, store s
provider = &OpenIDConnectProvider{
JSONWriter: herodot.NewJSONWriter(nil),
Store: NewStore(config, store),
- Config: NewConfig(config),
+ Config: NewConfig(config, templates),
}
provider.OAuth2Provider = fosite.NewOAuth2Provider(provider.Store, provider.Config)
diff --git a/internal/oidc/provider_test.go b/internal/oidc/provider_test.go
index e4a486eb5..85ab216ac 100644
--- a/internal/oidc/provider_test.go
+++ b/internal/oidc/provider_test.go
@@ -16,7 +16,7 @@ import (
var exampleIssuerPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAvcMVMB2vEbqI6PlSNJ4HmUyMxBDJ5iY7FS+zDDAHOZBg9S3S\nKcAn1CZcnyL0VvJ7wcdhR6oTnOwR94eKvzUyJZ+GL2hTMm27dubEYsNdhoCl6N3X\nyEEohNfoxiiCYraVauX8X3M9jFzbEz9+pacaDbHB2syaJ1qFmMNR+HSu2jPzOo7M\nlqKIOgUzA0741MaYNt47AEVg4XU5ORLdolbAkItmYg1QbyFndg9H5IvwKkYaXTGE\nlgDBcPUC0yVjAC15Mguquq+jZeQay+6PSbHTD8PQMOkLjyChI2xEhVNbdCXe676R\ncMW2R/gjrcK23zmtmTWRfdC1iZLSlHO+bJj9vQIDAQABAoIBAEZvkP/JJOCJwqPn\nV3IcbmmilmV4bdi1vByDFgyiDyx4wOSA24+PubjvfFW9XcCgRPuKjDtTj/AhWBHv\nB7stfa2lZuNV7/u562mZArA+IAr62Zp0LdIxDV8x3T8gbjVB3HhPYbv0RJZDKTYd\nzV6jhfIrVu9mHpoY6ZnodhapCPYIyk/d49KBIHZuAc25CUjMXgTeaVtf0c996036\nUxW6ef33wAOJAvW0RCvbXAJfmBeEq2qQlkjTIlpYx71fhZWexHifi8Ouv3Zonc+1\n/P2Adq5uzYVBT92f9RKHg9QxxNzVrLjSMaxyvUtWQCAQfW0tFIRdqBGsHYsQrFtI\nF4yzv8ECgYEA7ntpyN9HD9Z9lYQzPCR73sFCLM+ID99aVij0wHuxK97bkSyyvkLd\n7MyTaym3lg1UEqWNWBCLvFULZx7F0Ah6qCzD4ymm3Bj/ADpWWPgljBI0AFml+HHs\nhcATmXUrj5QbLyhiP2gmJjajp1o/rgATx6ED66seSynD6JOH8wUhhZUCgYEAy7OA\n06PF8GfseNsTqlDjNF0K7lOqd21S0prdwrsJLiVzUlfMM25MLE0XLDUutCnRheeh\nIlcuDoBsVTxz6rkvFGD74N+pgXlN4CicsBq5ofK060PbqCQhSII3fmHobrZ9Cr75\nHmBjAxHx998SKaAAGbBbcYGUAp521i1pH5CEPYkCgYEAkUd1Zf0+2RMdZhwm6hh/\nrW+l1I6IoMK70YkZsLipccRNld7Y9LbfYwYtODcts6di9AkOVfueZJiaXbONZfIE\nZrb+jkAteh9wGL9xIrnohbABJcV3Kiaco84jInUSmGDtPokncOENfHIEuEpuSJ2b\nbx1TuhmAVuGWivR0+ULC7RECgYEAgS0cDRpWc9Xzh9Cl7+PLsXEvdWNpPsL9OsEq\n0Ep7z9+/+f/jZtoTRCS/BTHUpDvAuwHglT5j3p5iFMt5VuiIiovWLwynGYwrbnNS\nqfrIrYKUaH1n1oDS+oBZYLQGCe9/7EifAjxtjYzbvSyg//SPG7tSwfBCREbpZXj2\nqSWkNsECgYA/mCDzCTlrrWPuiepo6kTmN+4TnFA+hJI6NccDVQ+jvbqEdoJ4SW4L\nzqfZSZRFJMNpSgIqkQNRPJqMP0jQ5KRtJrjMWBnYxktwKz9fDg2R2MxdFgMF2LH2\nHEMMhFHlv8NDjVOXh1KwRoltNGVWYsSrD9wKU9GhRCEfmNCGrvBcEg==\n-----END RSA PRIVATE KEY-----"
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_NotConfigured(t *testing.T) {
- provider, err := NewOpenIDConnectProvider(nil, nil)
+ provider, err := NewOpenIDConnectProvider(nil, nil, nil)
assert.NoError(t, err)
assert.Nil(t, provider)
@@ -39,7 +39,7 @@ func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing
},
},
},
- }, nil)
+ }, nil, nil)
assert.NoError(t, err)
@@ -88,7 +88,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *tes
},
},
},
- }, nil)
+ }, nil, nil)
assert.NotNil(t, provider)
assert.NoError(t, err)
@@ -109,7 +109,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
},
},
},
- }, nil)
+ }, nil, nil)
assert.NoError(t, err)
@@ -199,7 +199,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
},
},
},
- }, nil)
+ }, nil, nil)
assert.NoError(t, err)
@@ -278,7 +278,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
},
},
},
- }, nil)
+ }, nil, nil)
assert.NoError(t, err)
diff --git a/internal/server/handlers.go b/internal/server/handlers.go
index 032f54a7f..9992f40f5 100644
--- a/internal/server/handlers.go
+++ b/internal/server/handlers.go
@@ -236,27 +236,27 @@ func handleRouter(config schema.Configuration, providers middlewares.Providers)
}
if providers.OpenIDConnect != nil {
- middlewareOIDC := middlewares.NewBridgeBuilder(config, providers).WithPreMiddlewares(
- middlewares.SecurityHeaders, middlewares.SecurityHeadersCSPNone, middlewares.SecurityHeadersNoStore,
+ bridgeOIDC := middlewares.NewBridgeBuilder(config, providers).WithPreMiddlewares(
+ middlewares.SecurityHeaders, middlewares.SecurityHeadersCSPNoneOpenIDConnect, middlewares.SecurityHeadersNoStore,
).Build()
- r.GET("/api/oidc/consent", middlewareOIDC(handlers.OpenIDConnectConsentGET))
- r.POST("/api/oidc/consent", middlewareOIDC(handlers.OpenIDConnectConsentPOST))
+ r.GET("/api/oidc/consent", bridgeOIDC(handlers.OpenIDConnectConsentGET))
+ r.POST("/api/oidc/consent", bridgeOIDC(handlers.OpenIDConnectConsentPOST))
allowedOrigins := utils.StringSliceFromURLs(config.IdentityProviders.OIDC.CORS.AllowedOrigins)
r.OPTIONS(oidc.EndpointPathWellKnownOpenIDConfiguration, policyCORSPublicGET.HandleOPTIONS)
- r.GET(oidc.EndpointPathWellKnownOpenIDConfiguration, policyCORSPublicGET.Middleware(middlewareOIDC(handlers.OpenIDConnectConfigurationWellKnownGET)))
+ r.GET(oidc.EndpointPathWellKnownOpenIDConfiguration, policyCORSPublicGET.Middleware(bridgeOIDC(handlers.OpenIDConnectConfigurationWellKnownGET)))
r.OPTIONS(oidc.EndpointPathWellKnownOAuthAuthorizationServer, policyCORSPublicGET.HandleOPTIONS)
- r.GET(oidc.EndpointPathWellKnownOAuthAuthorizationServer, policyCORSPublicGET.Middleware(middlewareOIDC(handlers.OAuthAuthorizationServerWellKnownGET)))
+ r.GET(oidc.EndpointPathWellKnownOAuthAuthorizationServer, policyCORSPublicGET.Middleware(bridgeOIDC(handlers.OAuthAuthorizationServerWellKnownGET)))
r.OPTIONS(oidc.EndpointPathJWKs, policyCORSPublicGET.HandleOPTIONS)
r.GET(oidc.EndpointPathJWKs, policyCORSPublicGET.Middleware(middlewareAPI(handlers.JSONWebKeySetGET)))
// TODO (james-d-elliott): Remove in GA. This is a legacy implementation of the above endpoint.
r.OPTIONS("/api/oidc/jwks", policyCORSPublicGET.HandleOPTIONS)
- r.GET("/api/oidc/jwks", policyCORSPublicGET.Middleware(middlewareOIDC(handlers.JSONWebKeySetGET)))
+ r.GET("/api/oidc/jwks", policyCORSPublicGET.Middleware(bridgeOIDC(handlers.JSONWebKeySetGET)))
policyCORSAuthorization := middlewares.NewCORSPolicyBuilder().
WithAllowedMethods(fasthttp.MethodOptions, fasthttp.MethodGet, fasthttp.MethodPost).
@@ -265,13 +265,13 @@ func handleRouter(config schema.Configuration, providers middlewares.Providers)
Build()
r.OPTIONS(oidc.EndpointPathAuthorization, policyCORSAuthorization.HandleOnlyOPTIONS)
- r.GET(oidc.EndpointPathAuthorization, policyCORSAuthorization.Middleware(middlewareOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
- r.POST(oidc.EndpointPathAuthorization, policyCORSAuthorization.Middleware(middlewareOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
+ r.GET(oidc.EndpointPathAuthorization, policyCORSAuthorization.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
+ r.POST(oidc.EndpointPathAuthorization, policyCORSAuthorization.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
// TODO (james-d-elliott): Remove in GA. This is a legacy endpoint.
r.OPTIONS("/api/oidc/authorize", policyCORSAuthorization.HandleOnlyOPTIONS)
- r.GET("/api/oidc/authorize", policyCORSAuthorization.Middleware(middlewareOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
- r.POST("/api/oidc/authorize", policyCORSAuthorization.Middleware(middlewareOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
+ r.GET("/api/oidc/authorize", policyCORSAuthorization.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
+ r.POST("/api/oidc/authorize", policyCORSAuthorization.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
policyCORSToken := middlewares.NewCORSPolicyBuilder().
WithAllowCredentials(true).
@@ -281,7 +281,7 @@ func handleRouter(config schema.Configuration, providers middlewares.Providers)
Build()
r.OPTIONS(oidc.EndpointPathToken, policyCORSToken.HandleOPTIONS)
- r.POST(oidc.EndpointPathToken, policyCORSToken.Middleware(middlewareOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectTokenPOST))))
+ r.POST(oidc.EndpointPathToken, policyCORSToken.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectTokenPOST))))
policyCORSUserinfo := middlewares.NewCORSPolicyBuilder().
WithAllowCredentials(true).
@@ -291,8 +291,8 @@ func handleRouter(config schema.Configuration, providers middlewares.Providers)
Build()
r.OPTIONS(oidc.EndpointPathUserinfo, policyCORSUserinfo.HandleOPTIONS)
- r.GET(oidc.EndpointPathUserinfo, policyCORSUserinfo.Middleware(middlewareOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectUserinfo))))
- r.POST(oidc.EndpointPathUserinfo, policyCORSUserinfo.Middleware(middlewareOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectUserinfo))))
+ r.GET(oidc.EndpointPathUserinfo, policyCORSUserinfo.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectUserinfo))))
+ r.POST(oidc.EndpointPathUserinfo, policyCORSUserinfo.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectUserinfo))))
policyCORSIntrospection := middlewares.NewCORSPolicyBuilder().
WithAllowCredentials(true).
@@ -302,11 +302,11 @@ func handleRouter(config schema.Configuration, providers middlewares.Providers)
Build()
r.OPTIONS(oidc.EndpointPathIntrospection, policyCORSIntrospection.HandleOPTIONS)
- r.POST(oidc.EndpointPathIntrospection, policyCORSIntrospection.Middleware(middlewareOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthIntrospectionPOST))))
+ r.POST(oidc.EndpointPathIntrospection, policyCORSIntrospection.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthIntrospectionPOST))))
// TODO (james-d-elliott): Remove in GA. This is a legacy implementation of the above endpoint.
r.OPTIONS("/api/oidc/introspect", policyCORSIntrospection.HandleOPTIONS)
- r.POST("/api/oidc/introspect", policyCORSIntrospection.Middleware(middlewareOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthIntrospectionPOST))))
+ r.POST("/api/oidc/introspect", policyCORSIntrospection.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthIntrospectionPOST))))
policyCORSRevocation := middlewares.NewCORSPolicyBuilder().
WithAllowCredentials(true).
@@ -316,11 +316,11 @@ func handleRouter(config schema.Configuration, providers middlewares.Providers)
Build()
r.OPTIONS(oidc.EndpointPathRevocation, policyCORSRevocation.HandleOPTIONS)
- r.POST(oidc.EndpointPathRevocation, policyCORSRevocation.Middleware(middlewareOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthRevocationPOST))))
+ r.POST(oidc.EndpointPathRevocation, policyCORSRevocation.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthRevocationPOST))))
// TODO (james-d-elliott): Remove in GA. This is a legacy implementation of the above endpoint.
r.OPTIONS("/api/oidc/revoke", policyCORSRevocation.HandleOPTIONS)
- r.POST("/api/oidc/revoke", policyCORSRevocation.Middleware(middlewareOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthRevocationPOST))))
+ r.POST("/api/oidc/revoke", policyCORSRevocation.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OAuthRevocationPOST))))
}
r.HandleMethodNotAllowed = true
diff --git a/internal/server/public_html/index.html b/internal/server/public_html/index.html
index e69de29bb..5e384f70f 100644
--- a/internal/server/public_html/index.html
+++ b/internal/server/public_html/index.html
@@ -0,0 +1,9 @@
+{
+ "Base":"{{ .Base }}",
+ "DuoSelfEnrollment":"{{ .DuoSelfEnrollment }}",
+ "LogoOverride":"{{ .LogoOverride }}",
+ "RememberMe":"{{ .RememberMe }}",
+ "ResetPassword":"{{ .ResetPassword }}",
+ "ResetPasswordCustomURL":"{{ .ResetPasswordCustomURL }}",
+ "Theme":"{{ .Theme }}"
+}
diff --git a/internal/server/template.go b/internal/server/template.go
index 2aa24560d..aa24f4b99 100644
--- a/internal/server/template.go
+++ b/internal/server/template.go
@@ -16,6 +16,7 @@ import (
"github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/random"
+ "github.com/authelia/authelia/v4/internal/session"
"github.com/authelia/authelia/v4/internal/templates"
)
@@ -57,7 +58,16 @@ func ServeTemplatedFile(t templates.Template, opts *TemplatedFileOptions) middle
ctx.Response.Header.Add(fasthttp.HeaderContentSecurityPolicy, fmt.Sprintf(tmplCSPDefault, nonce))
}
- if err = t.Execute(ctx.Response.BodyWriter(), opts.CommonData(ctx.BasePath(), ctx.RootURLSlash().String(), nonce, logoOverride)); err != nil {
+ var (
+ rememberMe string
+ provider *session.Session
+ )
+
+ if provider, err = ctx.GetSessionProvider(); err == nil {
+ rememberMe = strconv.FormatBool(!provider.Config.DisableRememberMe)
+ }
+
+ if err = t.Execute(ctx.Response.BodyWriter(), opts.CommonData(ctx.BasePath(), ctx.RootURLSlash().String(), nonce, logoOverride, rememberMe)); err != nil {
ctx.RequestCtx.Error("an error occurred", 503)
ctx.Logger.WithError(err).Errorf("Error occcurred rendering template")
@@ -190,7 +200,7 @@ func NewTemplatedFileOptions(config *schema.Configuration) (opts *TemplatedFileO
opts = &TemplatedFileOptions{
AssetPath: config.Server.AssetPath,
DuoSelfEnrollment: strFalse,
- RememberMe: strconv.FormatBool(config.Session.RememberMeDuration != schema.RememberMeDisabled),
+ RememberMe: strconv.FormatBool(!config.Session.DisableRememberMe),
ResetPassword: strconv.FormatBool(!config.AuthenticationBackend.PasswordReset.Disable),
ResetPasswordCustomURL: config.AuthenticationBackend.PasswordReset.CustomURL.String(),
Theme: config.Theme,
@@ -227,7 +237,11 @@ type TemplatedFileOptions struct {
}
// CommonData returns a TemplatedFileCommonData with the dynamic options.
-func (options *TemplatedFileOptions) CommonData(base, baseURL, nonce, logoOverride string) TemplatedFileCommonData {
+func (options *TemplatedFileOptions) CommonData(base, baseURL, nonce, logoOverride, rememberMe string) TemplatedFileCommonData {
+ if rememberMe != "" {
+ return options.commonDataWithRememberMe(base, baseURL, nonce, logoOverride, rememberMe)
+ }
+
return TemplatedFileCommonData{
Base: base,
BaseURL: baseURL,
@@ -242,6 +256,22 @@ func (options *TemplatedFileOptions) CommonData(base, baseURL, nonce, logoOverri
}
}
+// CommonDataWithRememberMe returns a TemplatedFileCommonData with the dynamic options.
+func (options *TemplatedFileOptions) commonDataWithRememberMe(base, baseURL, nonce, logoOverride, rememberMe string) TemplatedFileCommonData {
+ return TemplatedFileCommonData{
+ Base: base,
+ BaseURL: baseURL,
+ CSPNonce: nonce,
+ LogoOverride: logoOverride,
+ DuoSelfEnrollment: options.DuoSelfEnrollment,
+ RememberMe: rememberMe,
+ ResetPassword: options.ResetPassword,
+ ResetPasswordCustomURL: options.ResetPasswordCustomURL,
+ Session: options.Session,
+ Theme: options.Theme,
+ }
+}
+
// OpenAPIData returns a TemplatedFileOpenAPIData with the dynamic options.
func (options *TemplatedFileOptions) OpenAPIData(base, baseURL, nonce string) TemplatedFileOpenAPIData {
return TemplatedFileOpenAPIData{
diff --git a/internal/session/encrypting_serializer.go b/internal/session/encrypting_serializer.go
index dabffdb9c..1fa6ccdb2 100644
--- a/internal/session/encrypting_serializer.go
+++ b/internal/session/encrypting_serializer.go
@@ -9,6 +9,12 @@ import (
"github.com/authelia/authelia/v4/internal/utils"
)
+// Serializer is a function that can serialize session information.
+type Serializer interface {
+ Encode(src session.Dict) (data []byte, err error)
+ Decode(dst *session.Dict, src []byte) (err error)
+}
+
// EncryptingSerializer a serializer encrypting the data with AES-GCM with 256-bit keys.
type EncryptingSerializer struct {
key [32]byte
@@ -21,8 +27,8 @@ func NewEncryptingSerializer(secret string) *EncryptingSerializer {
}
// Encode encode and encrypt session.
-func (e *EncryptingSerializer) Encode(src session.Dict) ([]byte, error) {
- if len(src.D) == 0 {
+func (e *EncryptingSerializer) Encode(src session.Dict) (data []byte, err error) {
+ if len(src.KV) == 0 {
return nil, nil
}
@@ -31,28 +37,30 @@ func (e *EncryptingSerializer) Encode(src session.Dict) ([]byte, error) {
return nil, fmt.Errorf("unable to marshal session: %v", err)
}
- encryptedDst, err := utils.Encrypt(dst, &e.key)
- if err != nil {
+ if data, err = utils.Encrypt(dst, &e.key); err != nil {
return nil, fmt.Errorf("unable to encrypt session: %v", err)
}
- return encryptedDst, nil
+ return data, nil
}
// Decode decrypt and decode session.
-func (e *EncryptingSerializer) Decode(dst *session.Dict, src []byte) error {
+func (e *EncryptingSerializer) Decode(dst *session.Dict, src []byte) (err error) {
if len(src) == 0 {
return nil
}
- dst.Reset()
+ for k := range dst.KV {
+ delete(dst.KV, k)
+ }
- decryptedSrc, err := utils.Decrypt(src, &e.key)
- if err != nil {
+ var data []byte
+
+ if data, err = utils.Decrypt(src, &e.key); err != nil {
return fmt.Errorf("unable to decrypt session: %s", err)
}
- _, err = dst.UnmarshalMsg(decryptedSrc)
+ _, err = dst.UnmarshalMsg(data)
return err
}
diff --git a/internal/session/encrypting_serializer_test.go b/internal/session/encrypting_serializer_test.go
index 288c990ea..fac909889 100644
--- a/internal/session/encrypting_serializer_test.go
+++ b/internal/session/encrypting_serializer_test.go
@@ -9,8 +9,7 @@ import (
)
func TestShouldEncryptAndDecrypt(t *testing.T) {
- payload := session.Dict{}
- payload.Set("key", "value")
+ payload := session.Dict{KV: map[string]interface{}{"key": "value"}}
dst, err := payload.MarshalMsg(nil)
require.NoError(t, err)
@@ -25,12 +24,11 @@ func TestShouldEncryptAndDecrypt(t *testing.T) {
err = serializer.Decode(&decodedPayload, encryptedDst)
require.NoError(t, err)
- assert.Equal(t, "value", decodedPayload.Get("key"))
+ assert.Equal(t, "value", decodedPayload.KV["key"])
}
func TestShouldNotSupportUnencryptedSessionForBackwardCompatibility(t *testing.T) {
- payload := session.Dict{}
- payload.Set("key", "value")
+ payload := session.Dict{KV: map[string]interface{}{"key": "value"}}
dst, err := payload.MarshalMsg(nil)
require.NoError(t, err)
diff --git a/internal/session/mocks/mock_storer.go b/internal/session/mocks/mock_storer.go
deleted file mode 100644
index b710447de..000000000
--- a/internal/session/mocks/mock_storer.go
+++ /dev/null
@@ -1,208 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/fasthttp/session/v2 (interfaces: Storer)
-
-// Package mock_session is a generated GoMock package.
-package mock_session
-
-import (
- "reflect"
- "time"
-
- "github.com/fasthttp/session/v2"
- "github.com/golang/mock/gomock"
-)
-
-// MockStorer is a mock of Storer interface
-type MockStorer struct {
- ctrl *gomock.Controller
- recorder *MockStorerMockRecorder
-}
-
-// MockStorerMockRecorder is the mock recorder for MockStorer
-type MockStorerMockRecorder struct {
- mock *MockStorer
-}
-
-// NewMockStorer creates a new mock instance
-func NewMockStorer(ctrl *gomock.Controller) *MockStorer {
- mock := &MockStorer{ctrl: ctrl}
- mock.recorder = &MockStorerMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (m *MockStorer) EXPECT() *MockStorerMockRecorder {
- return m.recorder
-}
-
-// Delete mocks base method
-func (m *MockStorer) Delete(arg0 string) {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Delete", arg0)
-}
-
-// Delete indicates an expected call of Delete
-func (mr *MockStorerMockRecorder) Delete(arg0 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockStorer)(nil).Delete), arg0)
-}
-
-// DeleteBytes mocks base method
-func (m *MockStorer) DeleteBytes(arg0 []byte) {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "DeleteBytes", arg0)
-}
-
-// DeleteBytes indicates an expected call of DeleteBytes
-func (mr *MockStorerMockRecorder) DeleteBytes(arg0 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBytes", reflect.TypeOf((*MockStorer)(nil).DeleteBytes), arg0)
-}
-
-// Flush mocks base method
-func (m *MockStorer) Flush() {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Flush")
-}
-
-// Flush indicates an expected call of Flush
-func (mr *MockStorerMockRecorder) Flush() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Flush", reflect.TypeOf((*MockStorer)(nil).Flush))
-}
-
-// Get mocks base method
-func (m *MockStorer) Get(arg0 string) interface{} {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", arg0)
- ret0, _ := ret[0].(interface{})
- return ret0
-}
-
-// Get indicates an expected call of Get
-func (mr *MockStorerMockRecorder) Get(arg0 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockStorer)(nil).Get), arg0)
-}
-
-// GetAll mocks base method
-func (m *MockStorer) GetAll() session.Dict {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetAll")
- ret0, _ := ret[0].(session.Dict)
- return ret0
-}
-
-// GetAll indicates an expected call of GetAll
-func (mr *MockStorerMockRecorder) GetAll() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAll", reflect.TypeOf((*MockStorer)(nil).GetAll))
-}
-
-// GetBytes mocks base method
-func (m *MockStorer) GetBytes(arg0 []byte) interface{} {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetBytes", arg0)
- ret0, _ := ret[0].(interface{})
- return ret0
-}
-
-// GetBytes indicates an expected call of GetBytes
-func (mr *MockStorerMockRecorder) GetBytes(arg0 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBytes", reflect.TypeOf((*MockStorer)(nil).GetBytes), arg0)
-}
-
-// GetExpiration mocks base method
-func (m *MockStorer) GetExpiration() time.Duration {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetExpiration")
- ret0, _ := ret[0].(time.Duration)
- return ret0
-}
-
-// GetExpiration indicates an expected call of GetExpiration
-func (mr *MockStorerMockRecorder) GetExpiration() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExpiration", reflect.TypeOf((*MockStorer)(nil).GetExpiration))
-}
-
-// GetSessionID mocks base method
-func (m *MockStorer) GetSessionID() []byte {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetSessionID")
- ret0, _ := ret[0].([]byte)
- return ret0
-}
-
-// GetSessionID indicates an expected call of GetSessionID
-func (mr *MockStorerMockRecorder) GetSessionID() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSessionID", reflect.TypeOf((*MockStorer)(nil).GetSessionID))
-}
-
-// HasExpirationChanged mocks base method
-func (m *MockStorer) HasExpirationChanged() bool {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "HasExpirationChanged")
- ret0, _ := ret[0].(bool)
- return ret0
-}
-
-// HasExpirationChanged indicates an expected call of HasExpirationChanged
-func (mr *MockStorerMockRecorder) HasExpirationChanged() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasExpirationChanged", reflect.TypeOf((*MockStorer)(nil).HasExpirationChanged))
-}
-
-// Save mocks base method
-func (m *MockStorer) Save() error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Save")
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Save indicates an expected call of Save
-func (mr *MockStorerMockRecorder) Save() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Save", reflect.TypeOf((*MockStorer)(nil).Save))
-}
-
-// Set mocks base method
-func (m *MockStorer) Set(arg0 string, arg1 interface{}) {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Set", arg0, arg1)
-}
-
-// Set indicates an expected call of Set
-func (mr *MockStorerMockRecorder) Set(arg0, arg1 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockStorer)(nil).Set), arg0, arg1)
-}
-
-// SetBytes mocks base method
-func (m *MockStorer) SetBytes(arg0 []byte, arg1 interface{}) {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "SetBytes", arg0, arg1)
-}
-
-// SetBytes indicates an expected call of SetBytes
-func (mr *MockStorerMockRecorder) SetBytes(arg0, arg1 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBytes", reflect.TypeOf((*MockStorer)(nil).SetBytes), arg0, arg1)
-}
-
-// SetExpiration mocks base method
-func (m *MockStorer) SetExpiration(arg0 time.Duration) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SetExpiration", arg0)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// SetExpiration indicates an expected call of SetExpiration
-func (mr *MockStorerMockRecorder) SetExpiration(arg0 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetExpiration", reflect.TypeOf((*MockStorer)(nil).SetExpiration), arg0)
-}
diff --git a/internal/session/provider.go b/internal/session/provider.go
index 88faef30d..4d6e5091d 100644
--- a/internal/session/provider.go
+++ b/internal/session/provider.go
@@ -2,158 +2,61 @@ package session
import (
"crypto/x509"
- "encoding/json"
- "time"
+ "fmt"
- fasthttpsession "github.com/fasthttp/session/v2"
- "github.com/fasthttp/session/v2/providers/memory"
- "github.com/fasthttp/session/v2/providers/redis"
- "github.com/valyala/fasthttp"
+ "github.com/fasthttp/session/v2"
"github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/logging"
)
-// Provider a session provider.
+// Provider contains a list of domain sessions.
type Provider struct {
- sessionHolder *fasthttpsession.Session
- RememberMe time.Duration
- Inactivity time.Duration
+ sessions map[string]*Session
}
// NewProvider instantiate a session provider given a configuration.
func NewProvider(config schema.SessionConfiguration, certPool *x509.CertPool) *Provider {
- c := NewProviderConfig(config, certPool)
+ log := logging.Logger()
- provider := new(Provider)
- provider.sessionHolder = fasthttpsession.New(c.config)
-
- logger := logging.Logger()
-
- provider.Inactivity, provider.RememberMe = config.Inactivity, config.RememberMeDuration
-
- var (
- providerImpl fasthttpsession.Provider
- err error
- )
-
- switch {
- case c.redisConfig != nil:
- providerImpl, err = redis.New(*c.redisConfig)
- if err != nil {
- logger.Fatal(err)
- }
- case c.redisSentinelConfig != nil:
- providerImpl, err = redis.NewFailoverCluster(*c.redisSentinelConfig)
- if err != nil {
- logger.Fatal(err)
- }
- default:
- providerImpl, err = memory.New(memory.Config{})
- if err != nil {
- logger.Fatal(err)
- }
+ name, p, s, err := NewSessionProvider(config, certPool)
+ if err != nil {
+ log.Fatal(err)
}
- err = provider.sessionHolder.SetProvider(providerImpl)
- if err != nil {
- logger.Fatal(err)
+ provider := &Provider{
+ sessions: map[string]*Session{},
+ }
+
+ var (
+ holder *session.Session
+ )
+
+ for _, dconfig := range config.Cookies {
+ if _, holder, err = NewProviderConfigAndSession(dconfig, name, s, p); err != nil {
+ log.Fatal(err)
+ }
+
+ provider.sessions[dconfig.Domain] = &Session{
+ Config: dconfig,
+ sessionHolder: holder,
+ }
}
return provider
}
-// GetSession return the user session from a request.
-func (p *Provider) GetSession(ctx *fasthttp.RequestCtx) (UserSession, error) {
- store, err := p.sessionHolder.Get(ctx)
-
- if err != nil {
- return NewDefaultUserSession(), err
+// Get returns session information for specified domain.
+func (p *Provider) Get(domain string) (*Session, error) {
+ if domain == "" {
+ return nil, fmt.Errorf("can not get session from an undefined domain")
}
- userSessionJSON, ok := store.Get(userSessionStorerKey).([]byte)
+ s, found := p.sessions[domain]
- // If userSession is not yet defined we create the new session with default values
- // and save it in the store.
- if !ok {
- userSession := NewDefaultUserSession()
-
- store.Set(userSessionStorerKey, userSession)
-
- return userSession, nil
+ if !found {
+ return nil, fmt.Errorf("no session found for domain '%s'", domain)
}
- var userSession UserSession
- err = json.Unmarshal(userSessionJSON, &userSession)
-
- if err != nil {
- return NewDefaultUserSession(), err
- }
-
- return userSession, nil
-}
-
-// SaveSession save the user session.
-func (p *Provider) SaveSession(ctx *fasthttp.RequestCtx, userSession UserSession) error {
- store, err := p.sessionHolder.Get(ctx)
-
- if err != nil {
- return err
- }
-
- userSessionJSON, err := json.Marshal(userSession)
-
- if err != nil {
- return err
- }
-
- store.Set(userSessionStorerKey, userSessionJSON)
-
- err = p.sessionHolder.Save(ctx, store)
-
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// RegenerateSession regenerate a session ID.
-func (p *Provider) RegenerateSession(ctx *fasthttp.RequestCtx) error {
- err := p.sessionHolder.Regenerate(ctx)
-
- return err
-}
-
-// DestroySession destroy a session ID and delete the cookie.
-func (p *Provider) DestroySession(ctx *fasthttp.RequestCtx) error {
- return p.sessionHolder.Destroy(ctx)
-}
-
-// UpdateExpiration update the expiration of the cookie and session.
-func (p *Provider) UpdateExpiration(ctx *fasthttp.RequestCtx, expiration time.Duration) error {
- store, err := p.sessionHolder.Get(ctx)
-
- if err != nil {
- return err
- }
-
- err = store.SetExpiration(expiration)
-
- if err != nil {
- return err
- }
-
- return p.sessionHolder.Save(ctx, store)
-}
-
-// GetExpiration get the expiration of the current session.
-func (p *Provider) GetExpiration(ctx *fasthttp.RequestCtx) (time.Duration, error) {
- store, err := p.sessionHolder.Get(ctx)
-
- if err != nil {
- return time.Duration(0), err
- }
-
- return store.GetExpiration(), nil
+ return s, nil
}
diff --git a/internal/session/provider_config.go b/internal/session/provider_config.go
index c9e694a28..0336a703e 100644
--- a/internal/session/provider_config.go
+++ b/internal/session/provider_config.go
@@ -8,6 +8,7 @@ import (
"strings"
"github.com/fasthttp/session/v2"
+ "github.com/fasthttp/session/v2/providers/memory"
"github.com/fasthttp/session/v2/providers/redis"
"github.com/sirupsen/logrus"
"github.com/valyala/fasthttp"
@@ -18,7 +19,7 @@ import (
)
// NewProviderConfig creates a configuration for creating the session provider.
-func NewProviderConfig(config schema.SessionConfiguration, certPool *x509.CertPool) ProviderConfig {
+func NewProviderConfig(config schema.SessionCookieConfiguration, providerName string, serializer Serializer) ProviderConfig {
c := session.NewDefaultConfig()
c.SessionIDGeneratorFunc = func() []byte {
@@ -61,16 +62,42 @@ func NewProviderConfig(config schema.SessionConfiguration, certPool *x509.CertPo
return true
}
- var redisConfig *redis.Config
+ if serializer != nil {
+ c.EncodeFunc = serializer.Encode
+ c.DecodeFunc = serializer.Decode
+ }
- var redisSentinelConfig *redis.FailoverConfig
+ return ProviderConfig{
+ c,
+ providerName,
+ }
+}
- var providerName string
+func NewProviderSession(pconfig ProviderConfig, provider session.Provider) (p *session.Session, err error) {
+ p = session.New(pconfig.config)
+ if err = p.SetProvider(provider); err != nil {
+ return nil, err
+ }
+
+ return p, nil
+}
+
+func NewProviderConfigAndSession(config schema.SessionCookieConfiguration, providerName string, serializer Serializer, provider session.Provider) (c ProviderConfig, p *session.Session, err error) {
+ c = NewProviderConfig(config, providerName, serializer)
+
+ if p, err = NewProviderSession(c, provider); err != nil {
+ return c, nil, err
+ }
+
+ return c, p, nil
+}
+
+func NewSessionProvider(config schema.SessionConfiguration, certPool *x509.CertPool) (name string, provider session.Provider, serializer Serializer, err error) {
// If redis configuration is provided, then use the redis provider.
switch {
case config.Redis != nil:
- serializer := NewEncryptingSerializer(config.Secret)
+ serializer = NewEncryptingSerializer(config.Secret)
var tlsConfig *tls.Config
@@ -92,8 +119,9 @@ func NewProviderConfig(config schema.SessionConfiguration, certPool *x509.CertPo
}
}
- providerName = "redis-sentinel"
- redisSentinelConfig = &redis.FailoverConfig{
+ name = "redis-sentinel"
+
+ provider, err = redis.NewFailoverCluster(redis.FailoverConfig{
Logger: logging.LoggerCtxPrintf(logrus.TraceLevel),
MasterName: config.Redis.HighAvailability.SentinelName,
SentinelAddrs: addrs,
@@ -109,9 +137,9 @@ func NewProviderConfig(config schema.SessionConfiguration, certPool *x509.CertPo
IdleTimeout: 300,
TLSConfig: tlsConfig,
KeyPrefix: "authelia-session",
- }
+ })
} else {
- providerName = "redis"
+ name = "redis"
network := "tcp"
var addr string
@@ -123,7 +151,7 @@ func NewProviderConfig(config schema.SessionConfiguration, certPool *x509.CertPo
addr = fmt.Sprintf("%s:%d", config.Redis.Host, config.Redis.Port)
}
- redisConfig = &redis.Config{
+ provider, err = redis.New(redis.Config{
Logger: logging.LoggerCtxPrintf(logrus.TraceLevel),
Network: network,
Addr: addr,
@@ -135,19 +163,12 @@ func NewProviderConfig(config schema.SessionConfiguration, certPool *x509.CertPo
IdleTimeout: 300,
TLSConfig: tlsConfig,
KeyPrefix: "authelia-session",
- }
+ })
}
-
- c.EncodeFunc = serializer.Encode
- c.DecodeFunc = serializer.Decode
default:
- providerName = "memory"
+ name = "memory"
+ provider, err = memory.New(memory.Config{})
}
- return ProviderConfig{
- c,
- redisConfig,
- redisSentinelConfig,
- providerName,
- }
+ return name, provider, serializer, err
}
diff --git a/internal/session/provider_config_test.go b/internal/session/provider_config_test.go
deleted file mode 100644
index 23b131c71..000000000
--- a/internal/session/provider_config_test.go
+++ /dev/null
@@ -1,288 +0,0 @@
-package session
-
-import (
- "crypto/sha256"
- "crypto/tls"
- "testing"
- "time"
-
- "github.com/fasthttp/session/v2"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "github.com/valyala/fasthttp"
-
- "github.com/authelia/authelia/v4/internal/configuration/schema"
- "github.com/authelia/authelia/v4/internal/utils"
-)
-
-func TestShouldCreateInMemorySessionProvider(t *testing.T) {
- // The redis configuration is not provided so we create a in-memory provider.
- configuration := schema.SessionConfiguration{}
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
- providerConfig := NewProviderConfig(configuration, nil)
-
- assert.Equal(t, "my_session", providerConfig.config.CookieName)
- assert.Equal(t, testDomain, providerConfig.config.Domain)
- assert.Equal(t, true, providerConfig.config.Secure)
- assert.Equal(t, time.Duration(40)*time.Second, providerConfig.config.Expiration)
- assert.True(t, providerConfig.config.IsSecureFunc(nil))
- assert.Equal(t, "memory", providerConfig.providerName)
-}
-
-func TestShouldCreateRedisSessionProviderTLS(t *testing.T) {
- configuration := schema.SessionConfiguration{}
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
- configuration.Redis = &schema.RedisSessionConfiguration{
- Host: "redis.example.com",
- Port: 6379,
- Password: "pass",
- TLS: &schema.TLSConfig{
- ServerName: "redis.fqdn.example.com",
- MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13},
- },
- }
- providerConfig := NewProviderConfig(configuration, nil)
-
- assert.Nil(t, providerConfig.redisSentinelConfig)
- assert.Equal(t, "my_session", providerConfig.config.CookieName)
- assert.Equal(t, testDomain, providerConfig.config.Domain)
- assert.Equal(t, true, providerConfig.config.Secure)
- assert.Equal(t, time.Duration(40)*time.Second, providerConfig.config.Expiration)
- assert.True(t, providerConfig.config.IsSecureFunc(nil))
-
- assert.Equal(t, "redis", providerConfig.providerName)
-
- pConfig := providerConfig.redisConfig
- assert.Equal(t, "redis.example.com:6379", pConfig.Addr)
- assert.Equal(t, "pass", pConfig.Password)
- // DbNumber is the fasthttp/session property for the Redis DB Index.
- assert.Equal(t, 0, pConfig.DB)
- assert.Equal(t, 0, pConfig.PoolSize)
- assert.Equal(t, 0, pConfig.MinIdleConns)
-
- require.NotNil(t, pConfig.TLSConfig)
- require.Equal(t, uint16(tls.VersionTLS13), pConfig.TLSConfig.MinVersion)
- require.Equal(t, "redis.fqdn.example.com", pConfig.TLSConfig.ServerName)
- require.False(t, pConfig.TLSConfig.InsecureSkipVerify)
-}
-
-func TestShouldCreateRedisSessionProvider(t *testing.T) {
- configuration := schema.SessionConfiguration{}
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
- configuration.Redis = &schema.RedisSessionConfiguration{
- Host: "redis.example.com",
- Port: 6379,
- Password: "pass",
- }
- providerConfig := NewProviderConfig(configuration, nil)
-
- assert.Nil(t, providerConfig.redisSentinelConfig)
- assert.Equal(t, "my_session", providerConfig.config.CookieName)
- assert.Equal(t, testDomain, providerConfig.config.Domain)
- assert.Equal(t, true, providerConfig.config.Secure)
- assert.Equal(t, time.Duration(40)*time.Second, providerConfig.config.Expiration)
- assert.True(t, providerConfig.config.IsSecureFunc(nil))
-
- assert.Equal(t, "redis", providerConfig.providerName)
-
- pConfig := providerConfig.redisConfig
- assert.Equal(t, "redis.example.com:6379", pConfig.Addr)
- assert.Equal(t, "pass", pConfig.Password)
- // DbNumber is the fasthttp/session property for the Redis DB Index.
- assert.Equal(t, 0, pConfig.DB)
- assert.Equal(t, 0, pConfig.PoolSize)
- assert.Equal(t, 0, pConfig.MinIdleConns)
-
- assert.Nil(t, pConfig.TLSConfig)
-}
-
-func TestShouldCreateRedisSentinelSessionProviderWithoutDuplicateHosts(t *testing.T) {
- configuration := schema.SessionConfiguration{}
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
- configuration.Redis = &schema.RedisSessionConfiguration{
- Host: "REDIS.example.com",
- Port: 26379,
- Password: "pass",
- MaximumActiveConnections: 8,
- MinimumIdleConnections: 2,
- HighAvailability: &schema.RedisHighAvailabilityConfiguration{
- SentinelName: "mysent",
- SentinelPassword: "mypass",
- Nodes: []schema.RedisNode{
- {
- Host: "redis2.example.com",
- Port: 26379,
- },
- {
- Host: "redis.example.com",
- Port: 26379,
- },
- },
- },
- }
-
- providerConfig := NewProviderConfig(configuration, nil)
-
- assert.Len(t, providerConfig.redisSentinelConfig.SentinelAddrs, 2)
- assert.Equal(t, providerConfig.redisSentinelConfig.SentinelAddrs[0], "redis.example.com:26379")
- assert.Equal(t, providerConfig.redisSentinelConfig.SentinelAddrs[1], "redis2.example.com:26379")
-}
-
-func TestShouldCreateRedisSentinelSessionProvider(t *testing.T) {
- configuration := schema.SessionConfiguration{}
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
- configuration.Redis = &schema.RedisSessionConfiguration{
- Host: "redis.example.com",
- Port: 26379,
- Password: "pass",
- MaximumActiveConnections: 8,
- MinimumIdleConnections: 2,
- HighAvailability: &schema.RedisHighAvailabilityConfiguration{
- SentinelName: "mysent",
- SentinelPassword: "mypass",
- Nodes: []schema.RedisNode{
- {
- Host: "redis2.example.com",
- Port: 26379,
- },
- },
- },
- }
- providerConfig := NewProviderConfig(configuration, nil)
-
- assert.Nil(t, providerConfig.redisConfig)
- assert.Equal(t, "my_session", providerConfig.config.CookieName)
- assert.Equal(t, testDomain, providerConfig.config.Domain)
- assert.Equal(t, true, providerConfig.config.Secure)
- assert.Equal(t, time.Duration(40)*time.Second, providerConfig.config.Expiration)
- assert.True(t, providerConfig.config.IsSecureFunc(nil))
-
- assert.Equal(t, "redis-sentinel", providerConfig.providerName)
-
- pConfig := providerConfig.redisSentinelConfig
- assert.Equal(t, "redis.example.com:26379", pConfig.SentinelAddrs[0])
- assert.Equal(t, "redis2.example.com:26379", pConfig.SentinelAddrs[1])
- assert.Equal(t, "pass", pConfig.Password)
- assert.Equal(t, "mysent", pConfig.MasterName)
- assert.Equal(t, "mypass", pConfig.SentinelPassword)
- assert.False(t, pConfig.RouteRandomly)
- assert.False(t, pConfig.RouteByLatency)
- assert.Equal(t, 8, pConfig.PoolSize)
- assert.Equal(t, 2, pConfig.MinIdleConns)
-
- // DbNumber is the fasthttp/session property for the Redis DB Index.
- assert.Equal(t, 0, pConfig.DB)
- assert.Nil(t, pConfig.TLSConfig)
-}
-
-func TestShouldSetCookieSameSite(t *testing.T) {
- configuration := schema.SessionConfiguration{}
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
-
- configValueExpectedValue := map[string]fasthttp.CookieSameSite{
- "": fasthttp.CookieSameSiteLaxMode,
- "lax": fasthttp.CookieSameSiteLaxMode,
- "strict": fasthttp.CookieSameSiteStrictMode,
- "none": fasthttp.CookieSameSiteNoneMode,
- "invalid": fasthttp.CookieSameSiteLaxMode,
- }
-
- for configValue, expectedValue := range configValueExpectedValue {
- configuration.SameSite = configValue
- providerConfig := NewProviderConfig(configuration, nil)
-
- assert.Equal(t, expectedValue, providerConfig.config.CookieSameSite)
- }
-}
-
-func TestShouldCreateRedisSessionProviderWithUnixSocket(t *testing.T) {
- configuration := schema.SessionConfiguration{}
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
- configuration.Redis = &schema.RedisSessionConfiguration{
- Host: "/var/run/redis/redis.sock",
- Port: 0,
- Password: "pass",
- }
-
- providerConfig := NewProviderConfig(configuration, nil)
-
- assert.Nil(t, providerConfig.redisSentinelConfig)
-
- assert.Equal(t, "my_session", providerConfig.config.CookieName)
- assert.Equal(t, testDomain, providerConfig.config.Domain)
- assert.Equal(t, true, providerConfig.config.Secure)
- assert.Equal(t, time.Duration(40)*time.Second, providerConfig.config.Expiration)
- assert.True(t, providerConfig.config.IsSecureFunc(nil))
-
- assert.Equal(t, "redis", providerConfig.providerName)
-
- pConfig := providerConfig.redisConfig
- assert.Equal(t, "/var/run/redis/redis.sock", pConfig.Addr)
- assert.Equal(t, "pass", pConfig.Password)
- // DbNumber is the fasthttp/session property for the Redis DB Index.
- assert.Equal(t, 0, pConfig.DB)
- assert.Nil(t, pConfig.TLSConfig)
-}
-
-func TestShouldSetDbNumber(t *testing.T) {
- configuration := schema.SessionConfiguration{}
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
- configuration.Redis = &schema.RedisSessionConfiguration{
- Host: "redis.example.com",
- Port: 6379,
- Password: "pass",
- DatabaseIndex: 5,
- }
-
- providerConfig := NewProviderConfig(configuration, nil)
-
- assert.Nil(t, providerConfig.redisSentinelConfig)
-
- assert.Equal(t, "redis", providerConfig.providerName)
- pConfig := providerConfig.redisConfig
- // DbNumber is the fasthttp/session property for the Redis DB Index.
- assert.Equal(t, 5, pConfig.DB)
-}
-
-func TestShouldUseEncryptingSerializerWithRedis(t *testing.T) {
- configuration := schema.SessionConfiguration{}
- configuration.Secret = "abc"
- configuration.Redis = &schema.RedisSessionConfiguration{
- Host: "redis.example.com",
- Port: 6379,
- Password: "pass",
- DatabaseIndex: 5,
- }
- providerConfig := NewProviderConfig(configuration, nil)
-
- payload := session.Dict{}
- payload.Set("key", "value")
-
- encoded, err := providerConfig.config.EncodeFunc(payload)
- require.NoError(t, err)
-
- // Now we try to decrypt what has been serialized.
- key := sha256.Sum256([]byte("abc"))
- decrypted, err := utils.Decrypt(encoded, &key)
- require.NoError(t, err)
-
- decoded := session.Dict{}
- _, _ = decoded.UnmarshalMsg(decrypted)
- assert.Equal(t, "value", decoded.Get("key"))
-}
diff --git a/internal/session/provider_test.go b/internal/session/provider_test.go
index 5deb312d9..81eab3cff 100644
--- a/internal/session/provider_test.go
+++ b/internal/session/provider_test.go
@@ -5,7 +5,6 @@ import (
"time"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
"github.com/valyala/fasthttp"
"github.com/authelia/authelia/v4/internal/authentication"
@@ -14,16 +13,31 @@ import (
"github.com/authelia/authelia/v4/internal/oidc"
)
+func newTestSession() (*Session, error) {
+ config := schema.SessionConfiguration{}
+ config.Cookies = []schema.SessionCookieConfiguration{
+ {
+ SessionCookieCommonConfiguration: schema.SessionCookieCommonConfiguration{
+ Name: testName,
+ Domain: testDomain,
+ Expiration: testExpiration,
+ },
+ },
+ }
+
+ provider := NewProvider(config, nil)
+
+ return provider.Get(testDomain)
+}
+
func TestShouldInitializerSession(t *testing.T) {
ctx := &fasthttp.RequestCtx{}
- configuration := schema.SessionConfiguration{}
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
- provider := NewProvider(configuration, nil)
+ provider, err := newTestSession()
+ assert.NoError(t, err)
+
session, err := provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t, NewDefaultUserSession(), session)
}
@@ -31,22 +45,19 @@ func TestShouldInitializerSession(t *testing.T) {
func TestShouldUpdateSession(t *testing.T) {
ctx := &fasthttp.RequestCtx{}
- configuration := schema.SessionConfiguration{}
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
+ provider, err := newTestSession()
+ assert.NoError(t, err)
- provider := NewProvider(configuration, nil)
session, _ := provider.GetSession(ctx)
session.Username = testUsername
session.AuthenticationLevel = authentication.TwoFactor
- err := provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ err = provider.SaveSession(ctx, session)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t, UserSession{
Username: testUsername,
@@ -56,26 +67,23 @@ func TestShouldUpdateSession(t *testing.T) {
func TestShouldSetSessionAuthenticationLevels(t *testing.T) {
ctx := &fasthttp.RequestCtx{}
- configuration := schema.SessionConfiguration{}
timeOneFactor := time.Unix(1625048140, 0)
timeTwoFactor := time.Unix(1625048150, 0)
timeZeroFactor := time.Unix(0, 0)
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
+ provider, err := newTestSession()
+ assert.NoError(t, err)
- provider := NewProvider(configuration, nil)
session, _ := provider.GetSession(ctx)
session.SetOneFactor(timeOneFactor, &authentication.UserDetails{Username: testUsername}, false)
- err := provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ err = provider.SaveSession(ctx, session)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
authAt, err := session.AuthenticatedTime(authorization.OneFactor)
assert.NoError(t, err)
@@ -100,10 +108,10 @@ func TestShouldSetSessionAuthenticationLevels(t *testing.T) {
session.SetTwoFactorDuo(timeTwoFactor)
err = provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t, UserSession{
Username: testUsername,
@@ -129,26 +137,23 @@ func TestShouldSetSessionAuthenticationLevels(t *testing.T) {
func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
ctx := &fasthttp.RequestCtx{}
- configuration := schema.SessionConfiguration{}
timeOneFactor := time.Unix(1625048140, 0)
timeTwoFactor := time.Unix(1625048150, 0)
timeZeroFactor := time.Unix(0, 0)
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
+ provider, err := newTestSession()
+ assert.NoError(t, err)
- provider := NewProvider(configuration, nil)
session, _ := provider.GetSession(ctx)
session.SetOneFactor(timeOneFactor, &authentication.UserDetails{Username: testUsername}, false)
- err := provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ err = provider.SaveSession(ctx, session)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
authAt, err := session.AuthenticatedTime(authorization.OneFactor)
assert.NoError(t, err)
@@ -173,10 +178,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
session.SetTwoFactorWebauthn(timeTwoFactor, false, false)
err = provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t, oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true}, session.AuthenticationMethodRefs)
assert.True(t, session.AuthenticationMethodRefs.MultiFactorAuthentication())
@@ -196,10 +201,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
session.SetTwoFactorWebauthn(timeTwoFactor, false, false)
err = provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true},
@@ -208,10 +213,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
session.SetTwoFactorWebauthn(timeTwoFactor, false, false)
err = provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true},
@@ -220,10 +225,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
session.SetTwoFactorWebauthn(timeTwoFactor, true, false)
err = provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true, WebauthnUserPresence: true},
@@ -232,10 +237,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
session.SetTwoFactorWebauthn(timeTwoFactor, true, false)
err = provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true, WebauthnUserPresence: true},
@@ -244,10 +249,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
session.SetTwoFactorWebauthn(timeTwoFactor, false, true)
err = provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true, WebauthnUserVerified: true},
@@ -256,10 +261,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
session.SetTwoFactorWebauthn(timeTwoFactor, false, true)
err = provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true, WebauthnUserVerified: true},
@@ -268,10 +273,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
session.SetTwoFactorTOTP(timeTwoFactor)
err = provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, TOTP: true, Webauthn: true, WebauthnUserVerified: true},
@@ -280,10 +285,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
session.SetTwoFactorTOTP(timeTwoFactor)
err = provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ assert.NoError(t, err)
session, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, TOTP: true, Webauthn: true, WebauthnUserVerified: true},
@@ -292,31 +297,28 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
func TestShouldDestroySessionAndWipeSessionData(t *testing.T) {
ctx := &fasthttp.RequestCtx{}
- configuration := schema.SessionConfiguration{}
- configuration.Domain = testDomain
- configuration.Name = testName
- configuration.Expiration = testExpiration
+ domainSession, err := newTestSession()
+ assert.NoError(t, err)
- provider := NewProvider(configuration, nil)
- session, err := provider.GetSession(ctx)
- require.NoError(t, err)
+ session, err := domainSession.GetSession(ctx)
+ assert.NoError(t, err)
session.Username = testUsername
session.AuthenticationLevel = authentication.TwoFactor
- err = provider.SaveSession(ctx, session)
- require.NoError(t, err)
+ err = domainSession.SaveSession(ctx, session)
+ assert.NoError(t, err)
- newUserSession, err := provider.GetSession(ctx)
- require.NoError(t, err)
+ newUserSession, err := domainSession.GetSession(ctx)
+ assert.NoError(t, err)
assert.Equal(t, testUsername, newUserSession.Username)
assert.Equal(t, authentication.TwoFactor, newUserSession.AuthenticationLevel)
- err = provider.DestroySession(ctx)
- require.NoError(t, err)
+ err = domainSession.DestroySession(ctx)
+ assert.NoError(t, err)
- newUserSession, err = provider.GetSession(ctx)
- require.NoError(t, err)
+ newUserSession, err = domainSession.GetSession(ctx)
+ assert.NoError(t, err)
assert.Equal(t, "", newUserSession.Username)
assert.Equal(t, authentication.NotAuthenticated, newUserSession.AuthenticationLevel)
}
diff --git a/internal/session/session.go b/internal/session/session.go
new file mode 100644
index 000000000..89c32830e
--- /dev/null
+++ b/internal/session/session.go
@@ -0,0 +1,113 @@
+package session
+
+import (
+ "encoding/json"
+ "time"
+
+ fasthttpsession "github.com/fasthttp/session/v2"
+ "github.com/valyala/fasthttp"
+
+ "github.com/authelia/authelia/v4/internal/configuration/schema"
+)
+
+// Session a session provider.
+type Session struct {
+ Config schema.SessionCookieConfiguration
+
+ sessionHolder *fasthttpsession.Session
+}
+
+// GetSession return the user session from a request.
+func (p *Session) GetSession(ctx *fasthttp.RequestCtx) (UserSession, error) {
+ store, err := p.sessionHolder.Get(ctx)
+
+ if err != nil {
+ return NewDefaultUserSession(), err
+ }
+
+ userSessionJSON, ok := store.Get(userSessionStorerKey).([]byte)
+
+ // If userSession is not yet defined we create the new session with default values
+ // and save it in the store.
+ if !ok {
+ userSession := NewDefaultUserSession()
+
+ store.Set(userSessionStorerKey, userSession)
+
+ return userSession, nil
+ }
+
+ var userSession UserSession
+ err = json.Unmarshal(userSessionJSON, &userSession)
+
+ if err != nil {
+ return NewDefaultUserSession(), err
+ }
+
+ return userSession, nil
+}
+
+// SaveSession save the user session.
+func (p *Session) SaveSession(ctx *fasthttp.RequestCtx, userSession UserSession) error {
+ store, err := p.sessionHolder.Get(ctx)
+
+ if err != nil {
+ return err
+ }
+
+ userSessionJSON, err := json.Marshal(userSession)
+
+ if err != nil {
+ return err
+ }
+
+ store.Set(userSessionStorerKey, userSessionJSON)
+
+ err = p.sessionHolder.Save(ctx, store)
+
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// RegenerateSession regenerate a session ID.
+func (p *Session) RegenerateSession(ctx *fasthttp.RequestCtx) error {
+ err := p.sessionHolder.Regenerate(ctx)
+
+ return err
+}
+
+// DestroySession destroy a session ID and delete the cookie.
+func (p *Session) DestroySession(ctx *fasthttp.RequestCtx) error {
+ return p.sessionHolder.Destroy(ctx)
+}
+
+// UpdateExpiration update the expiration of the cookie and session.
+func (p *Session) UpdateExpiration(ctx *fasthttp.RequestCtx, expiration time.Duration) error {
+ store, err := p.sessionHolder.Get(ctx)
+
+ if err != nil {
+ return err
+ }
+
+ err = store.SetExpiration(expiration)
+
+ if err != nil {
+ return err
+ }
+
+ return p.sessionHolder.Save(ctx, store)
+}
+
+// GetExpiration get the expiration of the current session.
+func (p *Session) GetExpiration(ctx *fasthttp.RequestCtx) (time.Duration, error) {
+ store, err := p.sessionHolder.Get(ctx)
+
+ if err != nil {
+ return time.Duration(0), err
+ }
+
+ return store.GetExpiration(), nil
+}
diff --git a/internal/session/types.go b/internal/session/types.go
index a3adbb89b..f256da2cb 100644
--- a/internal/session/types.go
+++ b/internal/session/types.go
@@ -3,8 +3,7 @@ package session
import (
"time"
- session "github.com/fasthttp/session/v2"
- "github.com/fasthttp/session/v2/providers/redis"
+ "github.com/fasthttp/session/v2"
"github.com/go-webauthn/webauthn/webauthn"
"github.com/authelia/authelia/v4/internal/authentication"
@@ -13,10 +12,8 @@ import (
// ProviderConfig is the configuration used to create the session provider.
type ProviderConfig struct {
- config session.Config
- redisConfig *redis.Config
- redisSentinelConfig *redis.FailoverConfig
- providerName string
+ config session.Config
+ providerName string
}
// UserSession is the structure representing the session of a user.
diff --git a/internal/suites/ActiveDirectory/configuration.yml b/internal/suites/ActiveDirectory/configuration.yml
index a1adb91cd..31f09189f 100644
--- a/internal/suites/ActiveDirectory/configuration.yml
+++ b/internal/suites/ActiveDirectory/configuration.yml
@@ -34,7 +34,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/BypassAll/configuration.yml b/internal/suites/BypassAll/configuration.yml
index 25a4c5c8b..e2b592a5f 100644
--- a/internal/suites/BypassAll/configuration.yml
+++ b/internal/suites/BypassAll/configuration.yml
@@ -23,7 +23,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/CLI/configuration.yml b/internal/suites/CLI/configuration.yml
index 9a978c5ae..99a1aee7d 100644
--- a/internal/suites/CLI/configuration.yml
+++ b/internal/suites/CLI/configuration.yml
@@ -20,10 +20,13 @@ authentication_backend:
session:
secret: unsecure_session_secret
- domain: example.com
- expiration: 3600 # 1 hour
- inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ cookies:
+ - name: 'authelia_session'
+ domain: 'example.com'
+ authelia_url: 'https://login.example.com'
+ expiration: 3600 # 1 hour
+ inactivity: 300 # 5 minutes
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/Caddy/configuration.yml b/internal/suites/Caddy/configuration.yml
index 31c5d75ba..4ce6a5b4d 100644
--- a/internal/suites/Caddy/configuration.yml
+++ b/internal/suites/Caddy/configuration.yml
@@ -24,7 +24,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/Docker/configuration.yml b/internal/suites/Docker/configuration.yml
index 5ac4aaa5b..d37132c67 100644
--- a/internal/suites/Docker/configuration.yml
+++ b/internal/suites/Docker/configuration.yml
@@ -24,7 +24,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/DuoPush/configuration.yml b/internal/suites/DuoPush/configuration.yml
index 368dca94f..cb8caaec0 100644
--- a/internal/suites/DuoPush/configuration.yml
+++ b/internal/suites/DuoPush/configuration.yml
@@ -24,7 +24,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
# Configuration of the storage backend used to store data and secrets. i.e. totp data
storage:
diff --git a/internal/suites/Envoy/configuration.yml b/internal/suites/Envoy/configuration.yml
index 7a5f55448..e1a05f80d 100644
--- a/internal/suites/Envoy/configuration.yml
+++ b/internal/suites/Envoy/configuration.yml
@@ -21,10 +21,12 @@ authentication_backend:
session:
secret: unsecure_session_secret
- domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
+ cookies:
+ - name: 'authelia_session'
+ domain: 'example.com'
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/HAProxy/configuration.yml b/internal/suites/HAProxy/configuration.yml
index 193cae03d..1de216395 100644
--- a/internal/suites/HAProxy/configuration.yml
+++ b/internal/suites/HAProxy/configuration.yml
@@ -23,7 +23,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/HighAvailability/configuration.yml b/internal/suites/HighAvailability/configuration.yml
index 2b0cc5987..530b9bdf3 100644
--- a/internal/suites/HighAvailability/configuration.yml
+++ b/internal/suites/HighAvailability/configuration.yml
@@ -102,7 +102,7 @@ session:
- host: redis-sentinel-2
port: 26379
- remember_me_duration: 1y
+ remember_me: 1y
regulation:
max_retries: 3
diff --git a/internal/suites/LDAP/configuration.yml b/internal/suites/LDAP/configuration.yml
index 4bcd9a307..f69a46c7c 100644
--- a/internal/suites/LDAP/configuration.yml
+++ b/internal/suites/LDAP/configuration.yml
@@ -38,7 +38,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/MariaDB/configuration.yml b/internal/suites/MariaDB/configuration.yml
index 0c406aa1f..3228275b3 100644
--- a/internal/suites/MariaDB/configuration.yml
+++ b/internal/suites/MariaDB/configuration.yml
@@ -24,7 +24,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
# Configuration of the storage backend used to store data and secrets. i.e. totp data
storage:
diff --git a/internal/suites/MultiCookieDomain/configuration.yml b/internal/suites/MultiCookieDomain/configuration.yml
new file mode 100644
index 000000000..30e194678
--- /dev/null
+++ b/internal/suites/MultiCookieDomain/configuration.yml
@@ -0,0 +1,219 @@
+---
+###############################################################
+# Authelia minimal configuration #
+###############################################################
+
+jwt_secret: unsecure_secret
+theme: auto
+
+server:
+ port: 9091
+ tls:
+ certificate: /config/ssl/cert.pem
+ key: /config/ssl/key.pem
+
+telemetry:
+ metrics:
+ enabled: true
+ address: tcp://0.0.0.0:9959
+
+log:
+ level: debug
+
+authentication_backend:
+ file:
+ path: /config/users.yml
+
+session:
+ secret: unsecure_session_secret
+ expiration: 3600
+ inactivity: 300
+ remember_me: 1y
+ cookies:
+ - name: 'authelia_session'
+ domain: 'example.com'
+ - name: 'example2_session'
+ domain: 'example2.com'
+ authelia_url: 'https://login.example2.com'
+ remember_me: -1
+ - name: 'authelia_session'
+ domain: 'example3.com'
+ authelia_url: 'https://login.example3.com'
+
+storage:
+ encryption_key: a_not_so_secure_encryption_key
+ local:
+ path: /config/db.sqlite
+
+totp:
+ issuer: example.com
+
+access_control:
+ default_policy: deny
+
+ rules:
+ # First cookie domain
+ - domain: singlefactor.example.com
+ policy: one_factor
+
+ - domain: public.example.com
+ policy: bypass
+
+ - domain: secure.example.com
+ policy: bypass
+ methods:
+ - OPTIONS
+
+ - domain: secure.example.com
+ policy: two_factor
+
+ - domain: "*.example.com"
+ subject: "group:admins"
+ policy: two_factor
+
+ - domain: dev.example.com
+ resources:
+ - "^/users/john/.*$"
+ subject: "user:john"
+ policy: two_factor
+
+ - domain: dev.example.com
+ resources:
+ - "^/users/harry/.*$"
+ subject: "user:harry"
+ policy: two_factor
+
+ - domain: "*.mail.example.com"
+ subject: "user:bob"
+ policy: two_factor
+
+ - domain: dev.example.com
+ resources:
+ - "^/users/bob/.*$"
+ subject: "user:bob"
+ policy: two_factor
+
+ # Second cookie domain
+ - domain: singlefactor.example2.com
+ policy: one_factor
+
+ - domain: public.example2.com
+ policy: bypass
+
+ - domain: secure.example2.com
+ policy: bypass
+ methods:
+ - OPTIONS
+
+ - domain: secure.example2.com
+ policy: two_factor
+
+ - domain: "*.example2.com"
+ subject: "group:admins"
+ policy: two_factor
+
+ - domain: dev.example2.com
+ resources:
+ - "^/users/john/.*$"
+ subject: "user:john"
+ policy: two_factor
+
+ - domain: dev.example2.com
+ resources:
+ - "^/users/harry/.*$"
+ subject: "user:harry"
+ policy: two_factor
+
+ - domain: "*.mail.example2.com"
+ subject: "user:bob"
+ policy: two_factor
+
+ - domain: dev.example2.com
+ resources:
+ - "^/users/bob/.*$"
+ subject: "user:bob"
+ policy: two_factor
+
+ # Third cookie domain
+ - domain: singlefactor.example3.com
+ policy: one_factor
+
+ - domain: public.example3.com
+ policy: bypass
+
+ - domain: secure.example3.com
+ policy: bypass
+ methods:
+ - OPTIONS
+
+ - domain: secure.example3.com
+ policy: two_factor
+
+ - domain: "*.example3.com"
+ subject: "group:admins"
+ policy: two_factor
+
+ - domain: dev.example3.com
+ resources:
+ - "^/users/john/.*$"
+ subject: "user:john"
+ policy: two_factor
+
+ - domain: dev.example3.com
+ resources:
+ - "^/users/harry/.*$"
+ subject: "user:harry"
+ policy: two_factor
+
+ - domain: "*.mail.example3.com"
+ subject: "user:bob"
+ policy: two_factor
+
+ - domain: dev.example3.com
+ resources:
+ - "^/users/bob/.*$"
+ subject: "user:bob"
+ policy: two_factor
+
+
+regulation:
+ # Set it to 0 to disable max_retries.
+ max_retries: 3
+ # The user is banned if the authentication failed `max_retries` times in a `find_time` seconds window.
+ find_time: 300
+ # The length of time before a banned user can login again.
+ ban_time: 900
+
+notifier:
+ smtp:
+ host: smtp
+ port: 1025
+ sender: admin@example.com
+ disable_require_tls: true
+ntp:
+ ## NTP server address
+ address: "time.cloudflare.com:123"
+ ## ntp version
+ version: 4
+ ## "maximum desynchronization" is the allowed offset time between the host and the ntp server
+ max_desync: 3s
+ ## You can enable or disable the NTP synchronization check on startup
+ disable_startup_check: false
+
+password_policy:
+ standard:
+ # Enables standard password Policy
+ enabled: false
+ min_length: 8
+ max_length: 0
+ require_uppercase: true
+ require_lowercase: true
+ require_number: true
+ require_special: true
+ zxcvbn:
+ ## zxcvbn: uses zxcvbn for password strength checking (see: https://github.com/dropbox/zxcvbn)
+ ## Note that the zxcvbn option does not prohibit the user from using a weak password,
+ ## it only offers feedback about the strength of the password they are entering.
+ ## if you need to enforce password rules, you should use `mode=classic`
+ enabled: false
+...
diff --git a/internal/suites/MultiCookieDomain/docker-compose.yml b/internal/suites/MultiCookieDomain/docker-compose.yml
new file mode 100644
index 000000000..1a2f0122b
--- /dev/null
+++ b/internal/suites/MultiCookieDomain/docker-compose.yml
@@ -0,0 +1,9 @@
+---
+version: '3'
+services:
+ authelia-backend:
+ volumes:
+ - './MultiCookieDomain/configuration.yml:/config/configuration.yml:ro'
+ - './MultiCookieDomain/users.yml:/config/users.yml'
+ - './common/ssl:/config/ssl:ro'
+...
diff --git a/internal/suites/MultiCookieDomain/users.yml b/internal/suites/MultiCookieDomain/users.yml
new file mode 100644
index 000000000..a52978b20
--- /dev/null
+++ b/internal/suites/MultiCookieDomain/users.yml
@@ -0,0 +1,35 @@
+---
+###############################################################
+# Users Database #
+###############################################################
+
+# This file can be used if you do not have an LDAP set up.
+
+# List of users
+users:
+ john:
+ displayname: "John Doe"
+ password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" # yamllint disable-line rule:line-length
+ email: john.doe@authelia.com
+ groups:
+ - admins
+ - dev
+
+ harry:
+ displayname: "Harry Potter"
+ password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" # yamllint disable-line rule:line-length
+ email: harry.potter@authelia.com
+ groups: []
+
+ bob:
+ displayname: "Bob Dylan"
+ password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" # yamllint disable-line rule:line-length
+ email: bob.dylan@authelia.com
+ groups:
+ - dev
+
+ james:
+ displayname: "James Dean"
+ password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" # yamllint disable-line rule:line-length
+ email: james.dean@authelia.com
+...
diff --git a/internal/suites/MySQL/configuration.yml b/internal/suites/MySQL/configuration.yml
index f57595375..cf8742d0b 100644
--- a/internal/suites/MySQL/configuration.yml
+++ b/internal/suites/MySQL/configuration.yml
@@ -25,7 +25,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
# Configuration of the storage backend used to store data and secrets. i.e. totp data
storage:
diff --git a/internal/suites/NetworkACL/configuration.yml b/internal/suites/NetworkACL/configuration.yml
index a4a6103d0..5bdf2fa87 100644
--- a/internal/suites/NetworkACL/configuration.yml
+++ b/internal/suites/NetworkACL/configuration.yml
@@ -23,7 +23,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
# Configuration of the storage backend used to store data and secrets. i.e. totp data
storage:
diff --git a/internal/suites/OIDC/configuration.yml b/internal/suites/OIDC/configuration.yml
index 9e0c5f383..543c6da38 100644
--- a/internal/suites/OIDC/configuration.yml
+++ b/internal/suites/OIDC/configuration.yml
@@ -16,10 +16,13 @@ authentication_backend:
session:
secret: unsecure_session_secret
- domain: example.com
- expiration: 3600 # 1 hour
- inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+
+ cookies:
+ - domain: example.com
+ expiration: 3600 # 1 hour
+ inactivity: 300 # 5 minutes
+ remember_me: 1y
+
# We use redis here to keep the users authenticated when Authelia restarts
# It eases development.
redis:
diff --git a/internal/suites/OIDCTraefik/configuration.yml b/internal/suites/OIDCTraefik/configuration.yml
index 1766da293..367dc4e2b 100644
--- a/internal/suites/OIDCTraefik/configuration.yml
+++ b/internal/suites/OIDCTraefik/configuration.yml
@@ -19,7 +19,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
# We use redis here to keep the users authenticated when Authelia restarts
# It eases development.
redis:
diff --git a/internal/suites/OneFactorOnly/configuration.yml b/internal/suites/OneFactorOnly/configuration.yml
index a2d44f649..203a803bd 100644
--- a/internal/suites/OneFactorOnly/configuration.yml
+++ b/internal/suites/OneFactorOnly/configuration.yml
@@ -24,7 +24,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/PathPrefix/configuration.yml b/internal/suites/PathPrefix/configuration.yml
index b2865affc..323187c5e 100644
--- a/internal/suites/PathPrefix/configuration.yml
+++ b/internal/suites/PathPrefix/configuration.yml
@@ -24,7 +24,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/Postgres/configuration.yml b/internal/suites/Postgres/configuration.yml
index a95ffa96c..958bf763a 100644
--- a/internal/suites/Postgres/configuration.yml
+++ b/internal/suites/Postgres/configuration.yml
@@ -24,7 +24,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
# Configuration of the storage backend used to store data and secrets. i.e. totp data
storage:
diff --git a/internal/suites/ShortTimeouts/configuration.yml b/internal/suites/ShortTimeouts/configuration.yml
index 0ac5fe3fe..e73648a65 100644
--- a/internal/suites/ShortTimeouts/configuration.yml
+++ b/internal/suites/ShortTimeouts/configuration.yml
@@ -21,10 +21,12 @@ authentication_backend:
session:
secret: unsecure_session_secret
- domain: example.com
- inactivity: 5
- expiration: 8
- remember_me_duration: 1y
+ cookies:
+ - name: authelia_session
+ domain: example.com
+ inactivity: 5
+ expiration: 8
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/Standalone/configuration.yml b/internal/suites/Standalone/configuration.yml
index 5a959c9b3..a86b20807 100644
--- a/internal/suites/Standalone/configuration.yml
+++ b/internal/suites/Standalone/configuration.yml
@@ -25,9 +25,9 @@ authentication_backend:
session:
domain: example.com
- expiration: 3600 # 1 hour
- inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ expiration: 3600
+ inactivity: 300
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
@@ -81,6 +81,7 @@ access_control:
subject: "user:bob"
policy: two_factor
+
regulation:
# Set it to 0 to disable max_retries.
max_retries: 3
diff --git a/internal/suites/Traefik/configuration.yml b/internal/suites/Traefik/configuration.yml
index 31c5d75ba..4ce6a5b4d 100644
--- a/internal/suites/Traefik/configuration.yml
+++ b/internal/suites/Traefik/configuration.yml
@@ -24,7 +24,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
storage:
encryption_key: a_not_so_secure_encryption_key
diff --git a/internal/suites/Traefik2/configuration.yml b/internal/suites/Traefik2/configuration.yml
index 3192c9bec..8442be3cb 100644
--- a/internal/suites/Traefik2/configuration.yml
+++ b/internal/suites/Traefik2/configuration.yml
@@ -24,7 +24,7 @@ session:
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
- remember_me_duration: 1y
+ remember_me: 1y
redis:
host: redis
port: 6379
diff --git a/internal/suites/action_login.go b/internal/suites/action_login.go
index 9eaf193a7..d44aec6e4 100644
--- a/internal/suites/action_login.go
+++ b/internal/suites/action_login.go
@@ -45,14 +45,14 @@ click:
}
// Login 1FA.
-func (rs *RodSession) doLoginOneFactor(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool, targetURL string) {
- rs.doVisitLoginPage(t, page, targetURL)
+func (rs *RodSession) doLoginOneFactor(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool, domain string, targetURL string) {
+ rs.doVisitLoginPage(t, page, domain, targetURL)
rs.doFillLoginPageAndClick(t, page, username, password, keepMeLoggedIn)
}
// Login 1FA and 2FA subsequently (must already be registered).
func (rs *RodSession) doLoginTwoFactor(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool, otpSecret, targetURL string) {
- rs.doLoginOneFactor(t, page, username, password, keepMeLoggedIn, targetURL)
+ rs.doLoginOneFactor(t, page, username, password, keepMeLoggedIn, BaseDomain, targetURL)
rs.verifyIsSecondFactorPage(t, page)
rs.doValidateTOTP(t, page, otpSecret)
// timeout when targetURL is not defined to prevent a show stopping redirect when visiting a protected domain.
@@ -63,9 +63,9 @@ func (rs *RodSession) doLoginTwoFactor(t *testing.T, page *rod.Page, username, p
// Login 1FA and register 2FA.
func (rs *RodSession) doLoginAndRegisterTOTP(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool) string {
- rs.doLoginOneFactor(t, page, username, password, keepMeLoggedIn, "")
+ rs.doLoginOneFactor(t, page, username, password, keepMeLoggedIn, BaseDomain, "")
secret := rs.doRegisterTOTP(t, page)
- rs.doVisit(t, page, GetLoginBaseURL())
+ rs.doVisit(t, page, GetLoginBaseURL(BaseDomain))
rs.verifyIsSecondFactorPage(t, page)
return secret
diff --git a/internal/suites/action_logout.go b/internal/suites/action_logout.go
index c8c27afcc..56e8c6706 100644
--- a/internal/suites/action_logout.go
+++ b/internal/suites/action_logout.go
@@ -9,12 +9,12 @@ import (
)
func (rs *RodSession) doLogout(t *testing.T, page *rod.Page) {
- rs.doVisit(t, page, fmt.Sprintf("%s%s", GetLoginBaseURL(), "/logout"))
+ rs.doVisit(t, page, fmt.Sprintf("%s%s", GetLoginBaseURL(BaseDomain), "/logout"))
rs.verifyIsFirstFactorPage(t, page)
}
func (rs *RodSession) doLogoutWithRedirect(t *testing.T, page *rod.Page, targetURL string, firstFactor bool) {
- rs.doVisit(t, page, fmt.Sprintf("%s%s%s", GetLoginBaseURL(), "/logout?rd=", url.QueryEscape(targetURL)))
+ rs.doVisit(t, page, fmt.Sprintf("%s%s%s", GetLoginBaseURL(BaseDomain), "/logout?rd=", url.QueryEscape(targetURL)))
if firstFactor {
rs.verifyIsFirstFactorPage(t, page)
diff --git a/internal/suites/action_visit.go b/internal/suites/action_visit.go
index 5fb36b70b..b282ba2a7 100644
--- a/internal/suites/action_visit.go
+++ b/internal/suites/action_visit.go
@@ -26,11 +26,11 @@ func (rs *RodSession) doVisitAndVerifyOneFactorStep(t *testing.T, page *rod.Page
rs.verifyIsFirstFactorPage(t, page)
}
-func (rs *RodSession) doVisitLoginPage(t *testing.T, page *rod.Page, targetURL string) {
+func (rs *RodSession) doVisitLoginPage(t *testing.T, page *rod.Page, baseDomain string, targetURL string) {
suffix := ""
if targetURL != "" {
suffix = fmt.Sprintf("?rd=%s", targetURL)
}
- rs.doVisitAndVerifyOneFactorStep(t, page, fmt.Sprintf("%s/%s", GetLoginBaseURL(), suffix))
+ rs.doVisitAndVerifyOneFactorStep(t, page, fmt.Sprintf("%s/%s", GetLoginBaseURL(baseDomain), suffix))
}
diff --git a/internal/suites/const.go b/internal/suites/const.go
index 8a8f11cda..45cd89a00 100644
--- a/internal/suites/const.go
+++ b/internal/suites/const.go
@@ -8,16 +8,38 @@ import (
)
// BaseDomain the base domain.
-var BaseDomain = "example.com:8080"
+var (
+ BaseDomain = "example.com:8080"
+ Example2DotCom = "example2.com:8080"
+ Example3DotCom = "example3.com:8080"
+)
// PathPrefix the prefix/url_base of the login portal.
var PathPrefix = os.Getenv("PathPrefix")
+// LoginBaseURLFmt the base URL of the login portal for specified baseDomain.
+func LoginBaseURLFmt(baseDomain string) string {
+ if baseDomain == "" {
+ baseDomain = BaseDomain
+ }
+
+ return fmt.Sprintf("https://login.%s", baseDomain)
+}
+
// LoginBaseURL the base URL of the login portal.
-var LoginBaseURL = fmt.Sprintf("https://login.%s", BaseDomain)
+var LoginBaseURL = LoginBaseURLFmt(BaseDomain)
+
+// SingleFactorBaseURLFmt the base URL of the singlefactor with custom domain.
+func SingleFactorBaseURLFmt(baseDomain string) string {
+ if baseDomain == "" {
+ baseDomain = BaseDomain
+ }
+
+ return fmt.Sprintf("https://singlefactor.%s", baseDomain)
+}
// SingleFactorBaseURL the base URL of the singlefactor domain.
-var SingleFactorBaseURL = fmt.Sprintf("https://singlefactor.%s", BaseDomain)
+var SingleFactorBaseURL = SingleFactorBaseURLFmt(BaseDomain)
// AdminBaseURL the base URL of the admin domain.
var AdminBaseURL = fmt.Sprintf("https://admin.%s", BaseDomain)
diff --git a/internal/suites/environment.go b/internal/suites/environment.go
index 9cf51979b..80f5ac260 100644
--- a/internal/suites/environment.go
+++ b/internal/suites/environment.go
@@ -1,7 +1,6 @@
package suites
import (
- "fmt"
"os"
"strings"
"time"
@@ -20,9 +19,8 @@ func waitUntilServiceLogDetected(
logPatterns []string) error {
log.Debug("Waiting for service " + service + " to be ready...")
- err := utils.CheckUntil(5*time.Second, 1*time.Minute, func() (bool, error) {
+ err := utils.CheckUntil(interval, timeout, func() (bool, error) {
logs, err := dockerEnvironment.Logs(service, []string{"--tail", "20"})
- fmt.Printf(".")
if err != nil {
return false, err
@@ -35,8 +33,6 @@ func waitUntilServiceLogDetected(
return false, nil
})
- fmt.Print("\n")
-
return err
}
@@ -55,7 +51,7 @@ func waitUntilAutheliaFrontendIsReady(dockerEnvironment *DockerEnvironment) erro
90*time.Second,
dockerEnvironment,
"authelia-frontend",
- []string{"dev server running at", "ready in"})
+ []string{"dev server running at", "ready in", "server restarted"})
}
func waitUntilK3DIsReady(dockerEnvironment *DockerEnvironment) error {
diff --git a/internal/suites/example/compose/authelia/Dockerfile.backend b/internal/suites/example/compose/authelia/Dockerfile.backend
index a5fe1c342..f2f8578db 100644
--- a/internal/suites/example/compose/authelia/Dockerfile.backend
+++ b/internal/suites/example/compose/authelia/Dockerfile.backend
@@ -1,4 +1,4 @@
-FROM golang:1.19.4-alpine
+FROM golang:1.19.5-alpine
ARG USER_ID
ARG GROUP_ID
diff --git a/internal/suites/example/compose/envoy/docker-compose.yml b/internal/suites/example/compose/envoy/docker-compose.yml
index ef1b18363..807f99967 100644
--- a/internal/suites/example/compose/envoy/docker-compose.yml
+++ b/internal/suites/example/compose/envoy/docker-compose.yml
@@ -2,7 +2,7 @@
version: '3'
services:
envoy:
- image: envoyproxy/envoy:v1.24.1
+ image: envoyproxy/envoy:v1.25.0
volumes:
- ./example/compose/envoy/envoy.yaml:/etc/envoy/envoy.yaml
- ./example/compose/nginx/portal/ssl:/etc/ssl
diff --git a/internal/suites/example/compose/nginx/backend/html/admin/secret.html b/internal/suites/example/compose/nginx/backend/html/admin/secret.html
index 3cb5dce0a..9f1e17f7d 100644
--- a/internal/suites/example/compose/nginx/backend/html/admin/secret.html
+++ b/internal/suites/example/compose/nginx/backend/html/admin/secret.html
@@ -3,6 +3,21 @@
Secret
+
@@ -10,4 +25,4 @@
Go back to home page.
-