Merge origin/master into feat-settings-ui

feat-otp-verification
James Elliott 2023-03-07 10:12:49 +11:00
commit b6883a337f
No known key found for this signature in database
GPG Key ID: 0F1C4A096E857E49
60 changed files with 1429 additions and 589 deletions

View File

@ -6,7 +6,7 @@ info:
Authelia is an open-source authentication and authorization server providing 2-factor authentication and single Authelia is an open-source authentication and authorization server providing 2-factor authentication and single
sign-on (SSO) for your applications via a web portal. sign-on (SSO) for your applications via a web portal.
contact: contact:
name: Authelia Support name: Support
url: https://www.authelia.com/contact/ url: https://www.authelia.com/contact/
email: team@authelia.com email: team@authelia.com
license: license:
@ -2996,9 +2996,9 @@ components:
- "address" - "address"
- "phone" - "phone"
openid.spec.IntrospectionRequest: openid.spec.IntrospectionRequest:
type: object
required: required:
- "token" - "token"
type: object
properties: properties:
token: token:
description: > description: >
@ -3008,8 +3008,8 @@ components:
this is the "refresh_token" value returned from the token endpoint this is the "refresh_token" value returned from the token endpoint
as defined in OAuth 2.0 [RFC6749], Section 5.1. Other token types as defined in OAuth 2.0 [RFC6749], Section 5.1. Other token types
are outside the scope of this specification. are outside the scope of this specification.
type: string
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn" example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
type: string
token_type_hint: token_type_hint:
description: > description: >
A hint about the type of the token submitted for A hint about the type of the token submitted for
@ -3021,27 +3021,61 @@ components:
is able to detect the token type automatically. Values for this is able to detect the token type automatically. Values for this
field are defined in the "OAuth Token Type Hints" registry defined field are defined in the "OAuth Token Type Hints" registry defined
in OAuth Token Revocation [RFC7009]. in OAuth Token Revocation [RFC7009].
type: string
example: "access_token"
enum: enum:
- "access_token" - "access_token"
- "refresh_token" - "refresh_token"
example: "access_token"
type: string
openid.spec.AccessRequest.ClientAuth: openid.spec.AccessRequest.ClientAuth:
oneOf:
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth.Base'
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth.Secret'
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth.JWT'
openid.spec.AccessRequest.ClientAuth.Base:
required:
- "client_id"
type: object type: object
properties: properties:
client_id: client_id:
description: > description: >
REQUIRED if the client is not authenticating with the REQUIRED if the client is not authenticating with the authorization server as described in
authorization server as described in Section 3.2.1. of [RFC6749]. Section 3.2.1. of [RFC6749]. The client identifier as described in Section 2.2 of [RFC6749].
The client identifier as described in Section 2.2 of [RFC6749]. example: "my_client"
type: string type: string
example: "authelia_dc_mn123kjn12kj3123njk" openid.spec.AccessRequest.ClientAuth.Secret:
required:
- "client_secret"
type: object
properties:
client_secret: client_secret:
description: > description: >
REQUIRED. The client secret. The client MAY omit the REQUIRED. The client secret. The client MAY omit the
parameter if the client secret is an empty string. parameter if the client secret is an empty string.
type: string
format: password format: password
type: string
openid.spec.AccessRequest.ClientAuth.JWT:
allOf:
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth.Base'
- type: object
required:
- "client_assertion"
- "client_assertion_type"
properties:
client_assertion:
description: >
The value of the client_assertion_type parameter MUST be
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
enum:
- "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
example: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
type: string
client_assertion_type:
description: >
A JWT signed with HS256 using the client secret value or RS256 using a registered public key.
Theoretically a properly formed JWT signed using HS256 with the client secret as the HMAC key should
work but this has not been tested.
format: password
type: string
openid.spec.AccessRequest.AuthorizationCodeFlow: openid.spec.AccessRequest.AuthorizationCodeFlow:
allOf: allOf:
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth' - $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
@ -3051,22 +3085,22 @@ components:
- "grant_type" - "grant_type"
properties: properties:
grant_type: grant_type:
description: Value MUST be set to "urn:ietf:params:oauth:grant-type:device_code". description: Value MUST be set to "code".
type: string
enum: enum:
- "authorization_code" - "authorization_code"
type: string
code: code:
description: The Authorization Code. description: The Authorization Code.
type: string
example: "authelia_ac_1j2kn3knj12n3kj12n" example: "authelia_ac_1j2kn3knj12n3kj12n"
type: string
code_verifier: code_verifier:
description: The Authorization Code Verifier (PKCE). description: The Authorization Code Verifier (PKCE).
type: string
example: "88a25754f7c0b3b3b88cf6cd4e29e8356b160524fdc1cb329a94471825628fd3" example: "88a25754f7c0b3b3b88cf6cd4e29e8356b160524fdc1cb329a94471825628fd3"
type: string
redirect_uri: redirect_uri:
description: The original Redirect URI used in the Authorization Request. description: The original Redirect URI used in the Authorization Request.
type: string
example: "https://app.example.com/oidc/callback" example: "https://app.example.com/oidc/callback"
type: string
openid.spec.AccessRequest.DeviceCodeFlow: openid.spec.AccessRequest.DeviceCodeFlow:
allOf: allOf:
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth' - $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
@ -3077,13 +3111,13 @@ components:
properties: properties:
grant_type: grant_type:
description: Value MUST be set to "urn:ietf:params:oauth:grant-type:device_code". description: Value MUST be set to "urn:ietf:params:oauth:grant-type:device_code".
type: string
enum: enum:
- "urn:ietf:params:oauth:grant-type:device_code" - "urn:ietf:params:oauth:grant-type:device_code"
type: string
device_code: device_code:
description: The Device Authorization Code. description: The Device Authorization Code.
type: string
example: "authelia_dc_mn123kjn12kj3123njk" example: "authelia_dc_mn123kjn12kj3123njk"
type: string
openid.spec.AccessRequest.RefreshTokenFlow: openid.spec.AccessRequest.RefreshTokenFlow:
allOf: allOf:
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth' - $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
@ -3094,12 +3128,13 @@ components:
properties: properties:
grant_type: grant_type:
description: Value MUST be set to "refresh_token". description: Value MUST be set to "refresh_token".
type: string
enum: enum:
- "refresh_token" - "refresh_token"
type: string
refresh_token: refresh_token:
description: The Refresh Token. description: The Refresh Token.
example: "authelia_rt_1n2j3kihn12kj3n12k" example: "authelia_rt_1n2j3kihn12kj3n12k"
type: string
scope: scope:
description: > description: >
The scope of the access request as described by The scope of the access request as described by
@ -3107,20 +3142,30 @@ components:
not originally granted by the resource owner, and if omitted is not originally granted by the resource owner, and if omitted is
treated as equal to the scope originally granted by the treated as equal to the scope originally granted by the
resource owner. resource owner.
example: "openid profile groups"
type: string
openid.spec.AccessResponse: openid.spec.AccessResponse:
type: object type: object
required:
- "access_token"
- "token_type"
- "expires_in"
properties: properties:
access_token: access_token:
description: The access token issued by the authorization server. description: The access token issued by the authorization server.
type: string
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn" example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
refresh_token:
type: string type: string
id_token:
description: The id token issued by the authorization server.
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
type: string
refresh_token:
description: > description: >
The refresh token, which can be used to obtain new access tokens using the The refresh token, which can be used to obtain new access tokens using the
same authorization grant as described in Section 6. same authorization grant as described in Section 6.
token_type: example: "authelia_rt_kGBoSMbfVGP2RR6Kvujv3Xg7uXV2i"
type: string type: string
token_type:
description: > description: >
The access token type provides the client with the information The access token type provides the client with the information
required to successfully utilize the access token to make a protected required to successfully utilize the access token to make a protected
@ -3129,21 +3174,26 @@ components:
type. type.
enum: enum:
- "bearer" - "bearer"
example: "bearer"
type: string
expires_in: expires_in:
type: integer
description: > description: >
The lifetime in seconds of the access token. For The lifetime in seconds of the access token. For
example, the value "3600" denotes that the access token will example, the value "3600" denotes that the access token will
expire in one hour from the time the response was generated. expire in one hour from the time the response was generated.
If omitted, the authorization server SHOULD provide the If omitted, the authorization server SHOULD provide the
expiration time via other means or document the default value. expiration time via other means or document the default value.
example: 3600
type: integer
state: state:
type: string
description: Exactly the state value passed in the authorization request if present. description: Exactly the state value passed in the authorization request if present.
scope: example: "5dVZhNfri5XZS6wadskuzUk4MHYCvEcUgidjMeBjsktAhY7EKB"
type: string type: string
scope:
description: > description: >
The scope of the access token as described by Section 3.3 if it differs from the requested scope. The scope of the access token as described by Section 3.3 if it differs from the requested scope.
example: "openid profile groups"
type: string
openid.spec.AuthorizeRequest: openid.spec.AuthorizeRequest:
type: object type: object
required: required:
@ -3154,14 +3204,14 @@ components:
properties: properties:
scope: scope:
description: The requested scope. description: The requested scope.
type: string
example: "openid profile groups" example: "openid profile groups"
type: string
response_type: response_type:
$ref: '#/components/schemas/openid.spec.ResponseType' $ref: '#/components/schemas/openid.spec.ResponseType'
client_id: client_id:
description: The OAuth 2.0 client identifier. description: The OAuth 2.0 client identifier.
type: string
example: "app" example: "app"
type: string
redirect_uri: redirect_uri:
description: > description: >
Redirection URI to which the response will be sent. This URI MUST exactly match one of the Redirection URI to which the response will be sent. This URI MUST exactly match one of the
@ -3171,15 +3221,15 @@ components:
that the Client Type is confidential, as defined in Section 2.1 of OAuth 2.0, and provided the OP that the Client Type is confidential, as defined in Section 2.1 of OAuth 2.0, and provided the OP
allows the use of http Redirection URIs in this case. The Redirection URI MAY use an alternate allows the use of http Redirection URIs in this case. The Redirection URI MAY use an alternate
scheme, such as one that is intended to identify a callback into a native application. scheme, such as one that is intended to identify a callback into a native application.
type: string
example: "https://app.example.com" example: "https://app.example.com"
type: string
state: state:
description: > description: >
Opaque value used to maintain state between the request and the callback. Typically, Cross-Site Opaque value used to maintain state between the request and the callback. Typically, Cross-Site
Request Forgery (CSRF, XSRF) mitigation is done by cryptographically binding the value of this Request Forgery (CSRF, XSRF) mitigation is done by cryptographically binding the value of this
parameter with a browser cookie. parameter with a browser cookie.
type: string
example: "oV84Vsy7wyCgRk2h4aZBmXZq4q3g2f" example: "oV84Vsy7wyCgRk2h4aZBmXZq4q3g2f"
type: string
response_mode: response_mode:
$ref: '#/components/schemas/openid.spec.ResponseMode' $ref: '#/components/schemas/openid.spec.ResponseMode'
nonce: nonce:
@ -3188,14 +3238,23 @@ components:
The value is passed through unmodified from the Authentication Request to the ID Token. Sufficient The value is passed through unmodified from the Authentication Request to the ID Token. Sufficient
entropy MUST be present in the nonce values used to prevent attackers from guessing values. For entropy MUST be present in the nonce values used to prevent attackers from guessing values. For
implementation notes, see Section 15.5.2. implementation notes, see Section 15.5.2.
type: string
example: "TRMLqchoKGQNcooXvBvUy9PtmLdJGf" example: "TRMLqchoKGQNcooXvBvUy9PtmLdJGf"
type: string
display: display:
$ref: '#/components/schemas/openid.spec.DisplayType' $ref: '#/components/schemas/openid.spec.DisplayType'
prompt: prompt:
description: > description: >
Not Supported: Space delimited, case sensitive list of ASCII string values that specifies whether Not Supported: Space delimited, case sensitive list of ASCII string values that specifies whether
the Authorization Server prompts the End-User for reauthentication and consent. the Authorization Server prompts the End-User for reauthentication and consent.
enum:
- "none"
- "login"
- "consent"
- "select_account"
- "login consent"
- "login select_account"
- "consent select_account"
example: "consent"
type: string type: string
max_age: max_age:
description: > description: >
@ -3273,34 +3332,32 @@ components:
description: > description: >
A Subject Identifier is a locally unique and never reassigned identifier within the Issuer for the A Subject Identifier is a locally unique and never reassigned identifier within the Issuer for the
End-User, which is intended to be consumed by the Client. End-User, which is intended to be consumed by the Client.
type: string
enum: enum:
- "public" - "public"
- "pairwise" - "pairwise"
type: string
openid.spec.ClientAuthMethod: openid.spec.ClientAuthMethod:
description: The OAuth 2.0 / OpenID Connect 1.0 Client Authentication Method. description: The OAuth 2.0 / OpenID Connect 1.0 Client Authentication Method.
type: string
enum: enum:
- "client_secret_basic" - "client_secret_basic"
- "client_secret_post" - "client_secret_post"
- "client_secret_jwt" - "client_secret_jwt"
- "private_key_jwt" - "private_key_jwt"
- "none" - "none"
type: string
openid.spec.DisplayType: openid.spec.DisplayType:
description: > description: >
ASCII string value that specifies how the Authorization Server displays the authentication and consent user ASCII string value that specifies how the Authorization Server displays the authentication and consent user
interface pages to the End-User. interface pages to the End-User.
type: string
example: "page"
enum: enum:
- "page" - "page"
- "popup" - "popup"
- "touch" - "touch"
- "wap" - "wap"
example: "page"
type: string
openid.spec.ResponseType: openid.spec.ResponseType:
description: The OAuth 2.0 / OpenID Connect 1.0 Response Type. description: The OAuth 2.0 / OpenID Connect 1.0 Response Type.
type: string
example: "code"
enum: enum:
- "code" - "code"
- "id_token" - "id_token"
@ -3310,21 +3367,21 @@ components:
- "token id_token" - "token id_token"
- "code id_token token" - "code id_token token"
- "none" - "none"
example: "code"
type: string
openid.spec.ResponseMode: openid.spec.ResponseMode:
description: > description: >
Informs the Authorization Server of the mechanism to be used for returning parameters from the Authorization Informs the Authorization Server of the mechanism to be used for returning parameters from the Authorization
Endpoint. This use of this parameter is NOT RECOMMENDED when the Response Mode that would be requested is Endpoint. This use of this parameter is NOT RECOMMENDED when the Response Mode that would be requested is
the default mode specified for the Response Type. the default mode specified for the Response Type.
type: string
example: "query"
enum: enum:
- "query" - "query"
- "fragment" - "fragment"
- "form_post" - "form_post"
example: "query"
type: string
openid.spec.GrantType: openid.spec.GrantType:
description: The OAuth 2.0 / OpenID Connect 1.0 Grant Type. description: The OAuth 2.0 / OpenID Connect 1.0 Grant Type.
type: string
example: "authorization_code"
enum: enum:
- "authorization_code" - "authorization_code"
- "refresh_token" - "refresh_token"
@ -3332,21 +3389,23 @@ components:
- "password" - "password"
- "client_credentials" - "client_credentials"
- "urn:ietf:params:oauth:grant-type:device_code" - "urn:ietf:params:oauth:grant-type:device_code"
example: "authorization_code"
type: string
openid.spec.CodeChallengeMethod: openid.spec.CodeChallengeMethod:
description: The RFC7636 Code Challenge Verifier Method. description: The RFC7636 Code Challenge Verifier Method.
type: string
example: "S256"
enum: enum:
- "plain" - "plain"
- "S256" - "S256"
example: "S256"
type: string
openid.spec.ClaimType: openid.spec.ClaimType:
description: The representation of claims. description: The representation of claims.
type: string
example: "normal"
enum: enum:
- "normal" - "normal"
- "aggregated" - "aggregated"
- "distributed" - "distributed"
example: "normal"
type: string
jose.spec.None: jose.spec.None:
description: The JSON Web Signature Algorithm description: The JSON Web Signature Algorithm
type: string type: string
@ -3354,13 +3413,12 @@ components:
- "none" - "none"
jose.spec.JWS.None: jose.spec.JWS.None:
description: The JSON Web Signature Algorithm description: The JSON Web Signature Algorithm
type: string
oneOf: oneOf:
- $ref: '#/components/schemas/jose.spec.None' - $ref: '#/components/schemas/jose.spec.None'
- $ref: '#/components/schemas/jose.spec.jws' - $ref: '#/components/schemas/jose.spec.jws'
type: string
jose.spec.jws: jose.spec.jws:
description: The JSON Web Signature Algorithm description: The JSON Web Signature Algorithm
type: string
enum: enum:
- "HS256" - "HS256"
- "HS384" - "HS384"
@ -3374,9 +3432,9 @@ components:
- "PS256" - "PS256"
- "PS384" - "PS384"
- "PS512" - "PS512"
type: string
jose.spec.JWE.alg: jose.spec.JWE.alg:
description: The JSON Web Encryption Algorithm (CEK) description: The JSON Web Encryption Algorithm (CEK)
type: string
enum: enum:
- "RSA1_5" - "RSA1_5"
- "RSA-OAEP" - "RSA-OAEP"
@ -3395,9 +3453,9 @@ components:
- "PBES2-HS256+A128KW" - "PBES2-HS256+A128KW"
- "PBES2-HS384+A192KW" - "PBES2-HS384+A192KW"
- "PBES2-HS512+A256KW" - "PBES2-HS512+A256KW"
type: string
jose.spec.JWE.enc: jose.spec.JWE.enc:
description: The JSON Web Encryption Algorithm (Claims) description: The JSON Web Encryption Algorithm (Claims)
type: string
enum: enum:
- "A128CBC-HS256" - "A128CBC-HS256"
- "A192CBC-HS384" - "A192CBC-HS384"
@ -3406,6 +3464,7 @@ components:
- "A256CBC" - "A256CBC"
- "A128GCM" - "A128GCM"
- "A256GCM" - "A256GCM"
type: string
jose.spec.JWK.base: jose.spec.JWK.base:
type: object type: object
properties: properties:
@ -3415,21 +3474,20 @@ components:
the public key. The "use" parameter is employed to indicate whether the public key. The "use" parameter is employed to indicate whether
a public key is used for encrypting data or verifying the signature a public key is used for encrypting data or verifying the signature
on data. on data.
type: string
example: "sig"
enum: enum:
- "sig" - "sig"
- "enc" - "enc"
example: "sig"
type: string
key_ops: key_ops:
description: > description: >
The "key_ops" (key operations) parameter identifies the operation(s) The "key_ops" (key operations) parameter identifies the operation(s)
for which the key is intended to be used. The "key_ops" parameter is for which the key is intended to be used. The "key_ops" parameter is
intended for use cases in which public, private, or symmetric keys intended for use cases in which public, private, or symmetric keys
may be present. may be present.
type: array
example: ["sign"] example: ["sign"]
type: array
items: items:
type: string
enum: enum:
- "sign" - "sign"
- "verify" - "verify"
@ -3439,6 +3497,7 @@ components:
- "unwrapKey" - "unwrapKey"
- "deriveKey" - "deriveKey"
- "deriveBits" - "deriveBits"
type: string
kid: kid:
description: > description: >
The "kid" (key ID) parameter is used to match a specific key. This The "kid" (key ID) parameter is used to match a specific key. This
@ -3483,8 +3542,8 @@ components:
OPTIONAL. OPTIONAL.
type: array type: array
items: items:
type: string
format: byte format: byte
type: string
x5t: x5t:
description: > description: >
The "x5t" (X.509 certificate SHA-1 thumbprint) parameter is a The "x5t" (X.509 certificate SHA-1 thumbprint) parameter is a
@ -3493,8 +3552,8 @@ components:
thumbprints are also sometimes known as certificate fingerprints. thumbprints are also sometimes known as certificate fingerprints.
The key in the certificate MUST match the public key represented by The key in the certificate MUST match the public key represented by
other members of the JWK. Use of this member is OPTIONAL. other members of the JWK. Use of this member is OPTIONAL.
type: string
format: byte format: byte
type: string
x5t#S256: x5t#S256:
description: > description: >
The "x5t#S256" (X.509 certificate SHA-256 thumbprint) parameter is a The "x5t#S256" (X.509 certificate SHA-256 thumbprint) parameter is a
@ -3503,17 +3562,17 @@ components:
thumbprints are also sometimes known as certificate fingerprints. thumbprints are also sometimes known as certificate fingerprints.
The key in the certificate MUST match the public key represented by The key in the certificate MUST match the public key represented by
other members of the JWK. Use of this member is OPTIONAL. other members of the JWK. Use of this member is OPTIONAL.
type: string
format: byte format: byte
type: string
jose.spec.JWK.RSA: jose.spec.JWK.RSA:
description: RSA Public Key in JSON Web Key format as defined by RFC7517 and RFC7518. description: RSA Public Key in JSON Web Key format as defined by RFC7517 and RFC7518.
allOf: allOf:
- $ref: '#/components/schemas/jose.spec.JWK.base' - $ref: '#/components/schemas/jose.spec.JWK.base'
- type: object - required:
required:
- "kty" - "kty"
- "n" - "n"
- "e" - "e"
type: object
properties: properties:
kty: kty:
description: > description: >

View File

@ -7,5 +7,5 @@
package cmd package cmd
const ( const (
versionSwaggerUI = "4.16.1" versionSwaggerUI = "4.17.0"
) )

View File

@ -272,6 +272,23 @@ Allows [PKCE] `plain` challenges when set to `true`.
*__Security Notice:__* Changing this value is generally discouraged. Applications should use the `S256` [PKCE] challenge *__Security Notice:__* Changing this value is generally discouraged. Applications should use the `S256` [PKCE] challenge
method instead. method instead.
### pushed_authorizations
Controls the behaviour of [Pushed Authorization Requests].
#### enforce
{{< confkey type="boolean" default="false" required="no" >}}
When enabled all authorization requests must use the [Pushed Authorization Requests] flow.
#### context_lifespan
{{< confkey type="duration" default="5m" required="no" >}}
The maximum amount of time between the [Pushed Authorization Requests] flow being initiated and the generated
`request_uri` being utilized by a client.
### cors ### cors
Some [OpenID Connect 1.0] Endpoints need to allow cross-origin resource sharing, however some are optional. This section allows Some [OpenID Connect 1.0] Endpoints need to allow cross-origin resource sharing, however some are optional. This section allows
@ -285,6 +302,7 @@ A list of endpoints to configure with cross-origin resource sharing headers. It
option is at least in this list. The potential endpoints which this can be enabled on are as follows: option is at least in this list. The potential endpoints which this can be enabled on are as follows:
* authorization * authorization
* pushed-authorization-request
* token * token
* revocation * revocation
* introspection * introspection
@ -472,6 +490,12 @@ See the [Response Modes](../../integration/openid-connect/introduction.md#respon
The authorization policy for this client: either `one_factor` or `two_factor`. The authorization policy for this client: either `one_factor` or `two_factor`.
#### enforce_par
{{< confkey type="boolean" default="false" required="no" >}}
Enforces the use of a [Pushed Authorization Requests] flow for this client.
#### enforce_pkce #### enforce_pkce
{{< confkey type="bool" default="false" required="no" >}} {{< confkey type="bool" default="false" required="no" >}}
@ -550,3 +574,4 @@ To integrate Authelia's [OpenID Connect 1.0] implementation with a relying party
[Authorization Code Flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth [Authorization Code Flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
[Subject Identifier Type]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes [Subject Identifier Type]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
[Pairwise Identifier Algorithm]: https://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg [Pairwise Identifier Algorithm]: https://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126

View File

@ -36,3 +36,4 @@ this instance if you wanted to downgrade to pre1 you would need to use an Authel
| 5 | 4.35.1 | Fixed the oauth2_consent_session table to accept NULL subjects for users who are not yet signed in | | 5 | 4.35.1 | Fixed the oauth2_consent_session table to accept NULL subjects for users who are not yet signed in |
| 6 | 4.37.0 | Adjusted the OpenID Connect tables to allow pre-configured consent improvements | | 6 | 4.37.0 | Adjusted the OpenID Connect tables to allow pre-configured consent improvements |
| 7 | 4.37.3 | Fixed some schema inconsistencies most notably the MySQL/MariaDB Engine and Collation | | 7 | 4.37.3 | Fixed some schema inconsistencies most notably the MySQL/MariaDB Engine and Collation |
| 8 | 4.38.0 | OpenID Connect 1.0 Pushed Authorization Requests |

View File

@ -0,0 +1,36 @@
---
title: "Accessibility"
description: "Authelia Development Accessibility Guidelines"
lead: "This section covers the accessibility guidelines we aim to respect during development."
date: 2023-03-06T11:42:13+11:00
draft: false
images: []
menu:
contributing:
parent: "guidelines"
weight: 350
toc: true
---
## Backend
There are no specific guidelines for backend accessibility other than ensuring there are reasonable logging and this is
extremely subjective.
## Frontend
### Translations
We aim to ensure as much of the web frontend information displayed to users is translated by default. This allows for
both automatic and manual translations by the community to be contributed to the code base. In addition it allows for
admins to locally override these values.
### Responsive Design
We aim to make the web frontend responsive to multiple screen resolutions. There are a few guidelines which we aim to
abide by:
- The available space is utilized efficiently in order to avoid scrolling where possible.
- The user only has to scroll in one direction to view available information. This direction should always be
vertically.

View File

@ -8,7 +8,7 @@ images: []
menu: menu:
contributing: contributing:
parent: "guidelines" parent: "guidelines"
weight: 320 weight: 350
toc: true toc: true
aliases: aliases:
- /docs/contributing/style-guide.html - /docs/contributing/style-guide.html

View File

@ -210,14 +210,71 @@ These endpoints can be utilized to discover other endpoints and metadata about t
These endpoints implement OpenID Connect elements. These endpoints implement OpenID Connect elements.
| Endpoint | Path | Discovery Attribute | | Endpoint | Path | Discovery Attribute |
|:-------------------:|:-----------------------------------------------:|:----------------------:| |:-------------------------------:|:--------------------------------------------------------------:|:-------------------------------------:|
| [JSON Web Key Sets] | https://auth.example.com/jwks.json | jwks_uri | | [JSON Web Key Set] | https://auth.example.com/jwks.json | jwks_uri |
| [Authorization] | https://auth.example.com/api/oidc/authorization | authorization_endpoint | | [Authorization] | https://auth.example.com/api/oidc/authorization | authorization_endpoint |
| [Pushed Authorization Requests] | https://auth.example.com/api/oidc/pushed-authorization-request | pushed_authorization_request_endpoint |
| [Token] | https://auth.example.com/api/oidc/token | token_endpoint | | [Token] | https://auth.example.com/api/oidc/token | token_endpoint |
| [UserInfo] | https://auth.example.com/api/oidc/userinfo | userinfo_endpoint | | [UserInfo] | https://auth.example.com/api/oidc/userinfo | userinfo_endpoint |
| [Introspection] | https://auth.example.com/api/oidc/introspection | introspection_endpoint | | [Introspection] | https://auth.example.com/api/oidc/introspection | introspection_endpoint |
| [Revocation] | https://auth.example.com/api/oidc/revocation | revocation_endpoint | | [Revocation] | https://auth.example.com/api/oidc/revocation | revocation_endpoint |
## Security
The following information covers some security topics some users may wish to be familiar with.
#### Pushed Authorization Requests Endpoint
The [Pushed Authorization Requests] endpoint is discussed in depth in [RFC9126] as well as in the
[OAuth 2.0 Pushed Authorization Requests](https://oauth.net/2/pushed-authorization-requests/) documentation.
Essentially it's a special endpoint that takes the same parameters as the [Authorization] endpoint (including
[Proof Key Code Exchange](#proof-key-code-exchange)) with a few caveats:
1. The same [Client Authentication] mechanism required by the [Token] endpoint **MUST** be used.
2. The request **MUST** use the [HTTP POST method].
3. The request **MUST** use the `application/x-www-form-urlencoded` content type (i.e. the parameters **MUST** be in the
body, not the URI).
4. The request **MUST** occur over the back-channel.
The response of this endpoint is a JSON Object with two key-value pairs:
- `request_uri`
- `expires_in`
The `expires_in` indicates how long the `request_uri` is valid for. The `request_uri` is used as a parameter to the
[Authorization] endpoint instead of the standard parameters (as the `request_uri` parameter).
The advantages of this approach are as follows:
1. [Pushed Authorization Requests] cannot be created or influenced by any party other than the Relying Party (client).
2. Since you can force all [Authorization] requests to be initiated via [Pushed Authorization Requests] you drastically
improve the authorization flows resistance to phishing attacks (this can be done globally or on a per-client basis).
3. Since the [Pushed Authorization Requests] endpoint requires all of the same [Client Authentication] mechanisms as the
[Token] endpoint:
1. Clients using the confidential [Client Type] can't have [Pushed Authorization Requests] generated by parties who do not
have the credentials.
2. Clients using the public [Client Type] and utilizing [Proof Key Code Exchange](#proof-key-code-exchange) never
transmit the verifier over any front-channel making even the `plain` challenge method relatively secure.
#### Proof Key Code Exchange
The [Proof Key Code Exchange] mechanism is discussed in depth in [RFC7636] as well as in the
[OAuth 2.0 Proof Key Code Exchange](https://oauth.net/2/pkce/) documentation.
Essentially a random opaque value is generated by the Relying Party and optionally (but recommended) passed through a
SHA256 hash. The original value is saved by the Relying Party, and the hashed value is sent in the [Authorization]
request in the `code_verifier` parameter with the `code_challenge_method` set to `S256` (or `plain` using a bad practice
of not hashing the opaque value).
When the Relying Party requests the token from the [Token] endpoint, they must include the `code_verifier` parameter
again (in the body), but this time they send the value without it being hashed.
The advantages of this approach are as follows:
1. Provided the value was hashed it's certain that the Relying Party which generated the authorization request is the
same party as the one requesting the token or is permitted by the Relying Party to make this request.
2. Even when using the public [Client Type] there is a form of authentication on the [Token] endpoint.
[ID Token]: https://openid.net/specs/openid-connect-core-1_0.html#IDToken [ID Token]: https://openid.net/specs/openid-connect-core-1_0.html#IDToken
[Access Token]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.4 [Access Token]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.4
[Refresh Token]: https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens [Refresh Token]: https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens
@ -230,14 +287,23 @@ These endpoints implement OpenID Connect elements.
[OpenID Connect Discovery]: https://openid.net/specs/openid-connect-discovery-1_0.html [OpenID Connect Discovery]: https://openid.net/specs/openid-connect-discovery-1_0.html
[OAuth 2.0 Authorization Server Metadata]: https://datatracker.ietf.org/doc/html/rfc8414 [OAuth 2.0 Authorization Server Metadata]: https://datatracker.ietf.org/doc/html/rfc8414
[JSON Web Key Sets]: https://datatracker.ietf.org/doc/html/rfc7517#section-5 [JSON Web Key Set]: https://datatracker.ietf.org/doc/html/rfc7517#section-5
[Authorization]: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint [Authorization]: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126
[Token]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint [Token]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
[UserInfo]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo [UserInfo]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
[Introspection]: https://datatracker.ietf.org/doc/html/rfc7662 [Introspection]: https://datatracker.ietf.org/doc/html/rfc7662
[Revocation]: https://datatracker.ietf.org/doc/html/rfc7009 [Revocation]: https://datatracker.ietf.org/doc/html/rfc7009
[Proof Key Code Exchange]: https://www.rfc-editor.org/rfc/rfc7636.html
[RFC8176]: https://datatracker.ietf.org/doc/html/rfc8176
[RFC4122]: https://datatracker.ietf.org/doc/html/rfc4122
[Subject Identifier Types]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes [Subject Identifier Types]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
[Client Authentication]: https://datatracker.ietf.org/doc/html/rfc6749#section-2.3
[Client Type]: https://oauth.net/2/client-types/
[HTTP POST method]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST
[Proof Key Code Exchange]: #proof-key-code-exchange
[RFC4122]: https://datatracker.ietf.org/doc/html/rfc4122
[RFC7636]: https://datatracker.ietf.org/doc/html/rfc7636
[RFC8176]: https://datatracker.ietf.org/doc/html/rfc8176
[RFC9126]: https://datatracker.ietf.org/doc/html/rfc9126

View File

@ -53,3 +53,20 @@ deprecated__* and we instead recommended that users remove this from their confi
Plaintext is either denoted by the `$plaintext$` prefix where everything after the prefix is the secret. In addition if Plaintext is either denoted by the `$plaintext$` prefix where everything after the prefix is the secret. In addition if
the secret does not start with the `$` character it's considered as a plaintext secret for the time being but is the secret does not start with the `$` character it's considered as a plaintext secret for the time being but is
deprecated as is the `$plaintext$` prefix. deprecated as is the `$plaintext$` prefix.
## Frequently Asked Questions
### Why isn't my application able to retrieve the token even though I've consented?
The most common cause for this issue is when the affected application can not make requests to the Token [Endpoint].
This becomes obvious when the log level is set to `debug` or `trace` and a presence of requests to the Authorization
[Endpoint] without errors but an absence of requests made to the Token [Endpoint].
These requests can be identified by looking at the `path` field in the logs, or by messages prefixed with
`Authorization Request` indicating a request to the Authorization [Endpoint] and `Access Request` indicating a request
to the Token [Endpoint].
All causes should be clearly logged by the client application, and all errors that do not match this scenario are
clearly logged by Authelia. It's not possible for us to log requests that never occur however.
[Endpoint]: ./introduction.md#discoverable-endpoints

View File

@ -383,7 +383,7 @@ proxy_set_header X-Forwarded-For $remote_addr;
set $upstream_authelia http://authelia:9091/api/authz/auth-request; set $upstream_authelia http://authelia:9091/api/authz/auth-request;
## Virtual endpoint created by nginx to forward auth requests. ## Virtual endpoint created by nginx to forward auth requests.
location /authelia { location /internal/authelia/authz {
## Essential Proxy Configuration ## Essential Proxy Configuration
internal; internal;
proxy_pass $upstream_authelia; proxy_pass $upstream_authelia;
@ -423,7 +423,7 @@ and is paired with [authelia-location.conf](#authelia-locationconf).*
{{< details "/config/nginx/snippets/authelia-authrequest.conf" >}} {{< details "/config/nginx/snippets/authelia-authrequest.conf" >}}
```nginx ```nginx
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource. ## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
auth_request /authelia; auth_request /internal/authelia/authz;
## Set the $target_url variable based on the original request. ## Set the $target_url variable based on the original request.
@ -478,7 +478,7 @@ implementation `AuthRequest` which contains the `HeaderAuthorization` and `Heade
set $upstream_authelia http://authelia:9091/api/authz/auth-request/basic; set $upstream_authelia http://authelia:9091/api/authz/auth-request/basic;
# Virtual endpoint created by nginx to forward auth requests. # Virtual endpoint created by nginx to forward auth requests.
location /authelia-basic { location /internal/authelia/authz/basic {
## Essential Proxy Configuration ## Essential Proxy Configuration
internal; internal;
proxy_pass $upstream_authelia; proxy_pass $upstream_authelia;
@ -526,7 +526,7 @@ endpoint. It's recommended to use [authelia-authrequest.conf](#authelia-authrequ
{{< details "/config/nginx/snippets/authelia-authrequest-basic.conf" >}} {{< details "/config/nginx/snippets/authelia-authrequest-basic.conf" >}}
```nginx ```nginx
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource. ## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
auth_request /authelia-basic; auth_request /internal/authelia/authz/basic;
## Comment this line if you're using nginx without the http_set_misc module. ## Comment this line if you're using nginx without the http_set_misc module.
set_escape_uri $target_url $scheme://$http_host$request_uri; set_escape_uri $target_url $scheme://$http_host$request_uri;
@ -570,7 +570,7 @@ if ($request_uri = "/force-basic") {
} }
## A new virtual endpoint to used if the auth_request failed ## A new virtual endpoint to used if the auth_request failed
location /authelia-detect { location /internal/authelia/authz/detect {
internal; internal;
if ($is_basic_auth) { if ($is_basic_auth) {
@ -598,7 +598,7 @@ endpoint. It's recommended to use [authelia-authrequest.conf](#authelia-authrequ
{{< details "/config/nginx/snippets/authelia-authrequest-detect.conf" >}} {{< details "/config/nginx/snippets/authelia-authrequest-detect.conf" >}}
```nginx ```nginx
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource. ## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
auth_request /authelia; auth_request /internal/authelia/authz;
## Comment this line if you're using nginx without the http_set_misc module. ## Comment this line if you're using nginx without the http_set_misc module.
set_escape_uri $target_url $scheme://$http_host$request_uri; set_escape_uri $target_url $scheme://$http_host$request_uri;
@ -619,7 +619,7 @@ proxy_set_header Remote-Name $name;
proxy_set_header Remote-Email $email; proxy_set_header Remote-Email $email;
## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal. ## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal.
error_page 401 =302 /authelia-detect?rd=$target_url; error_page 401 =302 /internal/authelia/authz/detect?rd=$target_url;
``` ```
{{< /details >}} {{< /details >}}

View File

@ -16,16 +16,16 @@ aliases:
--- ---
| Proxy | [Implementation] | [Standard](#standard) | [Kubernetes](#kubernetes) | [XHR Redirect](#xhr-redirect) | [Request Method](#request-method) | | Proxy | [Implementation] | [Standard](#standard) | [Kubernetes](#kubernetes) | [XHR Redirect](#xhr-redirect) | [Request Method](#request-method) |
|:---------------------:|:----------------:|:------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:|:---------------------------------:|:---------------------------------:| |:---------------------------------------:|:----------------:|:---------------------------------------------------:|:-------------------------------------------------------------------------------------:|:---------------------------------:|:---------------------------------:|
| [Traefik] | [ForwardAuth] | {{% support support="full" link="traefik.md" %}} | {{% support support="full" link="../../integration/kubernetes/traefik-ingress.md" %}} | {{% support support="full" %}} | {{% support support="full" %}} | | [Traefik] ([guide](/i/traefik)) | [ForwardAuth] | {{% support support="full" link="/i/traefik" %}} | {{% support support="full" link="../../integration/kubernetes/traefik-ingress.md" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
| [Caddy] | [ForwardAuth] | {{% support support="full" link="caddy.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} | | [Caddy] ([guide](/i/caddy)) | [ForwardAuth] | {{% support support="full" link="/i/caddy" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
| [Envoy] | [ExtAuthz] | {{% support support="full" link="envoy.md" %}} | {{% support support="full" link="../../integration/kubernetes/istio.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | | [Envoy] ([guide](/i/envoy)) | [ExtAuthz] | {{% support support="full" link="/i/envoy" %}} | {{% support support="full" link="../../integration/kubernetes/istio.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
| [NGINX] | [AuthRequest] | {{% support support="full" link="nginx.md" %}} | {{% support support="full" link="../../integration/kubernetes/nginx-ingress.md" %}} | {{% support %}} | {{% support support="full" %}} | | [NGINX] ([guide](/i/nginx)) | [AuthRequest] | {{% support support="full" link="/i/nginx" %}} | {{% support support="full" link="../../integration/kubernetes/nginx-ingress.md" %}} | {{% support %}} | {{% support support="full" %}} |
| [NGINX Proxy Manager] | [AuthRequest] | {{% support support="full" link="nginx-proxy-manager/index.md" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} | | [NGINX Proxy Manager] ([guide](/i/npm)) | [AuthRequest] | {{% support support="full" link="/i/npm" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
| [SWAG] | [AuthRequest] | {{% support support="full" link="swag.md" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} | | [SWAG] ([guide](/i/swag)) | [AuthRequest] | {{% support support="full" link="/i/swag" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
| [HAProxy] | [AuthRequest] | {{% support support="full" link="haproxy.md" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | | [HAProxy] ([guide](/i/haproxy)) | [AuthRequest] | {{% support support="full" link="/i/haproxy" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
| [Skipper] | [ForwardAuth] | {{% support support="full" link="skipper.md" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | | [Skipper] ([guide](/i/skipper)) | [ForwardAuth] | {{% support support="full" link="/i/skipper" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} |
| [Traefik] 1.x | [ForwardAuth] | {{% support support="full" link="traefikv1.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} | | [Traefik] 1.x ([guide](/i/traefik/v1)) | [ForwardAuth] | {{% support support="full" link="/i/traefik/v1" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
| [Apache] | N/A | {{% support link="#apache" %}} | {{% support %}} | {{% support %}} | {{% support %}} | | [Apache] | N/A | {{% support link="#apache" %}} | {{% support %}} | {{% support %}} | {{% support %}} |
| [IIS] | N/A | {{% support link="#iis" %}} | {{% support %}} | {{% support %}} | {{% support %}} | | [IIS] | N/A | {{% support link="#iis" %}} | {{% support %}} | {{% support %}} | {{% support %}} |

View File

@ -57,7 +57,7 @@ In addition this represents a bad user experience in some instances such as:
- Users sometimes visit the `https://app.example.com/authelia` URL which doesn't automatically redirect the user to - Users sometimes visit the `https://app.example.com/authelia` URL which doesn't automatically redirect the user to
`https://app.example.com` (if they visit `https://app.example.com` then they'll be redirected to authenticate then `https://app.example.com` (if they visit `https://app.example.com` then they'll be redirected to authenticate then
redirected back to their original URL). redirected back to their original URL)
- Administrators may wish to setup [OpenID Connect 1.0](../../configuration/identity-providers/open-id-connect.md) in - Administrators may wish to setup [OpenID Connect 1.0](../../configuration/identity-providers/open-id-connect.md) in
which case it also doesn't represent a good user experience as the `issuer` will be which case it also doesn't represent a good user experience as the `issuer` will be
`https://app.example.com/authelia` for example `https://app.example.com/authelia` for example
@ -147,8 +147,8 @@ services:
- '443:443' - '443:443'
volumes: volumes:
- ${PWD}/data/swag:/config - ${PWD}/data/swag:/config
## Uncomment the line below if you want to use the Authelia configuration snippets.
#- ${PWD}/data/nginx/snippets:/snippets:ro #- ${PWD}/data/nginx/snippets:/snippets:ro
## Uncomment the above line if you want to use the Authelia configuration snippets.
environment: environment:
PUID: '1000' PUID: '1000'
PGID: '1000' PGID: '1000'

View File

@ -11,6 +11,7 @@ menu:
weight: 371 weight: 371
toc: true toc: true
aliases: aliases:
- /i/traefik/v1
- /docs/deployment/supported-proxies/traefik1.x.html - /docs/deployment/supported-proxies/traefik1.x.html
--- ---

View File

@ -15,16 +15,16 @@ toc: false
The following table is a support matrix for Authelia features and specific reverse proxies. The following table is a support matrix for Authelia features and specific reverse proxies.
| Proxy | Standard | Kubernetes | XHR Redirect | Request Method | | Proxy | Standard | Kubernetes | XHR Redirect | Request Method |
|:---------------------:|:--------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:|:---------------------------------:|:---------------------------------:| |:---------------------------------------:|:-------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:|:---------------------------------:|:---------------------------------:|
| [Traefik] | {{% support support="full" link="../../integration/proxies/traefik.md" %}} | {{% support support="full" link="../../integration/kubernetes/traefik-ingress.md" %}} | {{% support support="full" %}} | {{% support support="full" %}} | | [Traefik] ([guide](/i/traefik)) | {{% support support="full" link="/i/traefik" %}} | {{% support support="full" link="../../integration/kubernetes/traefik-ingress.md" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
| [Caddy] | {{% support support="full" link="../../integration/proxies/caddy.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} | | [Caddy] ([guide](/i/caddy)) | {{% support support="full" link="/i/caddy" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
| [Envoy] | {{% support support="full" link="../../integration/proxies/envoy.md" %}} | {{% support support="full" link="../../integration/kubernetes/istio.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | | [Envoy] ([guide](/i/envoy)) | {{% support support="full" link="/i/envoy" %}} | {{% support support="full" link="../../integration/kubernetes/istio.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
| [NGINX] | {{% support support="full" link="../../integration/proxies/nginx.md" %}} | {{% support support="full" link="../../integration/kubernetes/nginx-ingress.md" %}} | {{% support %}} | {{% support support="full" %}} | | [NGINX] ([guide](/i/nginx)) | {{% support support="full" link="/i/nginx" %}} | {{% support support="full" link="../../integration/kubernetes/nginx-ingress.md" %}} | {{% support %}} | {{% support support="full" %}} |
| [NGINX Proxy Manager] | {{% support support="full" link="../../integration/proxies/nginx-proxy-manager/index.md" %}} | {{% support %}} | {{% support %}} | {{% support support="full" %}} | | [NGINX Proxy Manager] ([guide](/i/npm)) | {{% support support="full" link="/i/npm" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
| [SWAG] | {{% support support="full" link="../../integration/proxies/swag.md" %}} | {{% support %}} | {{% support %}} | {{% support support="full" %}} | | [SWAG] ([guide](/i/swag)) | {{% support support="full" link="/i/swag" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
| [HAProxy] | {{% support support="full" link="../../integration/proxies/haproxy.md" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | | [HAProxy] ([guide](/i/haproxy)) | {{% support support="full" link="/i/haproxy" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
| [Traefik] 1.x | {{% support support="full" link="../../integration/proxies/traefikv1.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} | | [Skipper] ([guide](/i/skipper)) | {{% support support="full" link="/i/skipper" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} |
| [Skipper] | {{% support support="full" link="../../integration/proxies/skipper.md" %}} | {{% support %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | | [Traefik] 1.x ([guide](/i/traefik/v1)) | {{% support support="full" link="/i/traefik/v1" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
| [Apache] | {{% support %}} | {{% support %}} | {{% support %}} | {{% support %}} | | [Apache] | {{% support %}} | {{% support %}} | {{% support %}} | {{% support %}} |
| [IIS] | {{% support %}} | {{% support %}} | {{% support %}} | {{% support %}} | | [IIS] | {{% support %}} | {{% support %}} | {{% support %}} | {{% support %}} |

View File

@ -0,0 +1,17 @@
---
title: "Frequently Asked Questions"
description: "This guide shows a list of other frequently asked question documents as well as some general ones"
lead: "This guide shows a list of other frequently asked question documents as well as some general ones."
date: 2023-03-06T11:04:10+11:00
draft: false
images: []
menu:
reference:
parent: "guides"
weight: 220
toc: true
---
## Identity Providers
- [OpenID Connect 1.0 Integration](../../integration/openid-connect/specific-information.md#frequently-asked-questions)

File diff suppressed because one or more lines are too long

20
go.mod
View File

@ -5,9 +5,9 @@ go 1.20
require ( require (
github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4 github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/deckarep/golang-set/v2 v2.1.0 github.com/deckarep/golang-set/v2 v2.2.0
github.com/duosecurity/duo_api_golang v0.0.0-20230203160531-b221c950c2b0 github.com/duosecurity/duo_api_golang v0.0.0-20230203160531-b221c950c2b0
github.com/fasthttp/router v1.4.16 github.com/fasthttp/router v1.4.17
github.com/fasthttp/session/v2 v2.4.16 github.com/fasthttp/session/v2 v2.4.16
github.com/fsnotify/fsnotify v1.6.0 github.com/fsnotify/fsnotify v1.6.0
github.com/go-asn1-ber/asn1-ber v1.5.4 github.com/go-asn1-ber/asn1-ber v1.5.4
@ -33,7 +33,7 @@ require (
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/ory/fosite v0.44.0 github.com/ory/fosite v0.44.0
github.com/ory/herodot v0.9.13 github.com/ory/herodot v0.9.13
github.com/ory/x v0.0.541 github.com/ory/x v0.0.543
github.com/otiai10/copy v1.9.0 github.com/otiai10/copy v1.9.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0 github.com/pquerna/otp v1.4.0
@ -45,10 +45,10 @@ require (
github.com/trustelem/zxcvbn v1.0.1 github.com/trustelem/zxcvbn v1.0.1
github.com/valyala/fasthttp v1.44.0 github.com/valyala/fasthttp v1.44.0
github.com/wneessen/go-mail v0.3.8 github.com/wneessen/go-mail v0.3.8
golang.org/x/net v0.7.0 golang.org/x/net v0.8.0
golang.org/x/sync v0.1.0 golang.org/x/sync v0.1.0
golang.org/x/term v0.5.0 golang.org/x/term v0.6.0
golang.org/x/text v0.7.0 golang.org/x/text v0.8.0
gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
@ -101,7 +101,7 @@ require (
github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/spf13/afero v1.9.3 // indirect github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
@ -115,10 +115,10 @@ require (
github.com/ysmood/gson v0.7.3 // indirect github.com/ysmood/gson v0.7.3 // indirect
github.com/ysmood/leakless v0.8.0 // indirect github.com/ysmood/leakless v0.8.0 // indirect
golang.org/x/crypto v0.6.0 // indirect golang.org/x/crypto v0.6.0 // indirect
golang.org/x/mod v0.7.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
golang.org/x/sys v0.5.0 // indirect golang.org/x/sys v0.6.0 // indirect
golang.org/x/tools v0.4.0 // indirect golang.org/x/tools v0.6.0 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect
google.golang.org/grpc v1.50.1 // indirect google.golang.org/grpc v1.50.1 // indirect

37
go.sum
View File

@ -96,8 +96,8 @@ github.com/dave/jennifer v1.6.0/go.mod h1:AxTG893FiZKqxy3FP1kL80VMshSMuz2G+Egvsz
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= github.com/deckarep/golang-set/v2 v2.2.0 h1:2pMQd3Soi6qfw7E5MMKaEh5W5ES18bW3AbFFnGl6LgQ=
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/deckarep/golang-set/v2 v2.2.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE=
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
@ -126,8 +126,8 @@ 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/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 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
github.com/fasthttp/router v1.4.16 h1:faWJ9OtaHvAtodreyQLps58M80YFNzphMJtOJzeESXs= github.com/fasthttp/router v1.4.17 h1:Z8fndZotdwcPoYTt8BWwnRBts2UQPnKmOxbb94n0GUc=
github.com/fasthttp/router v1.4.16/go.mod h1:NFNlTCilbRVkeLc+E5JDkcxUdkpiJGKDL8Zy7Ey2JTI= github.com/fasthttp/router v1.4.17/go.mod h1:EOMfK/dT1IMzbyPhzw6E2j90owHvY+/BY60bLxOye/8=
github.com/fasthttp/session/v2 v2.4.16 h1:JRvuEqr/+/cNMBkhGZN118FurLh6paUGscwJr26TxAQ= 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/fasthttp/session/v2 v2.4.16/go.mod h1:nv8SD6pAx3n3KjJsEt4k1p0vstqclbNcrCwjc1OjuCI=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
@ -387,8 +387,8 @@ github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8
github.com/ory/herodot v0.9.13 h1:cN/Z4eOkErl/9W7hDIDLb79IO/bfsH+8yscBjRpB4IU= 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/herodot v0.9.13/go.mod h1:IWDs9kSvFQqw/cQ8zi5ksyYvITiUU4dI7glUrhZcJYo=
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM= github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
github.com/ory/x v0.0.541 h1:rp8AD7X5/WiZIJws5kBwFgXoSeunxNMD54QV58+pcew= github.com/ory/x v0.0.543 h1:I6bl6IV2Ok07io6M2dnaRaJHP5oRU096T9FYoe8m48U=
github.com/ory/x v0.0.541/go.mod h1:ktXUvx51Ok1gMGr3ysvktanqr+eiB4FXglt4nF4w2Uo= github.com/ory/x v0.0.543/go.mod h1:ktXUvx51Ok1gMGr3ysvktanqr+eiB4FXglt4nF4w2Uo=
github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4= 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/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
@ -457,8 +457,9 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
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/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@ -607,8 +608,9 @@ 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.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 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/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.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-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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -656,8 +658,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 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.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.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -750,13 +752,13 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220908164124-27713097b956/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.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.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.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-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.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.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -767,8 +769,8 @@ 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.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.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.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -826,8 +828,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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -29,10 +29,17 @@ type OpenIDConnectConfiguration struct {
EnablePKCEPlainChallenge bool `koanf:"enable_pkce_plain_challenge"` EnablePKCEPlainChallenge bool `koanf:"enable_pkce_plain_challenge"`
CORS OpenIDConnectCORSConfiguration `koanf:"cors"` CORS OpenIDConnectCORSConfiguration `koanf:"cors"`
PAR OpenIDConnectPARConfiguration `koanf:"pushed_authorizations"`
Clients []OpenIDConnectClientConfiguration `koanf:"clients"` Clients []OpenIDConnectClientConfiguration `koanf:"clients"`
} }
// OpenIDConnectPARConfiguration represents an OpenID Connect PAR config.
type OpenIDConnectPARConfiguration struct {
Enforce bool `koanf:"enforce"`
ContextLifespan time.Duration `koanf:"context_lifespan"`
}
// OpenIDConnectCORSConfiguration represents an OpenID Connect CORS config. // OpenIDConnectCORSConfiguration represents an OpenID Connect CORS config.
type OpenIDConnectCORSConfiguration struct { type OpenIDConnectCORSConfiguration struct {
Endpoints []string `koanf:"endpoints"` Endpoints []string `koanf:"endpoints"`
@ -59,6 +66,7 @@ type OpenIDConnectClientConfiguration struct {
Policy string `koanf:"authorization_policy"` Policy string `koanf:"authorization_policy"`
EnforcePAR bool `koanf:"enforce_par"`
EnforcePKCE bool `koanf:"enforce_pkce"` EnforcePKCE bool `koanf:"enforce_pkce"`
PKCEChallengeMethod string `koanf:"pkce_challenge_method"` PKCEChallengeMethod string `koanf:"pkce_challenge_method"`

View File

@ -31,6 +31,8 @@ var Keys = []string{
"identity_providers.oidc.cors.endpoints", "identity_providers.oidc.cors.endpoints",
"identity_providers.oidc.cors.allowed_origins", "identity_providers.oidc.cors.allowed_origins",
"identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris", "identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris",
"identity_providers.oidc.pushed_authorizations.enforce",
"identity_providers.oidc.pushed_authorizations.context_lifespan",
"identity_providers.oidc.clients", "identity_providers.oidc.clients",
"identity_providers.oidc.clients[].id", "identity_providers.oidc.clients[].id",
"identity_providers.oidc.clients[].description", "identity_providers.oidc.clients[].description",
@ -44,6 +46,7 @@ var Keys = []string{
"identity_providers.oidc.clients[].response_types", "identity_providers.oidc.clients[].response_types",
"identity_providers.oidc.clients[].response_modes", "identity_providers.oidc.clients[].response_modes",
"identity_providers.oidc.clients[].authorization_policy", "identity_providers.oidc.clients[].authorization_policy",
"identity_providers.oidc.clients[].enforce_par",
"identity_providers.oidc.clients[].enforce_pkce", "identity_providers.oidc.clients[].enforce_pkce",
"identity_providers.oidc.clients[].pkce_challenge_method", "identity_providers.oidc.clients[].pkce_challenge_method",
"identity_providers.oidc.clients[].userinfo_signing_algorithm", "identity_providers.oidc.clients[].userinfo_signing_algorithm",

View File

@ -392,7 +392,7 @@ var (
validOIDCGrantTypes = []string{oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeAuthorizationCode, oidc.GrantTypePassword, oidc.GrantTypeClientCredentials} validOIDCGrantTypes = []string{oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeAuthorizationCode, oidc.GrantTypePassword, oidc.GrantTypeClientCredentials}
validOIDCResponseModes = []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery, oidc.ResponseModeFragment} validOIDCResponseModes = []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery, oidc.ResponseModeFragment}
validOIDCUserinfoAlgorithms = []string{oidc.SigningAlgorithmNone, oidc.SigningAlgorithmRSAWithSHA256} validOIDCUserinfoAlgorithms = []string{oidc.SigningAlgorithmNone, oidc.SigningAlgorithmRSAWithSHA256}
validOIDCCORSEndpoints = []string{oidc.EndpointAuthorization, oidc.EndpointToken, oidc.EndpointIntrospection, oidc.EndpointRevocation, oidc.EndpointUserinfo} validOIDCCORSEndpoints = []string{oidc.EndpointAuthorization, oidc.EndpointPushedAuthorizationRequest, oidc.EndpointToken, oidc.EndpointIntrospection, oidc.EndpointRevocation, oidc.EndpointUserinfo}
validOIDCClientConsentModes = []string{"auto", oidc.ClientConsentModeImplicit.String(), oidc.ClientConsentModeExplicit.String(), oidc.ClientConsentModePreConfigured.String()} validOIDCClientConsentModes = []string{"auto", oidc.ClientConsentModeImplicit.String(), oidc.ClientConsentModeExplicit.String(), oidc.ClientConsentModePreConfigured.String()}
) )

View File

@ -80,7 +80,7 @@ func TestShouldRaiseErrorWhenCORSEndpointsNotValid(t *testing.T) {
require.Len(t, validator.Errors(), 1) require.Len(t, validator.Errors(), 1)
assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: cors: option 'endpoints' contains an invalid value 'invalid_endpoint': must be one of 'authorization', 'token', 'introspection', 'revocation', 'userinfo'") assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: cors: option 'endpoints' contains an invalid value 'invalid_endpoint': must be one of 'authorization', 'pushed-authorization-request', 'token', 'introspection', 'revocation', 'userinfo'")
} }
func TestShouldRaiseErrorWhenOIDCPKCEEnforceValueInvalid(t *testing.T) { func TestShouldRaiseErrorWhenOIDCPKCEEnforceValueInvalid(t *testing.T) {

View File

@ -53,10 +53,20 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
return return
} }
if err = client.ValidateAuthorizationPolicy(requester); err != nil { if err = client.ValidatePARPolicy(requester, ctx.Providers.OpenIDConnect.GetPushedAuthorizeRequestURIPrefix(ctx)); err != nil {
rfc := fosite.ErrorToRFC6749Error(err) rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' failed to validate the authorization policy: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription()) ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' failed to validate the PAR policy: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, err)
return
}
if err = client.ValidatePKCEPolicy(requester); err != nil {
rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' failed to validate the PKCE policy: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, err) ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, err)
@ -95,13 +105,13 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' was successfully processed, proceeding to build Authorization Response", requester.GetID(), clientID) ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' was successfully processed, proceeding to build Authorization Response", requester.GetID(), clientID)
oidcSession := oidc.NewSessionWithAuthorizeRequest(issuer, ctx.Providers.OpenIDConnect.KeyManager.GetActiveKeyID(), session := oidc.NewSessionWithAuthorizeRequest(issuer, ctx.Providers.OpenIDConnect.KeyManager.GetActiveKeyID(),
userSession.Username, userSession.AuthenticationMethodRefs.MarshalRFC8176(), extraClaims, authTime, consent, requester) userSession.Username, userSession.AuthenticationMethodRefs.MarshalRFC8176(), extraClaims, authTime, consent, requester)
ctx.Logger.Tracef("Authorization Request with id '%s' on client with id '%s' creating session for Authorization Response for subject '%s' with username '%s' with claims: %+v", ctx.Logger.Tracef("Authorization Request with id '%s' on client with id '%s' creating session for Authorization Response for subject '%s' with username '%s' with claims: %+v",
requester.GetID(), oidcSession.ClientID, oidcSession.Subject, oidcSession.Username, oidcSession.Claims) requester.GetID(), session.ClientID, session.Subject, session.Username, session.Claims)
if responder, err = ctx.Providers.OpenIDConnect.NewAuthorizeResponse(ctx, requester, oidcSession); err != nil { if responder, err = ctx.Providers.OpenIDConnect.NewAuthorizeResponse(ctx, requester, session); err != nil {
rfc := fosite.ErrorToRFC6749Error(err) rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Authorization Response for Request with id '%s' on client with id '%s' could not be created: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription()) ctx.Logger.Errorf("Authorization Response for Request with id '%s' on client with id '%s' could not be created: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
@ -125,3 +135,62 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
ctx.Providers.OpenIDConnect.WriteAuthorizeResponse(ctx, rw, requester, responder) ctx.Providers.OpenIDConnect.WriteAuthorizeResponse(ctx, rw, requester, responder)
} }
// OpenIDConnectPushedAuthorizationRequest handles POST requests to the OAuth 2.0 Pushed Authorization Requests endpoint.
//
// RFC9126 https://www.rfc-editor.org/rfc/rfc9126.html
func OpenIDConnectPushedAuthorizationRequest(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, r *http.Request) {
var (
requester fosite.AuthorizeRequester
responder fosite.PushedAuthorizeResponder
err error
)
if requester, err = ctx.Providers.OpenIDConnect.NewPushedAuthorizeRequest(ctx, r); err != nil {
rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Pushed Authorization Request failed with error: %s", rfc.WithExposeDebug(true).GetDescription())
ctx.Providers.OpenIDConnect.WritePushedAuthorizeError(ctx, rw, requester, err)
return
}
var client *oidc.Client
clientID := requester.GetClient().GetID()
if client, err = ctx.Providers.OpenIDConnect.GetFullClient(clientID); err != nil {
if errors.Is(err, fosite.ErrNotFound) {
ctx.Logger.Errorf("Pushed Authorization Request with id '%s' on client with id '%s' could not be processed: client was not found", requester.GetID(), clientID)
} else {
ctx.Logger.Errorf("Pushed Authorization Request with id '%s' on client with id '%s' could not be processed: failed to find client: %+v", requester.GetID(), clientID, err)
}
ctx.Providers.OpenIDConnect.WritePushedAuthorizeError(ctx, rw, requester, err)
return
}
if err = client.ValidatePKCEPolicy(requester); err != nil {
rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Pushed Authorization Request with id '%s' on client with id '%s' failed to validate the PKCE policy: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
ctx.Providers.OpenIDConnect.WritePushedAuthorizeError(ctx, rw, requester, err)
return
}
if responder, err = ctx.Providers.OpenIDConnect.NewPushedAuthorizeResponse(ctx, requester, oidc.NewSession()); err != nil {
rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Pushed Authorization Request failed with error: %s", rfc.WithExposeDebug(true).GetDescription())
ctx.Providers.OpenIDConnect.WritePushedAuthorizeError(ctx, rw, requester, err)
return
}
ctx.Providers.OpenIDConnect.WritePushedAuthorizeResponse(ctx, rw, requester, responder)
}

View File

@ -9,9 +9,8 @@ import (
mail "net/mail" mail "net/mail"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock"
templates "github.com/authelia/authelia/v4/internal/templates" templates "github.com/authelia/authelia/v4/internal/templates"
gomock "github.com/golang/mock/gomock"
) )
// MockNotifier is a mock of Notifier interface. // MockNotifier is a mock of Notifier interface.

View File

@ -39,6 +39,7 @@ func (m *MockStorage) EXPECT() *MockStorageMockRecorder {
return m.recorder return m.recorder
} }
// AppendAuthenticationLog mocks base method. // AppendAuthenticationLog mocks base method.
func (m *MockStorage) AppendAuthenticationLog(arg0 context.Context, arg1 model.AuthenticationAttempt) error { func (m *MockStorage) AppendAuthenticationLog(arg0 context.Context, arg1 model.AuthenticationAttempt) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@ -269,6 +270,21 @@ func (mr *MockStorageMockRecorder) LoadOAuth2ConsentSessionByChallengeID(arg0, a
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOAuth2ConsentSessionByChallengeID", reflect.TypeOf((*MockStorage)(nil).LoadOAuth2ConsentSessionByChallengeID), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOAuth2ConsentSessionByChallengeID", reflect.TypeOf((*MockStorage)(nil).LoadOAuth2ConsentSessionByChallengeID), arg0, arg1)
} }
// LoadOAuth2PARContext mocks base method.
func (m *MockStorage) LoadOAuth2PARContext(arg0 context.Context, arg1 string) (*model.OAuth2PARContext, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LoadOAuth2PARContext", arg0, arg1)
ret0, _ := ret[0].(*model.OAuth2PARContext)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// LoadOAuth2PARContext indicates an expected call of LoadOAuth2PARContext.
func (mr *MockStorageMockRecorder) LoadOAuth2PARContext(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOAuth2PARContext", reflect.TypeOf((*MockStorage)(nil).LoadOAuth2PARContext), arg0, arg1)
}
// LoadOAuth2Session mocks base method. // LoadOAuth2Session mocks base method.
func (m *MockStorage) LoadOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 string) (*model.OAuth2Session, error) { func (m *MockStorage) LoadOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 string) (*model.OAuth2Session, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@ -464,6 +480,20 @@ func (mr *MockStorageMockRecorder) LoadWebauthnUser(arg0, arg1, arg2 interface{}
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebauthnUser", reflect.TypeOf((*MockStorage)(nil).LoadWebauthnUser), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebauthnUser", reflect.TypeOf((*MockStorage)(nil).LoadWebauthnUser), arg0, arg1, arg2)
} }
// RevokeOAuth2PARContext mocks base method.
func (m *MockStorage) RevokeOAuth2PARContext(arg0 context.Context, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RevokeOAuth2PARContext", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// RevokeOAuth2PARContext indicates an expected call of RevokeOAuth2PARContext.
func (mr *MockStorageMockRecorder) RevokeOAuth2PARContext(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeOAuth2PARContext", reflect.TypeOf((*MockStorage)(nil).RevokeOAuth2PARContext), arg0, arg1)
}
// RevokeOAuth2Session mocks base method. // RevokeOAuth2Session mocks base method.
func (m *MockStorage) RevokeOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 string) error { func (m *MockStorage) RevokeOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@ -605,6 +635,20 @@ func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSessionSubject(arg0, arg1 in
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentSessionSubject", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentSessionSubject), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentSessionSubject", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentSessionSubject), arg0, arg1)
} }
// SaveOAuth2PARContext mocks base method.
func (m *MockStorage) SaveOAuth2PARContext(arg0 context.Context, arg1 model.OAuth2PARContext) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SaveOAuth2PARContext", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// SaveOAuth2PARContext indicates an expected call of SaveOAuth2PARContext.
func (mr *MockStorageMockRecorder) SaveOAuth2PARContext(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2PARContext", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2PARContext), arg0, arg1)
}
// SaveOAuth2Session mocks base method. // SaveOAuth2Session mocks base method.
func (m *MockStorage) SaveOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 model.OAuth2Session) error { func (m *MockStorage) SaveOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 model.OAuth2Session) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -7,9 +7,8 @@ package mocks
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock"
model "github.com/authelia/authelia/v4/internal/model" model "github.com/authelia/authelia/v4/internal/model"
gomock "github.com/golang/mock/gomock"
) )
// MockTOTP is a mock of Provider interface. // MockTOTP is a mock of Provider interface.

View File

@ -7,9 +7,8 @@ package mocks
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock"
authentication "github.com/authelia/authelia/v4/internal/authentication" authentication "github.com/authelia/authelia/v4/internal/authentication"
gomock "github.com/golang/mock/gomock"
) )
// MockUserProvider is a mock of UserProvider interface. // MockUserProvider is a mock of UserProvider interface.

View File

@ -39,6 +39,14 @@ func NewOAuth2ConsentSession(subject uuid.UUID, r fosite.Requester) (consent *OA
return consent, nil return consent, nil
} }
// NewOAuth2BlacklistedJTI creates a new OAuth2BlacklistedJTI.
func NewOAuth2BlacklistedJTI(jti string, exp time.Time) (jtiBlacklist OAuth2BlacklistedJTI) {
return OAuth2BlacklistedJTI{
Signature: fmt.Sprintf("%x", sha256.Sum256([]byte(jti))),
ExpiresAt: exp,
}
}
// NewOAuth2SessionFromRequest creates a new OAuth2Session from a signature and fosite.Requester. // NewOAuth2SessionFromRequest creates a new OAuth2Session from a signature and fosite.Requester.
func NewOAuth2SessionFromRequest(signature string, r fosite.Requester) (session *OAuth2Session, err error) { func NewOAuth2SessionFromRequest(signature string, r fosite.Requester) (session *OAuth2Session, err error) {
var ( var (
@ -77,12 +85,43 @@ func NewOAuth2SessionFromRequest(signature string, r fosite.Requester) (session
}, nil }, nil
} }
// NewOAuth2BlacklistedJTI creates a new OAuth2BlacklistedJTI. // NewOAuth2PARContext creates a new Pushed Authorization Request Context as a OAuth2PARContext.
func NewOAuth2BlacklistedJTI(jti string, exp time.Time) (jtiBlacklist OAuth2BlacklistedJTI) { func NewOAuth2PARContext(contextID string, r fosite.AuthorizeRequester) (context *OAuth2PARContext, err error) {
return OAuth2BlacklistedJTI{ var (
Signature: fmt.Sprintf("%x", sha256.Sum256([]byte(jti))), s *OpenIDSession
ExpiresAt: exp, ok bool
req *fosite.AuthorizeRequest
session []byte
)
if s, ok = r.GetSession().(*OpenIDSession); !ok {
return nil, fmt.Errorf("can't convert type '%T' to an *OAuth2Session", r.GetSession())
} }
if session, err = json.Marshal(s); err != nil {
return nil, err
}
var handled StringSlicePipeDelimited
if req, ok = r.(*fosite.AuthorizeRequest); ok {
handled = StringSlicePipeDelimited(req.HandledResponseTypes)
}
return &OAuth2PARContext{
Signature: contextID,
RequestID: r.GetID(),
ClientID: r.GetClient().GetID(),
RequestedAt: r.GetRequestedAt(),
Scopes: StringSlicePipeDelimited(r.GetRequestedScopes()),
Audience: StringSlicePipeDelimited(r.GetRequestedAudience()),
HandledResponseTypes: handled,
ResponseMode: string(r.GetResponseMode()),
DefaultResponseMode: string(r.GetDefaultResponseMode()),
Revoked: false,
Form: r.GetRequestForm().Encode(),
Session: session,
}, nil
} }
// OAuth2ConsentPreConfig stores information about an OAuth2.0 Pre-Configured Consent. // OAuth2ConsentPreConfig stores information about an OAuth2.0 Pre-Configured Consent.
@ -264,6 +303,70 @@ func (s *OAuth2Session) ToRequest(ctx context.Context, session fosite.Session, s
}, nil }, nil
} }
// OAuth2PARContext holds relevant information about a Pushed Authorization Request in order to process the authorization.
type OAuth2PARContext struct {
ID int `db:"id"`
Signature string `db:"signature"`
RequestID string `db:"request_id"`
ClientID string `db:"client_id"`
RequestedAt time.Time `db:"requested_at"`
Scopes StringSlicePipeDelimited `db:"scopes"`
Audience StringSlicePipeDelimited `db:"audience"`
HandledResponseTypes StringSlicePipeDelimited `db:"handled_response_types"`
ResponseMode string `db:"response_mode"`
DefaultResponseMode string `db:"response_mode_default"`
Revoked bool `db:"revoked"`
Form string `db:"form_data"`
Session []byte `db:"session_data"`
}
func (par *OAuth2PARContext) ToAuthorizeRequest(ctx context.Context, session fosite.Session, store fosite.Storage) (request *fosite.AuthorizeRequest, err error) {
if session != nil {
if err = json.Unmarshal(par.Session, session); err != nil {
return nil, err
}
}
var (
client fosite.Client
form url.Values
)
if client, err = store.GetClient(ctx, par.ClientID); err != nil {
return nil, err
}
if form, err = url.ParseQuery(par.Form); err != nil {
return nil, err
}
request = fosite.NewAuthorizeRequest()
request.Request = fosite.Request{
ID: par.RequestID,
RequestedAt: par.RequestedAt,
Client: client,
RequestedScope: fosite.Arguments(par.Scopes),
RequestedAudience: fosite.Arguments(par.Audience),
Form: form,
Session: session,
}
if par.ResponseMode != "" {
request.ResponseMode = fosite.ResponseModeType(par.ResponseMode)
}
if par.DefaultResponseMode != "" {
request.DefaultResponseMode = fosite.ResponseModeType(par.DefaultResponseMode)
}
if len(par.HandledResponseTypes) != 0 {
request.HandledResponseTypes = fosite.Arguments(par.HandledResponseTypes)
}
return request, nil
}
// OpenIDSession holds OIDC Session information. // OpenIDSession holds OIDC Session information.
type OpenIDSession struct { type OpenIDSession struct {
*openid.DefaultSession `json:"id_token"` *openid.DefaultSession `json:"id_token"`

View File

@ -1,7 +1,7 @@
package oidc package oidc
import ( import (
"fmt" "strings"
"github.com/ory/fosite" "github.com/ory/fosite"
"github.com/ory/x/errorsx" "github.com/ory/x/errorsx"
@ -32,6 +32,8 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client)
ResponseTypes: config.ResponseTypes, ResponseTypes: config.ResponseTypes,
ResponseModes: []fosite.ResponseModeType{fosite.ResponseModeDefault}, ResponseModes: []fosite.ResponseModeType{fosite.ResponseModeDefault},
EnforcePAR: config.EnforcePAR,
UserinfoSigningAlgorithm: config.UserinfoSigningAlgorithm, UserinfoSigningAlgorithm: config.UserinfoSigningAlgorithm,
Policy: authorization.NewLevel(config.Policy), Policy: authorization.NewLevel(config.Policy),
@ -46,22 +48,22 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client)
return client return client
} }
// ValidateAuthorizationPolicy is a helper function to validate additional policy constraints on a per-client basis. // ValidatePKCEPolicy is a helper function to validate PKCE policy constraints on a per-client basis.
func (c *Client) ValidateAuthorizationPolicy(r fosite.Requester) (err error) { func (c *Client) ValidatePKCEPolicy(r fosite.Requester) (err error) {
form := r.GetRequestForm() form := r.GetRequestForm()
if c.EnforcePKCE { if c.EnforcePKCE {
if form.Get("code_challenge") == "" { if form.Get(FormParameterCodeChallenge) == "" {
return errorsx.WithStack(fosite.ErrInvalidRequest. return errorsx.WithStack(fosite.ErrInvalidRequest.
WithHint("Clients must include a code_challenge when performing the authorize code flow, but it is missing."). WithHint("Clients must include a code_challenge when performing the authorize code flow, but it is missing.").
WithDebug("The server is configured in a way that enforces PKCE for this client.")) WithDebug("The server is configured in a way that enforces PKCE for this client."))
} }
if c.EnforcePKCEChallengeMethod { if c.EnforcePKCEChallengeMethod {
if method := form.Get("code_challenge_method"); method != c.PKCEChallengeMethod { if method := form.Get(FormParameterCodeChallengeMethod); method != c.PKCEChallengeMethod {
return errorsx.WithStack(fosite.ErrInvalidRequest. return errorsx.WithStack(fosite.ErrInvalidRequest.
WithHint(fmt.Sprintf("Client must use code_challenge_method=%s, %s is not allowed.", c.PKCEChallengeMethod, method)). WithHintf("Client must use code_challenge_method=%s, %s is not allowed.", c.PKCEChallengeMethod, method).
WithDebug(fmt.Sprintf("The server is configured in a way that enforces PKCE %s as challenge method for this client.", c.PKCEChallengeMethod))) WithDebugf("The server is configured in a way that enforces PKCE %s as challenge method for this client.", c.PKCEChallengeMethod))
} }
} }
} }
@ -69,6 +71,23 @@ func (c *Client) ValidateAuthorizationPolicy(r fosite.Requester) (err error) {
return nil return nil
} }
// ValidatePARPolicy is a helper function to validate additional policy constraints on a per-client basis.
func (c *Client) ValidatePARPolicy(r fosite.Requester, prefix string) (err error) {
form := r.GetRequestForm()
if c.EnforcePAR {
if requestURI := form.Get(FormParameterRequestURI); !strings.HasPrefix(requestURI, prefix) {
if requestURI == "" {
return errorsx.WithStack(ErrPAREnforcedClientMissingPAR.WithDebug("The request_uri parameter was empty."))
}
return errorsx.WithStack(ErrPAREnforcedClientMissingPAR.WithDebugf("The request_uri parameter '%s' is malformed.", requestURI))
}
}
return nil
}
// IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient. // IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient.
func (c *Client) IsAuthenticationLevelSufficient(level authentication.Level) bool { func (c *Client) IsAuthenticationLevelSufficient(level authentication.Level) bool {
if level == authentication.NotAuthenticated { if level == authentication.NotAuthenticated {
@ -105,7 +124,7 @@ func (c *Client) GetID() string {
} }
// GetHashedSecret returns the Secret. // GetHashedSecret returns the Secret.
func (c *Client) GetHashedSecret() []byte { func (c *Client) GetHashedSecret() (secret []byte) {
if c.Secret == nil { if c.Secret == nil {
return []byte(nil) return []byte(nil)
} }
@ -114,7 +133,7 @@ func (c *Client) GetHashedSecret() []byte {
} }
// GetRedirectURIs returns the RedirectURIs. // GetRedirectURIs returns the RedirectURIs.
func (c *Client) GetRedirectURIs() []string { func (c *Client) GetRedirectURIs() (redirectURIs []string) {
return c.RedirectURIs return c.RedirectURIs
} }

View File

@ -224,7 +224,7 @@ func TestNewClientPKCE(t *testing.T) {
expectedEnforcePKCE bool expectedEnforcePKCE bool
expectedEnforcePKCEChallengeMethod bool expectedEnforcePKCEChallengeMethod bool
expected string expected string
req *fosite.Request r *fosite.Request
err string err string
}{ }{
{ {
@ -288,8 +288,8 @@ func TestNewClientPKCE(t *testing.T) {
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.EnforcePKCEChallengeMethod) assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.EnforcePKCEChallengeMethod)
assert.Equal(t, tc.expected, client.PKCEChallengeMethod) assert.Equal(t, tc.expected, client.PKCEChallengeMethod)
if tc.req != nil { if tc.r != nil {
err := client.ValidateAuthorizationPolicy(tc.req) err := client.ValidatePKCEPolicy(tc.r)
if tc.err != "" { if tc.err != "" {
assert.EqualError(t, err, tc.err) assert.EqualError(t, err, tc.err)

View File

@ -6,6 +6,7 @@ import (
"hash" "hash"
"html/template" "html/template"
"net/url" "net/url"
"path"
"time" "time"
"github.com/hashicorp/go-retryablehttp" "github.com/hashicorp/go-retryablehttp"
@ -23,8 +24,8 @@ import (
"github.com/authelia/authelia/v4/internal/utils" "github.com/authelia/authelia/v4/internal/utils"
) )
func NewConfig(config *schema.OpenIDConnectConfiguration, templates *templates.Provider) *Config { func NewConfig(config *schema.OpenIDConnectConfiguration, templates *templates.Provider) (c *Config) {
c := &Config{ c = &Config{
GlobalSecret: []byte(utils.HashSHA256FromString(config.HMACSecret)), GlobalSecret: []byte(utils.HashSHA256FromString(config.HMACSecret)),
SendDebugMessagesToClients: config.EnableClientDebugMessages, SendDebugMessagesToClients: config.EnableClientDebugMessages,
MinParameterEntropy: config.MinimumParameterEntropy, MinParameterEntropy: config.MinimumParameterEntropy,
@ -39,18 +40,23 @@ func NewConfig(config *schema.OpenIDConnectConfiguration, templates *templates.P
EnforcePublicClients: config.EnforcePKCE != "never", EnforcePublicClients: config.EnforcePKCE != "never",
AllowPlainChallengeMethod: config.EnablePKCEPlainChallenge, AllowPlainChallengeMethod: config.EnablePKCEPlainChallenge,
}, },
PAR: PARConfig{
Enforced: config.PAR.Enforce,
ContextLifespan: config.PAR.ContextLifespan,
URIPrefix: urnPARPrefix,
},
Templates: templates, Templates: templates,
} }
c.Strategy.Core = &HMACCoreStrategy{ c.Strategy.Core = &HMACCoreStrategy{
Enigma: &hmac.HMACStrategy{Config: c}, Enigma: &hmac.HMACStrategy{Config: c},
Config: c, Config: c,
prefix: tokenPrefixFmt,
} }
return c return c
} }
// Config is an implementation of the fosite.Configurator.
type Config struct { type Config struct {
// GlobalSecret is the global secret used to sign and verify signatures. // GlobalSecret is the global secret used to sign and verify signatures.
GlobalSecret []byte GlobalSecret []byte
@ -67,7 +73,7 @@ type Config struct {
JWTScopeField jwt.JWTScopeFieldEnum JWTScopeField jwt.JWTScopeFieldEnum
JWTMaxDuration time.Duration JWTMaxDuration time.Duration
Hasher *AdaptiveHasher Hasher *Hasher
Hash HashConfig Hash HashConfig
Strategy StrategyConfig Strategy StrategyConfig
PAR PARConfig PAR PARConfig
@ -91,11 +97,13 @@ type Config struct {
Templates *templates.Provider Templates *templates.Provider
} }
// HashConfig holds specific fosite.Configurator information for hashing.
type HashConfig struct { type HashConfig struct {
ClientSecrets fosite.Hasher ClientSecrets fosite.Hasher
HMAC func() (h hash.Hash) HMAC func() (h hash.Hash)
} }
// StrategyConfig holds specific fosite.Configurator information for various strategies.
type StrategyConfig struct { type StrategyConfig struct {
Core oauth2.CoreStrategy Core oauth2.CoreStrategy
OpenID openid.OpenIDConnectTokenStrategy OpenID openid.OpenIDConnectTokenStrategy
@ -105,17 +113,20 @@ type StrategyConfig struct {
ClientAuthentication fosite.ClientAuthenticationStrategy ClientAuthentication fosite.ClientAuthenticationStrategy
} }
// PARConfig holds specific fosite.Configurator information for Pushed Authorization Requests.
type PARConfig struct { type PARConfig struct {
Enforced bool Enforced bool
URIPrefix string URIPrefix string
ContextLifespan time.Duration ContextLifespan time.Duration
} }
// IssuersConfig holds specific fosite.Configurator information for the issuer.
type IssuersConfig struct { type IssuersConfig struct {
IDToken string IDToken string
AccessToken string AccessToken string
} }
// HandlersConfig holds specific fosite.Configurator handlers configuration information.
type HandlersConfig struct { type HandlersConfig struct {
// ResponseMode provides an extension handler for custom response modes. // ResponseMode provides an extension handler for custom response modes.
ResponseMode fosite.ResponseModeHandler ResponseMode fosite.ResponseModeHandler
@ -136,18 +147,21 @@ type HandlersConfig struct {
PushedAuthorizeEndpoint fosite.PushedAuthorizeEndpointHandlers PushedAuthorizeEndpoint fosite.PushedAuthorizeEndpointHandlers
} }
// GrantTypeJWTBearerConfig holds specific fosite.Configurator information for the JWT Bearer Grant Type.
type GrantTypeJWTBearerConfig struct { type GrantTypeJWTBearerConfig struct {
OptionalClientAuth bool OptionalClientAuth bool
OptionalJTIClaim bool OptionalJTIClaim bool
OptionalIssuedDate bool OptionalIssuedDate bool
} }
// ProofKeyCodeExchangeConfig holds specific fosite.Configurator information for PKCE.
type ProofKeyCodeExchangeConfig struct { type ProofKeyCodeExchangeConfig struct {
Enforce bool Enforce bool
EnforcePublicClients bool EnforcePublicClients bool
AllowPlainChallengeMethod bool AllowPlainChallengeMethod bool
} }
// LifespanConfig holds specific fosite.Configurator information for various lifespans.
type LifespanConfig struct { type LifespanConfig struct {
AccessToken time.Duration AccessToken time.Duration
AuthorizeCode time.Duration AuthorizeCode time.Duration
@ -161,6 +175,7 @@ const (
PromptConsent = "consent" PromptConsent = "consent"
) )
// LoadHandlers reloads the handlers based on the current configuration.
func (c *Config) LoadHandlers(store *Store, strategy jwt.Signer) { func (c *Config) LoadHandlers(store *Store, strategy jwt.Signer) {
validator := openid.NewOpenIDConnectRequestValidator(strategy, c) validator := openid.NewOpenIDConnectRequestValidator(strategy, c)
@ -277,6 +292,10 @@ func (c *Config) LoadHandlers(store *Store, strategy jwt.Signer) {
if h, ok := handler.(fosite.RevocationHandler); ok { if h, ok := handler.(fosite.RevocationHandler); ok {
x.Revocation.Append(h) x.Revocation.Append(h)
} }
if h, ok := handler.(fosite.PushedAuthorizeEndpointHandler); ok {
x.PushedAuthorizeEndpoint.Append(h)
}
} }
c.Handlers = x c.Handlers = x
@ -515,13 +534,24 @@ func (c *Config) GetFormPostHTMLTemplate(ctx context.Context) (tmpl *template.Te
// GetTokenURL returns the token URL. // GetTokenURL returns the token URL.
func (c *Config) GetTokenURL(ctx context.Context) (tokenURL string) { func (c *Config) GetTokenURL(ctx context.Context) (tokenURL string) {
if ctx, ok := ctx.(OpenIDConnectContext); ok {
tokenURI, err := ctx.IssuerURL()
if err != nil {
return c.TokenURL
}
tokenURI.Path = path.Join(tokenURI.Path, EndpointPathToken)
return tokenURI.String()
}
return c.TokenURL return c.TokenURL
} }
// GetSecretsHasher returns the client secrets hashing function. // GetSecretsHasher returns the client secrets hashing function.
func (c *Config) GetSecretsHasher(ctx context.Context) (hasher fosite.Hasher) { func (c *Config) GetSecretsHasher(ctx context.Context) (hasher fosite.Hasher) {
if c.Hash.ClientSecrets == nil { if c.Hash.ClientSecrets == nil {
c.Hash.ClientSecrets, _ = NewAdaptiveHasher() c.Hash.ClientSecrets, _ = NewHasher()
} }
return c.Hash.ClientSecrets return c.Hash.ClientSecrets
@ -583,7 +613,7 @@ func (c *Config) EnforcePushedAuthorize(ctx context.Context) bool {
// GetPushedAuthorizeContextLifespan is the lifespan of the short-lived PAR context. // GetPushedAuthorizeContextLifespan is the lifespan of the short-lived PAR context.
func (c *Config) GetPushedAuthorizeContextLifespan(ctx context.Context) (lifespan time.Duration) { func (c *Config) GetPushedAuthorizeContextLifespan(ctx context.Context) (lifespan time.Duration) {
if c.PAR.ContextLifespan == 0 { if c.PAR.ContextLifespan.Seconds() == 0 {
c.PAR.ContextLifespan = lifespanPARContextDefault c.PAR.ContextLifespan = lifespanPARContextDefault
} }

View File

@ -73,6 +73,25 @@ const (
GrantTypeClientCredentials = "client_credentials" GrantTypeClientCredentials = "client_credentials"
) )
// Client Auth Method strings.
const (
ClientAuthMethodClientSecretBasic = "client_secret_basic"
ClientAuthMethodClientSecretPost = "client_secret_post"
ClientAuthMethodClientSecretJWT = "client_secret_jwt"
ClientAuthMethodNone = "none"
)
// Response Type strings.
const (
ResponseTypeAuthorizationCodeFlow = "code"
ResponseTypeImplicitFlowIDToken = "id_token"
ResponseTypeImplicitFlowToken = "token"
ResponseTypeImplicitFlowBoth = "id_token token"
ResponseTypeHybridFlowIDToken = "code id_token"
ResponseTypeHybridFlowToken = "code token"
ResponseTypeHybridFlowBoth = "code id_token token"
)
// Signing Algorithm strings. // Signing Algorithm strings.
const ( const (
SigningAlgorithmNone = none SigningAlgorithmNone = none
@ -91,6 +110,12 @@ const (
PKCEChallengeMethodSHA256 = "S256" PKCEChallengeMethodSHA256 = "S256"
) )
const (
FormParameterRequestURI = "request_uri"
FormParameterCodeChallenge = "code_challenge"
FormParameterCodeChallengeMethod = "code_challenge_method"
)
// Endpoints. // Endpoints.
const ( const (
EndpointAuthorization = "authorization" EndpointAuthorization = "authorization"
@ -98,6 +123,7 @@ const (
EndpointUserinfo = "userinfo" EndpointUserinfo = "userinfo"
EndpointIntrospection = "introspection" EndpointIntrospection = "introspection"
EndpointRevocation = "revocation" EndpointRevocation = "revocation"
EndpointPushedAuthorizationRequest = "pushed-authorization-request"
) )
// JWT Headers. // JWT Headers.
@ -107,7 +133,9 @@ const (
) )
const ( const (
tokenPrefixFmt = "authelia_%s_" //nolint:gosec tokenPrefixOrgAutheliaFmt = "authelia_%s_" //nolint:gosec
tokenPrefixOrgOryFmt = "ory_%s_" //nolint:gosec
tokenPrefixPartAccessToken = "at" tokenPrefixPartAccessToken = "at"
tokenPrefixPartRefreshToken = "rt" tokenPrefixPartRefreshToken = "rt"
tokenPrefixPartAuthorizeCode = "ac" tokenPrefixPartAuthorizeCode = "ac"
@ -127,6 +155,8 @@ const (
EndpointPathUserinfo = EndpointPathRoot + "/" + EndpointUserinfo EndpointPathUserinfo = EndpointPathRoot + "/" + EndpointUserinfo
EndpointPathIntrospection = EndpointPathRoot + "/" + EndpointIntrospection EndpointPathIntrospection = EndpointPathRoot + "/" + EndpointIntrospection
EndpointPathRevocation = EndpointPathRoot + "/" + EndpointRevocation EndpointPathRevocation = EndpointPathRoot + "/" + EndpointRevocation
EndpointPathPushedAuthorizationRequest = EndpointPathRoot + "/" + EndpointPushedAuthorizationRequest
) )
// Authentication Method Reference Values https://datatracker.ietf.org/doc/html/rfc8176 // Authentication Method Reference Values https://datatracker.ietf.org/doc/html/rfc8176

View File

@ -19,7 +19,6 @@ type HMACCoreStrategy struct {
fosite.RefreshTokenLifespanProvider fosite.RefreshTokenLifespanProvider
fosite.AuthorizeCodeLifespanProvider fosite.AuthorizeCodeLifespanProvider
} }
prefix string
} }
// AccessTokenSignature implements oauth2.AccessTokenStrategy. // AccessTokenSignature implements oauth2.AccessTokenStrategy.
@ -112,11 +111,11 @@ func (h *HMACCoreStrategy) ValidateAuthorizeCode(ctx context.Context, r fosite.R
} }
func (h *HMACCoreStrategy) getPrefix(part string) string { func (h *HMACCoreStrategy) getPrefix(part string) string {
if len(h.prefix) == 0 { return h.getCustomPrefix(tokenPrefixOrgAutheliaFmt, part)
return ""
} }
return fmt.Sprintf(h.prefix, part) func (h *HMACCoreStrategy) getCustomPrefix(tokenPrefixFmt, part string) string {
return fmt.Sprintf(tokenPrefixFmt, part)
} }
func (h *HMACCoreStrategy) setPrefix(token, part string) string { func (h *HMACCoreStrategy) setPrefix(token, part string) string {
@ -124,5 +123,9 @@ func (h *HMACCoreStrategy) setPrefix(token, part string) string {
} }
func (h *HMACCoreStrategy) trimPrefix(token, part string) string { func (h *HMACCoreStrategy) trimPrefix(token, part string) string {
if strings.HasPrefix(token, h.getCustomPrefix(tokenPrefixOrgOryFmt, part)) {
return strings.TrimPrefix(token, h.getCustomPrefix(tokenPrefixOrgOryFmt, part))
}
return strings.TrimPrefix(token, h.getPrefix(part)) return strings.TrimPrefix(token, h.getPrefix(part))
} }

View File

@ -0,0 +1,56 @@
package oidc
import (
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestHMACCoreStrategy_TrimPrefix(t *testing.T) {
testCases := []struct {
name string
have string
part string
expected string
}{
{"ShouldTrimAutheliaPrefix", "authelia_at_example", tokenPrefixPartAccessToken, "example"},
{"ShouldTrimOryPrefix", "ory_at_example", tokenPrefixPartAccessToken, "example"},
{"ShouldTrimOnlyAutheliaPrefix", "authelia_at_ory_at_example", tokenPrefixPartAccessToken, "ory_at_example"},
{"ShouldTrimOnlyOryPrefix", "ory_at_authelia_at_example", tokenPrefixPartAccessToken, "authelia_at_example"},
{"ShouldNotTrimGitHubPrefix", "gh_at_example", tokenPrefixPartAccessToken, "gh_at_example"},
}
strategy := &HMACCoreStrategy{}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.expected, strategy.trimPrefix(tc.have, tc.part))
})
}
}
func TestHMACCoreStrategy_GetSetPrefix(t *testing.T) {
testCases := []struct {
name string
have string
expectedSet string
expectedGet string
}{
{"ShouldAddPrefix", "example", "authelia_%s_example", "authelia_%s_"},
}
strategy := &HMACCoreStrategy{}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, part := range []string{tokenPrefixPartAccessToken, tokenPrefixPartAuthorizeCode, tokenPrefixPartRefreshToken} {
t.Run(strings.ToUpper(part), func(t *testing.T) {
assert.Equal(t, fmt.Sprintf(tc.expectedSet, part), strategy.setPrefix(tc.have, part))
assert.Equal(t, fmt.Sprintf(tc.expectedGet, part), strategy.getPrefix(part))
})
}
})
}
}

View File

@ -1,21 +1,29 @@
package oidc package oidc
import (
"github.com/authelia/authelia/v4/internal/configuration/schema"
)
// NewOpenIDConnectWellKnownConfiguration generates a new OpenIDConnectWellKnownConfiguration. // NewOpenIDConnectWellKnownConfiguration generates a new OpenIDConnectWellKnownConfiguration.
func NewOpenIDConnectWellKnownConfiguration(enablePKCEPlainChallenge bool, clients map[string]*Client) (config OpenIDConnectWellKnownConfiguration) { func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration, clients map[string]*Client) (config OpenIDConnectWellKnownConfiguration) {
config = OpenIDConnectWellKnownConfiguration{ config = OpenIDConnectWellKnownConfiguration{
CommonDiscoveryOptions: CommonDiscoveryOptions{ CommonDiscoveryOptions: CommonDiscoveryOptions{
SubjectTypesSupported: []string{ SubjectTypesSupported: []string{
SubjectTypePublic, SubjectTypePublic,
}, },
ResponseTypesSupported: []string{ ResponseTypesSupported: []string{
"code", ResponseTypeAuthorizationCodeFlow,
"token", ResponseTypeImplicitFlowIDToken,
"id_token", ResponseTypeImplicitFlowToken,
"code token", ResponseTypeImplicitFlowBoth,
"code id_token", ResponseTypeHybridFlowIDToken,
"token id_token", ResponseTypeHybridFlowToken,
"code token id_token", ResponseTypeHybridFlowBoth,
"none", },
GrantTypesSupported: []string{
GrantTypeAuthorizationCode,
GrantTypeImplicit,
GrantTypeRefreshToken,
}, },
ResponseModesSupported: []string{ ResponseModesSupported: []string{
ResponseModeFormPost, ResponseModeFormPost,
@ -49,6 +57,12 @@ func NewOpenIDConnectWellKnownConfiguration(enablePKCEPlainChallenge bool, clien
ClaimPreferredUsername, ClaimPreferredUsername,
ClaimFullName, ClaimFullName,
}, },
TokenEndpointAuthMethodsSupported: []string{
ClientAuthMethodClientSecretBasic,
ClientAuthMethodClientSecretPost,
ClientAuthMethodClientSecretJWT,
ClientAuthMethodNone,
},
}, },
OAuth2DiscoveryOptions: OAuth2DiscoveryOptions{ OAuth2DiscoveryOptions: OAuth2DiscoveryOptions{
CodeChallengeMethodsSupported: []string{ CodeChallengeMethodsSupported: []string{
@ -68,6 +82,9 @@ func NewOpenIDConnectWellKnownConfiguration(enablePKCEPlainChallenge bool, clien
SigningAlgorithmRSAWithSHA256, SigningAlgorithmRSAWithSHA256,
}, },
}, },
PushedAuthorizationDiscoveryOptions: PushedAuthorizationDiscoveryOptions{
RequirePushedAuthorizationRequests: c.PAR.Enforce,
},
} }
var pairwise, public bool var pairwise, public bool
@ -86,7 +103,7 @@ func NewOpenIDConnectWellKnownConfiguration(enablePKCEPlainChallenge bool, clien
config.SubjectTypesSupported = append(config.SubjectTypesSupported, SubjectTypePairwise) config.SubjectTypesSupported = append(config.SubjectTypesSupported, SubjectTypePairwise)
} }
if enablePKCEPlainChallenge { if c.EnablePKCEPlainChallenge {
config.CodeChallengeMethodsSupported = append(config.CodeChallengeMethodsSupported, PKCEChallengeMethodPlain) config.CodeChallengeMethodsSupported = append(config.CodeChallengeMethodsSupported, PKCEChallengeMethodPlain)
} }

View File

@ -4,12 +4,15 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/authelia/authelia/v4/internal/configuration/schema"
) )
func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) { func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
testCases := []struct { testCases := []struct {
desc string desc string
pkcePlainChallenge bool pkcePlainChallenge bool
enforcePAR bool
clients map[string]*Client clients map[string]*Client
expectCodeChallengeMethodsSupported, expectSubjectTypesSupported []string expectCodeChallengeMethodsSupported, expectSubjectTypesSupported []string
@ -63,7 +66,14 @@ func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) { t.Run(tc.desc, func(t *testing.T) {
actual := NewOpenIDConnectWellKnownConfiguration(tc.pkcePlainChallenge, tc.clients) c := schema.OpenIDConnectConfiguration{
EnablePKCEPlainChallenge: tc.pkcePlainChallenge,
PAR: schema.OpenIDConnectPARConfiguration{
Enforce: tc.enforcePAR,
},
}
actual := NewOpenIDConnectWellKnownConfiguration(&c, tc.clients)
for _, codeChallengeMethod := range tc.expectCodeChallengeMethodsSupported { for _, codeChallengeMethod := range tc.expectCodeChallengeMethodsSupported {
assert.Contains(t, actual.CodeChallengeMethodsSupported, codeChallengeMethod) assert.Contains(t, actual.CodeChallengeMethodsSupported, codeChallengeMethod)
} }

View File

@ -9,11 +9,27 @@ import (
var errPasswordsDoNotMatch = errors.New("the passwords don't match") var errPasswordsDoNotMatch = errors.New("the passwords don't match")
var ( var (
// ErrIssuerCouldNotDerive is sent when the issuer couldn't be determined from the headers.
ErrIssuerCouldNotDerive = fosite.ErrServerError.WithHint("Could not safely derive the issuer.") ErrIssuerCouldNotDerive = fosite.ErrServerError.WithHint("Could not safely derive the issuer.")
// ErrSubjectCouldNotLookup is sent when the Subject Identifier for a user couldn't be generated or obtained from the database.
ErrSubjectCouldNotLookup = fosite.ErrServerError.WithHint("Could not lookup user subject.") ErrSubjectCouldNotLookup = fosite.ErrServerError.WithHint("Could not lookup user subject.")
// ErrConsentCouldNotPerform is sent when the Consent Session couldn't be performed for varying reasons.
ErrConsentCouldNotPerform = fosite.ErrServerError.WithHint("Could not perform consent.") ErrConsentCouldNotPerform = fosite.ErrServerError.WithHint("Could not perform consent.")
// ErrConsentCouldNotGenerate is sent when the Consent Session failed to be generated for some reason, usually a failed UUIDv4 generation.
ErrConsentCouldNotGenerate = fosite.ErrServerError.WithHint("Could not generate the consent session.") ErrConsentCouldNotGenerate = fosite.ErrServerError.WithHint("Could not generate the consent session.")
// ErrConsentCouldNotSave is sent when the Consent Session couldn't be saved to the database.
ErrConsentCouldNotSave = fosite.ErrServerError.WithHint("Could not save the consent session.") ErrConsentCouldNotSave = fosite.ErrServerError.WithHint("Could not save the consent session.")
// ErrConsentCouldNotLookup is sent when the Consent ID is not a known UUID.
ErrConsentCouldNotLookup = fosite.ErrServerError.WithHint("Failed to lookup the consent session.") ErrConsentCouldNotLookup = fosite.ErrServerError.WithHint("Failed to lookup the consent session.")
// ErrConsentMalformedChallengeID is sent when the Consent ID is not a UUID.
ErrConsentMalformedChallengeID = fosite.ErrServerError.WithHint("Malformed consent session challenge ID.") ErrConsentMalformedChallengeID = fosite.ErrServerError.WithHint("Malformed consent session challenge ID.")
// ErrPAREnforcedClientMissingPAR is sent when a client has EnforcePAR configured but the Authorization Request was not Pushed.
ErrPAREnforcedClientMissingPAR = fosite.ErrInvalidRequest.WithHint("Pushed Authorization Requests are enforced for this client but no such request was sent.")
) )

View File

@ -8,8 +8,9 @@ import (
"github.com/go-crypt/crypt/algorithm/plaintext" "github.com/go-crypt/crypt/algorithm/plaintext"
) )
func NewAdaptiveHasher() (hasher *AdaptiveHasher, err error) { // NewHasher returns a new Hasher.
hasher = &AdaptiveHasher{} func NewHasher() (hasher *Hasher, err error) {
hasher = &Hasher{}
if hasher.decoder, err = crypt.NewDefaultDecoder(); err != nil { if hasher.decoder, err = crypt.NewDefaultDecoder(); err != nil {
return nil, err return nil, err
@ -22,13 +23,13 @@ func NewAdaptiveHasher() (hasher *AdaptiveHasher, err error) {
return hasher, nil return hasher, nil
} }
// AdaptiveHasher implements the fosite.Hasher interface without an actual hashing algo. // Hasher implements the fosite.Hasher interface and adaptively compares hashes.
type AdaptiveHasher struct { type Hasher struct {
decoder algorithm.DecoderRegister decoder algorithm.DecoderRegister
} }
// Compare compares the hash with the data and returns an error if they don't match. // Compare compares the hash with the data and returns an error if they don't match.
func (h *AdaptiveHasher) Compare(_ context.Context, hash, data []byte) (err error) { func (h Hasher) Compare(_ context.Context, hash, data []byte) (err error) {
var digest algorithm.Digest var digest algorithm.Digest
if digest, err = h.decoder.Decode(string(hash)); err != nil { if digest, err = h.decoder.Decode(string(hash)); err != nil {
@ -43,6 +44,6 @@ func (h *AdaptiveHasher) Compare(_ context.Context, hash, data []byte) (err erro
} }
// Hash creates a new hash from data. // Hash creates a new hash from data.
func (h *AdaptiveHasher) Hash(_ context.Context, data []byte) (hash []byte, err error) { func (h Hasher) Hash(_ context.Context, data []byte) (hash []byte, err error) {
return data, nil return data, nil
} }

View File

@ -9,7 +9,7 @@ import (
) )
func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) { func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
hasher, err := NewAdaptiveHasher() hasher, err := NewHasher()
require.NoError(t, err) require.NoError(t, err)
@ -22,7 +22,7 @@ func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
} }
func TestShouldNotRaiseErrorOnEqualPasswordsPlainTextWithSeparator(t *testing.T) { func TestShouldNotRaiseErrorOnEqualPasswordsPlainTextWithSeparator(t *testing.T) {
hasher, err := NewAdaptiveHasher() hasher, err := NewHasher()
require.NoError(t, err) require.NoError(t, err)
@ -35,7 +35,7 @@ func TestShouldNotRaiseErrorOnEqualPasswordsPlainTextWithSeparator(t *testing.T)
} }
func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) { func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
hasher, err := NewAdaptiveHasher() hasher, err := NewHasher()
require.NoError(t, err) require.NoError(t, err)
@ -48,7 +48,7 @@ func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
} }
func TestShouldHashPassword(t *testing.T) { func TestShouldHashPassword(t *testing.T) {
hasher := AdaptiveHasher{} hasher := Hasher{}
data := []byte("abc") data := []byte("abc")

View File

@ -37,7 +37,7 @@ func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, store s
provider.Config.LoadHandlers(provider.Store, provider.KeyManager.Strategy()) provider.Config.LoadHandlers(provider.Store, provider.KeyManager.Strategy())
provider.discovery = NewOpenIDConnectWellKnownConfiguration(config.EnablePKCEPlainChallenge, provider.Store.clients) provider.discovery = NewOpenIDConnectWellKnownConfiguration(config, provider.Store.clients)
return provider, nil return provider, nil
} }
@ -50,12 +50,12 @@ func (p *OpenIDConnectProvider) GetOAuth2WellKnownConfiguration(issuer string) O
} }
options.Issuer = issuer options.Issuer = issuer
options.JWKSURI = fmt.Sprintf("%s%s", issuer, EndpointPathJWKs) options.JWKSURI = fmt.Sprintf("%s%s", issuer, EndpointPathJWKs)
options.IntrospectionEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathIntrospection)
options.TokenEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathToken)
options.AuthorizationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathAuthorization) options.AuthorizationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathAuthorization)
options.PushedAuthorizationRequestEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathPushedAuthorizationRequest)
options.TokenEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathToken)
options.IntrospectionEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathIntrospection)
options.RevocationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathRevocation) options.RevocationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathRevocation)
return options return options
@ -72,14 +72,14 @@ func (p *OpenIDConnectProvider) GetOpenIDConnectWellKnownConfiguration(issuer st
} }
options.Issuer = issuer options.Issuer = issuer
options.JWKSURI = fmt.Sprintf("%s%s", issuer, EndpointPathJWKs) options.JWKSURI = fmt.Sprintf("%s%s", issuer, EndpointPathJWKs)
options.IntrospectionEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathIntrospection)
options.TokenEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathToken)
options.AuthorizationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathAuthorization) options.AuthorizationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathAuthorization)
options.RevocationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathRevocation) options.PushedAuthorizationRequestEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathPushedAuthorizationRequest)
options.TokenEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathToken)
options.UserinfoEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathUserinfo) options.UserinfoEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathUserinfo)
options.IntrospectionEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathIntrospection)
options.RevocationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathRevocation)
return options return options
} }

View File

@ -142,15 +142,25 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
assert.Len(t, disco.SubjectTypesSupported, 1) assert.Len(t, disco.SubjectTypesSupported, 1)
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic) assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
assert.Len(t, disco.ResponseTypesSupported, 8) assert.Len(t, disco.ResponseTypesSupported, 7)
assert.Contains(t, disco.ResponseTypesSupported, "code") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow)
assert.Contains(t, disco.ResponseTypesSupported, "token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowIDToken)
assert.Contains(t, disco.ResponseTypesSupported, "id_token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowToken)
assert.Contains(t, disco.ResponseTypesSupported, "code token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowBoth)
assert.Contains(t, disco.ResponseTypesSupported, "code id_token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowIDToken)
assert.Contains(t, disco.ResponseTypesSupported, "token id_token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken)
assert.Contains(t, disco.ResponseTypesSupported, "code token id_token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth)
assert.Contains(t, disco.ResponseTypesSupported, "none")
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 4)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretJWT)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone)
assert.Len(t, disco.GrantTypesSupported, 3)
assert.Contains(t, disco.GrantTypesSupported, GrantTypeAuthorizationCode)
assert.Contains(t, disco.GrantTypesSupported, GrantTypeRefreshToken)
assert.Contains(t, disco.GrantTypesSupported, GrantTypeImplicit)
assert.Len(t, disco.IDTokenSigningAlgValuesSupported, 1) assert.Len(t, disco.IDTokenSigningAlgValuesSupported, 1)
assert.Contains(t, disco.IDTokenSigningAlgValuesSupported, SigningAlgorithmRSAWithSHA256) assert.Contains(t, disco.IDTokenSigningAlgValuesSupported, SigningAlgorithmRSAWithSHA256)
@ -231,15 +241,25 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
assert.Len(t, disco.SubjectTypesSupported, 1) assert.Len(t, disco.SubjectTypesSupported, 1)
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic) assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
assert.Len(t, disco.ResponseTypesSupported, 8) assert.Len(t, disco.ResponseTypesSupported, 7)
assert.Contains(t, disco.ResponseTypesSupported, "code") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow)
assert.Contains(t, disco.ResponseTypesSupported, "token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowIDToken)
assert.Contains(t, disco.ResponseTypesSupported, "id_token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowToken)
assert.Contains(t, disco.ResponseTypesSupported, "code token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowBoth)
assert.Contains(t, disco.ResponseTypesSupported, "code id_token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowIDToken)
assert.Contains(t, disco.ResponseTypesSupported, "token id_token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken)
assert.Contains(t, disco.ResponseTypesSupported, "code token id_token") assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth)
assert.Contains(t, disco.ResponseTypesSupported, "none")
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 4)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretJWT)
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone)
assert.Len(t, disco.GrantTypesSupported, 3)
assert.Contains(t, disco.GrantTypesSupported, GrantTypeAuthorizationCode)
assert.Contains(t, disco.GrantTypesSupported, GrantTypeRefreshToken)
assert.Contains(t, disco.GrantTypesSupported, GrantTypeImplicit)
assert.Len(t, disco.ClaimsSupported, 18) assert.Len(t, disco.ClaimsSupported, 18)
assert.Contains(t, disco.ClaimsSupported, ClaimAuthenticationMethodsReference) assert.Contains(t, disco.ClaimsSupported, ClaimAuthenticationMethodsReference)

View File

@ -165,7 +165,7 @@ func (s *Store) InvalidateAuthorizeCodeSession(ctx context.Context, code string)
// This implements a portion of oauth2.AuthorizeCodeStorage. // This implements a portion of oauth2.AuthorizeCodeStorage.
func (s *Store) GetAuthorizeCodeSession(ctx context.Context, code string, session fosite.Session) (request fosite.Requester, err error) { func (s *Store) GetAuthorizeCodeSession(ctx context.Context, code string, session fosite.Session) (request fosite.Requester, err error) {
// TODO: Implement the fosite.ErrInvalidatedAuthorizeCode error above. This requires splitting the invalidated sessions and deleted sessions. // TODO: Implement the fosite.ErrInvalidatedAuthorizeCode error above. This requires splitting the invalidated sessions and deleted sessions.
return s.loadSessionBySignature(ctx, storage.OAuth2SessionTypeAuthorizeCode, code, session) return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeAuthorizeCode, code, session)
} }
// CreateAccessTokenSession stores the authorization request for a given access token. // CreateAccessTokenSession stores the authorization request for a given access token.
@ -190,7 +190,7 @@ func (s *Store) RevokeAccessToken(ctx context.Context, requestID string) (err er
// GetAccessTokenSession gets the authorization request for a given access token. // GetAccessTokenSession gets the authorization request for a given access token.
// This implements a portion of oauth2.AccessTokenStorage. // This implements a portion of oauth2.AccessTokenStorage.
func (s *Store) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) { func (s *Store) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) {
return s.loadSessionBySignature(ctx, storage.OAuth2SessionTypeAccessToken, signature, session) return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeAccessToken, signature, session)
} }
// CreateRefreshTokenSession stores the authorization request for a given refresh token. // CreateRefreshTokenSession stores the authorization request for a given refresh token.
@ -223,7 +223,7 @@ func (s *Store) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requestI
// GetRefreshTokenSession gets the authorization request for a given refresh token. // GetRefreshTokenSession gets the authorization request for a given refresh token.
// This implements a portion of oauth2.RefreshTokenStorage. // This implements a portion of oauth2.RefreshTokenStorage.
func (s *Store) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) { func (s *Store) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) {
return s.loadSessionBySignature(ctx, storage.OAuth2SessionTypeRefreshToken, signature, session) return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeRefreshToken, signature, session)
} }
// CreatePKCERequestSession stores the authorization request for a given PKCE request. // CreatePKCERequestSession stores the authorization request for a given PKCE request.
@ -241,7 +241,7 @@ func (s *Store) DeletePKCERequestSession(ctx context.Context, signature string)
// GetPKCERequestSession gets the authorization request for a given PKCE request. // GetPKCERequestSession gets the authorization request for a given PKCE request.
// This implements a portion of pkce.PKCERequestStorage. // This implements a portion of pkce.PKCERequestStorage.
func (s *Store) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (requester fosite.Requester, err error) { func (s *Store) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (requester fosite.Requester, err error) {
return s.loadSessionBySignature(ctx, storage.OAuth2SessionTypePKCEChallenge, signature, session) return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypePKCEChallenge, signature, session)
} }
// CreateOpenIDConnectSession creates an open id connect session for a given authorize code. // CreateOpenIDConnectSession creates an open id connect session for a given authorize code.
@ -263,7 +263,37 @@ func (s *Store) DeleteOpenIDConnectSession(ctx context.Context, authorizeCode st
// - or an arbitrary error if an error occurred. // - or an arbitrary error if an error occurred.
// This implements a portion of openid.OpenIDConnectRequestStorage. // This implements a portion of openid.OpenIDConnectRequestStorage.
func (s *Store) GetOpenIDConnectSession(ctx context.Context, authorizeCode string, request fosite.Requester) (r fosite.Requester, err error) { func (s *Store) GetOpenIDConnectSession(ctx context.Context, authorizeCode string, request fosite.Requester) (r fosite.Requester, err error) {
return s.loadSessionBySignature(ctx, storage.OAuth2SessionTypeOpenIDConnect, authorizeCode, request.GetSession()) return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeOpenIDConnect, authorizeCode, request.GetSession())
}
// CreatePARSession stores the pushed authorization request context. The requestURI is used to derive the key.
// This implements a portion of fosite.PARStorage.
func (s *Store) CreatePARSession(ctx context.Context, requestURI string, request fosite.AuthorizeRequester) (err error) {
var par *model.OAuth2PARContext
if par, err = model.NewOAuth2PARContext(requestURI, request); err != nil {
return err
}
return s.provider.SaveOAuth2PARContext(ctx, *par)
}
// GetPARSession gets the push authorization request context. The caller is expected to merge the AuthorizeRequest.
// This implements a portion of fosite.PARStorage.
func (s *Store) GetPARSession(ctx context.Context, requestURI string) (request fosite.AuthorizeRequester, err error) {
var par *model.OAuth2PARContext
if par, err = s.provider.LoadOAuth2PARContext(ctx, requestURI); err != nil {
return nil, err
}
return par.ToAuthorizeRequest(ctx, NewSession(), s)
}
// DeletePARSession deletes the context.
// This implements a portion of fosite.PARStorage.
func (s *Store) DeletePARSession(ctx context.Context, requestURI string) (err error) {
return s.provider.RevokeOAuth2PARContext(ctx, requestURI)
} }
// IsJWTUsed implements an interface required for RFC7523. // IsJWTUsed implements an interface required for RFC7523.
@ -280,7 +310,7 @@ func (s *Store) MarkJWTUsedForTime(ctx context.Context, jti string, exp time.Tim
return s.SetClientAssertionJWT(ctx, jti, exp) return s.SetClientAssertionJWT(ctx, jti, exp)
} }
func (s *Store) loadSessionBySignature(ctx context.Context, sessionType storage.OAuth2SessionType, signature string, session fosite.Session) (r fosite.Requester, err error) { func (s *Store) loadRequesterBySignature(ctx context.Context, sessionType storage.OAuth2SessionType, signature string, session fosite.Session) (r fosite.Requester, err error) {
var ( var (
sessionModel *model.OAuth2Session sessionModel *model.OAuth2Session
) )

View File

@ -1,6 +1,7 @@
package oidc package oidc
import ( import (
"context"
"net/url" "net/url"
"time" "time"
@ -118,6 +119,8 @@ type Client struct {
ResponseTypes []string ResponseTypes []string
ResponseModes []fosite.ResponseModeType ResponseModes []fosite.ResponseModeType
EnforcePAR bool
UserinfoSigningAlgorithm string UserinfoSigningAlgorithm string
Policy authorization.Level Policy authorization.Level
@ -643,3 +646,10 @@ type OpenIDConnectWellKnownConfiguration struct {
OpenIDConnectFrontChannelLogoutDiscoveryOptions OpenIDConnectFrontChannelLogoutDiscoveryOptions
OpenIDConnectBackChannelLogoutDiscoveryOptions OpenIDConnectBackChannelLogoutDiscoveryOptions
} }
// OpenIDConnectContext represents the context implementation that is used by some OpenID Connect 1.0 implementations.
type OpenIDConnectContext interface {
context.Context
IssuerURL() (issuerURL *url.URL, err error)
}

View File

@ -338,6 +338,15 @@ func handleRouter(config *schema.Configuration, providers middlewares.Providers)
r.GET("/api/oidc/authorize", policyCORSAuthorization.Middleware(bridgeOIDC(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)))) r.POST("/api/oidc/authorize", policyCORSAuthorization.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
policyCORSPAR := middlewares.NewCORSPolicyBuilder().
WithAllowedMethods(fasthttp.MethodOptions, fasthttp.MethodPost).
WithAllowedOrigins(allowedOrigins...).
WithEnabled(utils.IsStringInSliceFold(oidc.EndpointPushedAuthorizationRequest, config.IdentityProviders.OIDC.CORS.Endpoints)).
Build()
r.OPTIONS(oidc.EndpointPathPushedAuthorizationRequest, policyCORSPAR.HandleOnlyOPTIONS)
r.POST(oidc.EndpointPathPushedAuthorizationRequest, policyCORSPAR.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectPushedAuthorizationRequest))))
policyCORSToken := middlewares.NewCORSPolicyBuilder(). policyCORSToken := middlewares.NewCORSPolicyBuilder().
WithAllowCredentials(true). WithAllowCredentials(true).
WithAllowedMethods(fasthttp.MethodOptions, fasthttp.MethodPost). WithAllowedMethods(fasthttp.MethodOptions, fasthttp.MethodPost).

View File

@ -14,15 +14,16 @@ const (
tableWebauthnDevices = "webauthn_devices" tableWebauthnDevices = "webauthn_devices"
tableWebauthnUsers = "webauthn_users" tableWebauthnUsers = "webauthn_users"
tableOAuth2BlacklistedJTI = "oauth2_blacklisted_jti"
tableOAuth2ConsentSession = "oauth2_consent_session" tableOAuth2ConsentSession = "oauth2_consent_session"
tableOAuth2ConsentPreConfiguration = "oauth2_consent_preconfiguration" tableOAuth2ConsentPreConfiguration = "oauth2_consent_preconfiguration"
tableOAuth2AuthorizeCodeSession = "oauth2_authorization_code_session"
tableOAuth2AccessTokenSession = "oauth2_access_token_session" //nolint:gosec // This is not a hardcoded credential. tableOAuth2AccessTokenSession = "oauth2_access_token_session" //nolint:gosec // This is not a hardcoded credential.
tableOAuth2RefreshTokenSession = "oauth2_refresh_token_session" //nolint:gosec // This is not a hardcoded credential. tableOAuth2AuthorizeCodeSession = "oauth2_authorization_code_session"
tableOAuth2PKCERequestSession = "oauth2_pkce_request_session"
tableOAuth2OpenIDConnectSession = "oauth2_openid_connect_session" tableOAuth2OpenIDConnectSession = "oauth2_openid_connect_session"
tableOAuth2BlacklistedJTI = "oauth2_blacklisted_jti" tableOAuth2PARContext = "oauth2_par_context"
tableOAuth2PKCERequestSession = "oauth2_pkce_request_session"
tableOAuth2RefreshTokenSession = "oauth2_refresh_token_session" //nolint:gosec // This is not a hardcoded credential.
tableMigrations = "migrations" tableMigrations = "migrations"
tableEncryption = "encryption" tableEncryption = "encryption"
@ -33,26 +34,29 @@ type OAuth2SessionType int
// Representation of specific OAuth 2.0 session types. // Representation of specific OAuth 2.0 session types.
const ( const (
OAuth2SessionTypeAuthorizeCode OAuth2SessionType = iota OAuth2SessionTypeAccessToken OAuth2SessionType = iota
OAuth2SessionTypeAccessToken OAuth2SessionTypeAuthorizeCode
OAuth2SessionTypeRefreshToken
OAuth2SessionTypePKCEChallenge
OAuth2SessionTypeOpenIDConnect OAuth2SessionTypeOpenIDConnect
OAuth2SessionTypePAR
OAuth2SessionTypePKCEChallenge
OAuth2SessionTypeRefreshToken
) )
// String returns a string representation of this OAuth2SessionType. // String returns a string representation of this OAuth2SessionType.
func (s OAuth2SessionType) String() string { func (s OAuth2SessionType) String() string {
switch s { switch s {
case OAuth2SessionTypeAuthorizeCode:
return "authorization code"
case OAuth2SessionTypeAccessToken: case OAuth2SessionTypeAccessToken:
return "access token" return "access token"
case OAuth2SessionTypeRefreshToken: case OAuth2SessionTypeAuthorizeCode:
return "refresh token" return "authorization code"
case OAuth2SessionTypePKCEChallenge:
return "pkce challenge"
case OAuth2SessionTypeOpenIDConnect: case OAuth2SessionTypeOpenIDConnect:
return "openid connect" return "openid connect"
case OAuth2SessionTypePAR:
return "pushed authorization request context"
case OAuth2SessionTypePKCEChallenge:
return "pkce challenge"
case OAuth2SessionTypeRefreshToken:
return "refresh token"
default: default:
return "invalid" return "invalid"
} }
@ -61,16 +65,18 @@ func (s OAuth2SessionType) String() string {
// Table returns the table name for this session type. // Table returns the table name for this session type.
func (s OAuth2SessionType) Table() string { func (s OAuth2SessionType) Table() string {
switch s { switch s {
case OAuth2SessionTypeAuthorizeCode:
return tableOAuth2AuthorizeCodeSession
case OAuth2SessionTypeAccessToken: case OAuth2SessionTypeAccessToken:
return tableOAuth2AccessTokenSession return tableOAuth2AccessTokenSession
case OAuth2SessionTypeRefreshToken: case OAuth2SessionTypeAuthorizeCode:
return tableOAuth2RefreshTokenSession return tableOAuth2AuthorizeCodeSession
case OAuth2SessionTypePKCEChallenge:
return tableOAuth2PKCERequestSession
case OAuth2SessionTypeOpenIDConnect: case OAuth2SessionTypeOpenIDConnect:
return tableOAuth2OpenIDConnectSession return tableOAuth2OpenIDConnectSession
case OAuth2SessionTypePAR:
return tableOAuth2PARContext
case OAuth2SessionTypePKCEChallenge:
return tableOAuth2PKCERequestSession
case OAuth2SessionTypeRefreshToken:
return tableOAuth2RefreshTokenSession
default: default:
return "" return ""
} }
@ -120,7 +126,7 @@ const (
) )
var ( var (
reMigration = regexp.MustCompile(`^V(\d{4})\.([^.]+)\.(all|sqlite|postgres|mysql)\.(up|down)\.sql$`) reMigration = regexp.MustCompile(`^V(?P<Version>\d{4})\.(?P<Name>[^.]+)\.(?P<Provider>(all|sqlite|postgres|mysql))\.(?P<Direction>(up|down))\.sql$`)
) )
const ( const (

View File

@ -130,15 +130,15 @@ func skipMigration(providerName string, up bool, target, prior int, migration *m
} }
func scanMigration(m string) (migration model.SchemaMigration, err error) { func scanMigration(m string) (migration model.SchemaMigration, err error) {
result := reMigration.FindStringSubmatch(m) if !reMigration.MatchString(m) {
if result == nil || len(result) != 5 {
return model.SchemaMigration{}, errors.New("invalid migration: could not parse the format") return model.SchemaMigration{}, errors.New("invalid migration: could not parse the format")
} }
result := reMigration.FindStringSubmatch(m)
migration = model.SchemaMigration{ migration = model.SchemaMigration{
Name: strings.ReplaceAll(result[2], "_", " "), Name: strings.ReplaceAll(result[reMigration.SubexpIndex("Name")], "_", " "),
Provider: result[3], Provider: result[reMigration.SubexpIndex("Provider")],
} }
data, err := migrationsFS.ReadFile(fmt.Sprintf("migrations/%s", m)) data, err := migrationsFS.ReadFile(fmt.Sprintf("migrations/%s", m))
@ -148,22 +148,22 @@ func scanMigration(m string) (migration model.SchemaMigration, err error) {
migration.Query = string(data) migration.Query = string(data)
switch result[4] { switch direction := result[reMigration.SubexpIndex("Direction")]; direction {
case "up": case "up":
migration.Up = true migration.Up = true
case "down": case "down":
migration.Up = false migration.Up = false
default: default:
return model.SchemaMigration{}, fmt.Errorf("invalid migration: value in position 4 '%s' must be up or down", result[4]) return model.SchemaMigration{}, fmt.Errorf("invalid migration: value in Direction group '%s' must be up or down", direction)
} }
migration.Version, _ = strconv.Atoi(result[1]) migration.Version, _ = strconv.Atoi(result[reMigration.SubexpIndex("Version")])
switch migration.Provider { switch migration.Provider {
case providerAll, providerSQLite, providerMySQL, providerPostgres: case providerAll, providerSQLite, providerMySQL, providerPostgres:
break break
default: default:
return model.SchemaMigration{}, fmt.Errorf("invalid migration: value in position 3 '%s' must be all, sqlite, postgres, or mysql", result[3]) return model.SchemaMigration{}, fmt.Errorf("invalid migration: value in Provider group '%s' must be all, sqlite, postgres, or mysql", migration.Provider)
} }
return migration, nil return migration, nil

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS oauth2_par_context;

View File

@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS oauth2_par_context (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
request_id VARCHAR(40) NOT NULL,
client_id VARCHAR(255) NOT NULL,
signature VARCHAR(255) NOT NULL,
requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
scopes TEXT NOT NULL,
audience TEXT NOT NULL,
handled_response_types TEXT NOT NULL,
response_mode TEXT NOT NULL,
response_mode_default TEXT NOT NULL,
revoked BOOLEAN NOT NULL DEFAULT FALSE,
form_data TEXT NOT NULL,
session_data BLOB NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE UNIQUE INDEX oauth2_par_context_signature_key ON oauth2_par_context (signature);

View File

@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS oauth2_par_context (
id SERIAL CONSTRAINT oauth2_par_context_pkey PRIMARY KEY,
request_id VARCHAR(40) NOT NULL,
client_id VARCHAR(255) NOT NULL,
signature VARCHAR(255) NOT NULL,
requested_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
scopes TEXT NOT NULL,
audience TEXT NULL DEFAULT '',
handled_response_types TEXT NOT NULL DEFAULT '',
response_mode TEXT NOT NULL DEFAULT '',
response_mode_default TEXT NOT NULL DEFAULT '',
revoked BOOLEAN NOT NULL DEFAULT FALSE,
form_data TEXT NOT NULL,
session_data BYTEA NOT NULL
);
CREATE UNIQUE INDEX oauth2_par_context_signature_key ON oauth2_par_context (signature);

View File

@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS oauth2_par_context (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
signature VARCHAR(255) NOT NULL,
request_id VARCHAR(40) NOT NULL,
client_id VARCHAR(255) NOT NULL,
requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
scopes TEXT NOT NULL,
audience TEXT NOT NULL,
handled_response_types TEXT NOT NULL,
response_mode TEXT NOT NULL,
response_mode_default TEXT NOT NULL,
revoked BOOLEAN NOT NULL DEFAULT FALSE,
form_data TEXT NOT NULL,
session_data BLOB NOT NULL
);
CREATE UNIQUE INDEX oauth2_par_context_signature_key ON oauth2_par_context (signature);

View File

@ -24,8 +24,8 @@ type Provider interface {
LoadUserInfo(ctx context.Context, username string) (info model.UserInfo, err error) LoadUserInfo(ctx context.Context, username string) (info model.UserInfo, err error)
SaveUserOpaqueIdentifier(ctx context.Context, subject model.UserOpaqueIdentifier) (err error) SaveUserOpaqueIdentifier(ctx context.Context, subject model.UserOpaqueIdentifier) (err error)
LoadUserOpaqueIdentifier(ctx context.Context, opaqueUUID uuid.UUID) (subject *model.UserOpaqueIdentifier, err error) LoadUserOpaqueIdentifier(ctx context.Context, identifier uuid.UUID) (subject *model.UserOpaqueIdentifier, err error)
LoadUserOpaqueIdentifiers(ctx context.Context) (opaqueIDs []model.UserOpaqueIdentifier, err error) LoadUserOpaqueIdentifiers(ctx context.Context) (identifiers []model.UserOpaqueIdentifier, err error)
LoadUserOpaqueIdentifierBySignature(ctx context.Context, service, sectorID, username string) (subject *model.UserOpaqueIdentifier, err error) LoadUserOpaqueIdentifierBySignature(ctx context.Context, service, sectorID, username string) (subject *model.UserOpaqueIdentifier, err error)
SaveIdentityVerification(ctx context.Context, verification model.IdentityVerification) (err error) SaveIdentityVerification(ctx context.Context, verification model.IdentityVerification) (err error)
@ -70,6 +70,10 @@ type Provider interface {
DeactivateOAuth2SessionByRequestID(ctx context.Context, sessionType OAuth2SessionType, requestID string) (err error) DeactivateOAuth2SessionByRequestID(ctx context.Context, sessionType OAuth2SessionType, requestID string) (err error)
LoadOAuth2Session(ctx context.Context, sessionType OAuth2SessionType, signature string) (session *model.OAuth2Session, err error) LoadOAuth2Session(ctx context.Context, sessionType OAuth2SessionType, signature string) (session *model.OAuth2Session, err error)
SaveOAuth2PARContext(ctx context.Context, par model.OAuth2PARContext) (err error)
LoadOAuth2PARContext(ctx context.Context, signature string) (par *model.OAuth2PARContext, err error)
RevokeOAuth2PARContext(ctx context.Context, signature string) (err error)
SaveOAuth2BlacklistedJTI(ctx context.Context, blacklistedJTI model.OAuth2BlacklistedJTI) (err error) SaveOAuth2BlacklistedJTI(ctx context.Context, blacklistedJTI model.OAuth2BlacklistedJTI) (err error)
LoadOAuth2BlacklistedJTI(ctx context.Context, signature string) (blacklistedJTI *model.OAuth2BlacklistedJTI, err error) LoadOAuth2BlacklistedJTI(ctx context.Context, signature string) (blacklistedJTI *model.OAuth2BlacklistedJTI, err error)

View File

@ -73,6 +73,13 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
sqlSelectUserOpaqueIdentifiers: fmt.Sprintf(queryFmtSelectUserOpaqueIdentifiers, tableUserOpaqueIdentifier), sqlSelectUserOpaqueIdentifiers: fmt.Sprintf(queryFmtSelectUserOpaqueIdentifiers, tableUserOpaqueIdentifier),
sqlSelectUserOpaqueIdentifierBySignature: fmt.Sprintf(queryFmtSelectUserOpaqueIdentifierBySignature, tableUserOpaqueIdentifier), sqlSelectUserOpaqueIdentifierBySignature: fmt.Sprintf(queryFmtSelectUserOpaqueIdentifierBySignature, tableUserOpaqueIdentifier),
sqlUpsertOAuth2BlacklistedJTI: fmt.Sprintf(queryFmtUpsertOAuth2BlacklistedJTI, tableOAuth2BlacklistedJTI),
sqlSelectOAuth2BlacklistedJTI: fmt.Sprintf(queryFmtSelectOAuth2BlacklistedJTI, tableOAuth2BlacklistedJTI),
sqlInsertOAuth2PARContext: fmt.Sprintf(queryFmtInsertOAuth2PARContext, tableOAuth2PARContext),
sqlSelectOAuth2PARContext: fmt.Sprintf(queryFmtSelectOAuth2PARContext, tableOAuth2PARContext),
sqlRevokeOAuth2PARContext: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2PARContext),
sqlInsertOAuth2ConsentPreConfiguration: fmt.Sprintf(queryFmtInsertOAuth2ConsentPreConfiguration, tableOAuth2ConsentPreConfiguration), sqlInsertOAuth2ConsentPreConfiguration: fmt.Sprintf(queryFmtInsertOAuth2ConsentPreConfiguration, tableOAuth2ConsentPreConfiguration),
sqlSelectOAuth2ConsentPreConfigurations: fmt.Sprintf(queryFmtSelectOAuth2ConsentPreConfigurations, tableOAuth2ConsentPreConfiguration), sqlSelectOAuth2ConsentPreConfigurations: fmt.Sprintf(queryFmtSelectOAuth2ConsentPreConfigurations, tableOAuth2ConsentPreConfiguration),
@ -82,13 +89,6 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
sqlUpdateOAuth2ConsentSessionGranted: fmt.Sprintf(queryFmtUpdateOAuth2ConsentSessionGranted, tableOAuth2ConsentSession), sqlUpdateOAuth2ConsentSessionGranted: fmt.Sprintf(queryFmtUpdateOAuth2ConsentSessionGranted, tableOAuth2ConsentSession),
sqlSelectOAuth2ConsentSessionByChallengeID: fmt.Sprintf(queryFmtSelectOAuth2ConsentSessionByChallengeID, tableOAuth2ConsentSession), sqlSelectOAuth2ConsentSessionByChallengeID: fmt.Sprintf(queryFmtSelectOAuth2ConsentSessionByChallengeID, tableOAuth2ConsentSession),
sqlInsertOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2AuthorizeCodeSession),
sqlSelectOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2AuthorizeCodeSession),
sqlRevokeOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2AuthorizeCodeSession),
sqlRevokeOAuth2AuthorizeCodeSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2AuthorizeCodeSession),
sqlDeactivateOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2AuthorizeCodeSession),
sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2AuthorizeCodeSession),
sqlInsertOAuth2AccessTokenSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2AccessTokenSession), sqlInsertOAuth2AccessTokenSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2AccessTokenSession),
sqlSelectOAuth2AccessTokenSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2AccessTokenSession), sqlSelectOAuth2AccessTokenSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2AccessTokenSession),
sqlRevokeOAuth2AccessTokenSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2AccessTokenSession), sqlRevokeOAuth2AccessTokenSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2AccessTokenSession),
@ -96,19 +96,12 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
sqlDeactivateOAuth2AccessTokenSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2AccessTokenSession), sqlDeactivateOAuth2AccessTokenSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2AccessTokenSession),
sqlDeactivateOAuth2AccessTokenSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2AccessTokenSession), sqlDeactivateOAuth2AccessTokenSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2AccessTokenSession),
sqlInsertOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2RefreshTokenSession), sqlInsertOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2AuthorizeCodeSession),
sqlSelectOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2RefreshTokenSession), sqlSelectOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2AuthorizeCodeSession),
sqlRevokeOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2RefreshTokenSession), sqlRevokeOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2AuthorizeCodeSession),
sqlRevokeOAuth2RefreshTokenSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2RefreshTokenSession), sqlRevokeOAuth2AuthorizeCodeSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2AuthorizeCodeSession),
sqlDeactivateOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2RefreshTokenSession), sqlDeactivateOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2AuthorizeCodeSession),
sqlDeactivateOAuth2RefreshTokenSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2RefreshTokenSession), sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2AuthorizeCodeSession),
sqlInsertOAuth2PKCERequestSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2PKCERequestSession),
sqlSelectOAuth2PKCERequestSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2PKCERequestSession),
sqlRevokeOAuth2PKCERequestSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2PKCERequestSession),
sqlRevokeOAuth2PKCERequestSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2PKCERequestSession),
sqlDeactivateOAuth2PKCERequestSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2PKCERequestSession),
sqlDeactivateOAuth2PKCERequestSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2PKCERequestSession),
sqlInsertOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2OpenIDConnectSession), sqlInsertOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2OpenIDConnectSession),
sqlSelectOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2OpenIDConnectSession), sqlSelectOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2OpenIDConnectSession),
@ -117,8 +110,19 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
sqlDeactivateOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2OpenIDConnectSession), sqlDeactivateOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2OpenIDConnectSession),
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2OpenIDConnectSession), sqlDeactivateOAuth2OpenIDConnectSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2OpenIDConnectSession),
sqlUpsertOAuth2BlacklistedJTI: fmt.Sprintf(queryFmtUpsertOAuth2BlacklistedJTI, tableOAuth2BlacklistedJTI), sqlInsertOAuth2PKCERequestSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2PKCERequestSession),
sqlSelectOAuth2BlacklistedJTI: fmt.Sprintf(queryFmtSelectOAuth2BlacklistedJTI, tableOAuth2BlacklistedJTI), sqlSelectOAuth2PKCERequestSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2PKCERequestSession),
sqlRevokeOAuth2PKCERequestSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2PKCERequestSession),
sqlRevokeOAuth2PKCERequestSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2PKCERequestSession),
sqlDeactivateOAuth2PKCERequestSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2PKCERequestSession),
sqlDeactivateOAuth2PKCERequestSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2PKCERequestSession),
sqlInsertOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2RefreshTokenSession),
sqlSelectOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2RefreshTokenSession),
sqlRevokeOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2RefreshTokenSession),
sqlRevokeOAuth2RefreshTokenSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2RefreshTokenSession),
sqlDeactivateOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2RefreshTokenSession),
sqlDeactivateOAuth2RefreshTokenSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2RefreshTokenSession),
sqlInsertMigration: fmt.Sprintf(queryFmtInsertMigration, tableMigrations), sqlInsertMigration: fmt.Sprintf(queryFmtInsertMigration, tableMigrations),
sqlSelectMigrations: fmt.Sprintf(queryFmtSelectMigrations, tableMigrations), sqlSelectMigrations: fmt.Sprintf(queryFmtSelectMigrations, tableMigrations),
@ -233,13 +237,18 @@ type SQLProvider struct {
sqlDeactivateOAuth2AccessTokenSession string sqlDeactivateOAuth2AccessTokenSession string
sqlDeactivateOAuth2AccessTokenSessionByRequestID string sqlDeactivateOAuth2AccessTokenSessionByRequestID string
// Table: oauth2_refresh_token_session. // Table: oauth2_openid_connect_session.
sqlInsertOAuth2RefreshTokenSession string sqlInsertOAuth2OpenIDConnectSession string
sqlSelectOAuth2RefreshTokenSession string sqlSelectOAuth2OpenIDConnectSession string
sqlRevokeOAuth2RefreshTokenSession string sqlRevokeOAuth2OpenIDConnectSession string
sqlRevokeOAuth2RefreshTokenSessionByRequestID string sqlRevokeOAuth2OpenIDConnectSessionByRequestID string
sqlDeactivateOAuth2RefreshTokenSession string sqlDeactivateOAuth2OpenIDConnectSession string
sqlDeactivateOAuth2RefreshTokenSessionByRequestID string sqlDeactivateOAuth2OpenIDConnectSessionByRequestID string
// Table: oauth2_par_context.
sqlInsertOAuth2PARContext string
sqlSelectOAuth2PARContext string
sqlRevokeOAuth2PARContext string
// Table: oauth2_pkce_request_session. // Table: oauth2_pkce_request_session.
sqlInsertOAuth2PKCERequestSession string sqlInsertOAuth2PKCERequestSession string
@ -249,13 +258,13 @@ type SQLProvider struct {
sqlDeactivateOAuth2PKCERequestSession string sqlDeactivateOAuth2PKCERequestSession string
sqlDeactivateOAuth2PKCERequestSessionByRequestID string sqlDeactivateOAuth2PKCERequestSessionByRequestID string
// Table: oauth2_openid_connect_session. // Table: oauth2_refresh_token_session.
sqlInsertOAuth2OpenIDConnectSession string sqlInsertOAuth2RefreshTokenSession string
sqlSelectOAuth2OpenIDConnectSession string sqlSelectOAuth2RefreshTokenSession string
sqlRevokeOAuth2OpenIDConnectSession string sqlRevokeOAuth2RefreshTokenSession string
sqlRevokeOAuth2OpenIDConnectSessionByRequestID string sqlRevokeOAuth2RefreshTokenSessionByRequestID string
sqlDeactivateOAuth2OpenIDConnectSession string sqlDeactivateOAuth2RefreshTokenSession string
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID string sqlDeactivateOAuth2RefreshTokenSessionByRequestID string
sqlUpsertOAuth2BlacklistedJTI string sqlUpsertOAuth2BlacklistedJTI string
sqlSelectOAuth2BlacklistedJTI string sqlSelectOAuth2BlacklistedJTI string
@ -348,32 +357,32 @@ func (p *SQLProvider) Rollback(ctx context.Context) (err error) {
} }
// SaveUserOpaqueIdentifier saves a new opaque user identifier to the database. // SaveUserOpaqueIdentifier saves a new opaque user identifier to the database.
func (p *SQLProvider) SaveUserOpaqueIdentifier(ctx context.Context, opaqueID model.UserOpaqueIdentifier) (err error) { func (p *SQLProvider) SaveUserOpaqueIdentifier(ctx context.Context, subject model.UserOpaqueIdentifier) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlInsertUserOpaqueIdentifier, opaqueID.Service, opaqueID.SectorID, opaqueID.Username, opaqueID.Identifier); err != nil { if _, err = p.db.ExecContext(ctx, p.sqlInsertUserOpaqueIdentifier, subject.Service, subject.SectorID, subject.Username, subject.Identifier); err != nil {
return fmt.Errorf("error inserting user opaque id for user '%s' with opaque id '%s': %w", opaqueID.Username, opaqueID.Identifier.String(), err) return fmt.Errorf("error inserting user opaque id for user '%s' with opaque id '%s': %w", subject.Username, subject.Identifier.String(), err)
} }
return nil return nil
} }
// LoadUserOpaqueIdentifier selects an opaque user identifier from the database. // LoadUserOpaqueIdentifier selects an opaque user identifier from the database.
func (p *SQLProvider) LoadUserOpaqueIdentifier(ctx context.Context, opaqueUUID uuid.UUID) (opaqueID *model.UserOpaqueIdentifier, err error) { func (p *SQLProvider) LoadUserOpaqueIdentifier(ctx context.Context, identifier uuid.UUID) (subject *model.UserOpaqueIdentifier, err error) {
opaqueID = &model.UserOpaqueIdentifier{} subject = &model.UserOpaqueIdentifier{}
if err = p.db.GetContext(ctx, opaqueID, p.sqlSelectUserOpaqueIdentifier, opaqueUUID); err != nil { if err = p.db.GetContext(ctx, subject, p.sqlSelectUserOpaqueIdentifier, identifier); err != nil {
switch { switch {
case errors.Is(err, sql.ErrNoRows): case errors.Is(err, sql.ErrNoRows):
return nil, nil return nil, nil
default: default:
return nil, fmt.Errorf("error selecting user opaque id with value '%s': %w", opaqueUUID.String(), err) return nil, fmt.Errorf("error selecting user opaque id with value '%s': %w", identifier.String(), err)
} }
} }
return opaqueID, nil return subject, nil
} }
// LoadUserOpaqueIdentifiers selects an opaque user identifiers from the database. // LoadUserOpaqueIdentifiers selects an opaque user identifiers from the database.
func (p *SQLProvider) LoadUserOpaqueIdentifiers(ctx context.Context) (opaqueIDs []model.UserOpaqueIdentifier, err error) { func (p *SQLProvider) LoadUserOpaqueIdentifiers(ctx context.Context) (identifiers []model.UserOpaqueIdentifier, err error) {
var rows *sqlx.Rows var rows *sqlx.Rows
if rows, err = p.db.QueryxContext(ctx, p.sqlSelectUserOpaqueIdentifiers); err != nil { if rows, err = p.db.QueryxContext(ctx, p.sqlSelectUserOpaqueIdentifiers); err != nil {
@ -389,17 +398,17 @@ func (p *SQLProvider) LoadUserOpaqueIdentifiers(ctx context.Context) (opaqueIDs
return nil, fmt.Errorf("error selecting user opaque identifiers: error scanning row: %w", err) return nil, fmt.Errorf("error selecting user opaque identifiers: error scanning row: %w", err)
} }
opaqueIDs = append(opaqueIDs, *opaqueID) identifiers = append(identifiers, *opaqueID)
} }
return opaqueIDs, nil return identifiers, nil
} }
// LoadUserOpaqueIdentifierBySignature selects an opaque user identifier from the database given a service name, sector id, and username. // LoadUserOpaqueIdentifierBySignature selects an opaque user identifier from the database given a service name, sector id, and username.
func (p *SQLProvider) LoadUserOpaqueIdentifierBySignature(ctx context.Context, service, sectorID, username string) (opaqueID *model.UserOpaqueIdentifier, err error) { func (p *SQLProvider) LoadUserOpaqueIdentifierBySignature(ctx context.Context, service, sectorID, username string) (subject *model.UserOpaqueIdentifier, err error) {
opaqueID = &model.UserOpaqueIdentifier{} subject = &model.UserOpaqueIdentifier{}
if err = p.db.GetContext(ctx, opaqueID, p.sqlSelectUserOpaqueIdentifierBySignature, service, sectorID, username); err != nil { if err = p.db.GetContext(ctx, subject, p.sqlSelectUserOpaqueIdentifierBySignature, service, sectorID, username); err != nil {
switch { switch {
case errors.Is(err, sql.ErrNoRows): case errors.Is(err, sql.ErrNoRows):
return nil, nil return nil, nil
@ -408,7 +417,7 @@ func (p *SQLProvider) LoadUserOpaqueIdentifierBySignature(ctx context.Context, s
} }
} }
return opaqueID, nil return subject, nil
} }
// SaveOAuth2ConsentSession inserts an OAuth2.0 consent session. // SaveOAuth2ConsentSession inserts an OAuth2.0 consent session.
@ -505,22 +514,22 @@ func (p *SQLProvider) SaveOAuth2Session(ctx context.Context, sessionType OAuth2S
var query string var query string
switch sessionType { switch sessionType {
case OAuth2SessionTypeAuthorizeCode:
query = p.sqlInsertOAuth2AuthorizeCodeSession
case OAuth2SessionTypeAccessToken: case OAuth2SessionTypeAccessToken:
query = p.sqlInsertOAuth2AccessTokenSession query = p.sqlInsertOAuth2AccessTokenSession
case OAuth2SessionTypeRefreshToken: case OAuth2SessionTypeAuthorizeCode:
query = p.sqlInsertOAuth2RefreshTokenSession query = p.sqlInsertOAuth2AuthorizeCodeSession
case OAuth2SessionTypePKCEChallenge:
query = p.sqlInsertOAuth2PKCERequestSession
case OAuth2SessionTypeOpenIDConnect: case OAuth2SessionTypeOpenIDConnect:
query = p.sqlInsertOAuth2OpenIDConnectSession query = p.sqlInsertOAuth2OpenIDConnectSession
case OAuth2SessionTypePKCEChallenge:
query = p.sqlInsertOAuth2PKCERequestSession
case OAuth2SessionTypeRefreshToken:
query = p.sqlInsertOAuth2RefreshTokenSession
default: default:
return fmt.Errorf("error inserting oauth2 session for subject '%s' and request id '%s': unknown oauth2 session type '%s'", session.Subject, session.RequestID, sessionType) return fmt.Errorf("error inserting oauth2 session for subject '%s' and request id '%s': unknown oauth2 session type '%s'", session.Subject, session.RequestID, sessionType)
} }
if session.Session, err = p.encrypt(session.Session); err != nil { if session.Session, err = p.encrypt(session.Session); err != nil {
return fmt.Errorf("error encrypting the oauth2 %s session data for subject '%s' and request id '%s' and challenge id '%s': %w", sessionType, session.Subject, session.RequestID, session.ChallengeID.String(), err) return fmt.Errorf("error encrypting oauth2 %s session data for subject '%s' and request id '%s' and challenge id '%s': %w", sessionType, session.Subject, session.RequestID, session.ChallengeID.String(), err)
} }
_, err = p.db.ExecContext(ctx, query, _, err = p.db.ExecContext(ctx, query,
@ -541,16 +550,16 @@ func (p *SQLProvider) RevokeOAuth2Session(ctx context.Context, sessionType OAuth
var query string var query string
switch sessionType { switch sessionType {
case OAuth2SessionTypeAuthorizeCode:
query = p.sqlRevokeOAuth2AuthorizeCodeSession
case OAuth2SessionTypeAccessToken: case OAuth2SessionTypeAccessToken:
query = p.sqlRevokeOAuth2AccessTokenSession query = p.sqlRevokeOAuth2AccessTokenSession
case OAuth2SessionTypeRefreshToken: case OAuth2SessionTypeAuthorizeCode:
query = p.sqlRevokeOAuth2RefreshTokenSession query = p.sqlRevokeOAuth2AuthorizeCodeSession
case OAuth2SessionTypePKCEChallenge:
query = p.sqlRevokeOAuth2PKCERequestSession
case OAuth2SessionTypeOpenIDConnect: case OAuth2SessionTypeOpenIDConnect:
query = p.sqlRevokeOAuth2OpenIDConnectSession query = p.sqlRevokeOAuth2OpenIDConnectSession
case OAuth2SessionTypePKCEChallenge:
query = p.sqlRevokeOAuth2PKCERequestSession
case OAuth2SessionTypeRefreshToken:
query = p.sqlRevokeOAuth2RefreshTokenSession
default: default:
return fmt.Errorf("error revoking oauth2 session with signature '%s': unknown oauth2 session type '%s'", signature, sessionType.String()) return fmt.Errorf("error revoking oauth2 session with signature '%s': unknown oauth2 session type '%s'", signature, sessionType.String())
} }
@ -567,16 +576,16 @@ func (p *SQLProvider) RevokeOAuth2SessionByRequestID(ctx context.Context, sessio
var query string var query string
switch sessionType { switch sessionType {
case OAuth2SessionTypeAuthorizeCode:
query = p.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID
case OAuth2SessionTypeAccessToken: case OAuth2SessionTypeAccessToken:
query = p.sqlRevokeOAuth2AccessTokenSessionByRequestID query = p.sqlRevokeOAuth2AccessTokenSessionByRequestID
case OAuth2SessionTypeRefreshToken: case OAuth2SessionTypeAuthorizeCode:
query = p.sqlRevokeOAuth2RefreshTokenSessionByRequestID query = p.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID
case OAuth2SessionTypePKCEChallenge:
query = p.sqlRevokeOAuth2PKCERequestSessionByRequestID
case OAuth2SessionTypeOpenIDConnect: case OAuth2SessionTypeOpenIDConnect:
query = p.sqlRevokeOAuth2OpenIDConnectSessionByRequestID query = p.sqlRevokeOAuth2OpenIDConnectSessionByRequestID
case OAuth2SessionTypePKCEChallenge:
query = p.sqlRevokeOAuth2PKCERequestSessionByRequestID
case OAuth2SessionTypeRefreshToken:
query = p.sqlRevokeOAuth2RefreshTokenSessionByRequestID
default: default:
return fmt.Errorf("error revoking oauth2 session with request id '%s': unknown oauth2 session type '%s'", requestID, sessionType.String()) return fmt.Errorf("error revoking oauth2 session with request id '%s': unknown oauth2 session type '%s'", requestID, sessionType.String())
} }
@ -593,16 +602,16 @@ func (p *SQLProvider) DeactivateOAuth2Session(ctx context.Context, sessionType O
var query string var query string
switch sessionType { switch sessionType {
case OAuth2SessionTypeAuthorizeCode:
query = p.sqlDeactivateOAuth2AuthorizeCodeSession
case OAuth2SessionTypeAccessToken: case OAuth2SessionTypeAccessToken:
query = p.sqlDeactivateOAuth2AccessTokenSession query = p.sqlDeactivateOAuth2AccessTokenSession
case OAuth2SessionTypeRefreshToken: case OAuth2SessionTypeAuthorizeCode:
query = p.sqlDeactivateOAuth2RefreshTokenSession query = p.sqlDeactivateOAuth2AuthorizeCodeSession
case OAuth2SessionTypePKCEChallenge:
query = p.sqlDeactivateOAuth2PKCERequestSession
case OAuth2SessionTypeOpenIDConnect: case OAuth2SessionTypeOpenIDConnect:
query = p.sqlDeactivateOAuth2OpenIDConnectSession query = p.sqlDeactivateOAuth2OpenIDConnectSession
case OAuth2SessionTypePKCEChallenge:
query = p.sqlDeactivateOAuth2PKCERequestSession
case OAuth2SessionTypeRefreshToken:
query = p.sqlDeactivateOAuth2RefreshTokenSession
default: default:
return fmt.Errorf("error deactivating oauth2 session with signature '%s': unknown oauth2 session type '%s'", signature, sessionType.String()) return fmt.Errorf("error deactivating oauth2 session with signature '%s': unknown oauth2 session type '%s'", signature, sessionType.String())
} }
@ -619,16 +628,16 @@ func (p *SQLProvider) DeactivateOAuth2SessionByRequestID(ctx context.Context, se
var query string var query string
switch sessionType { switch sessionType {
case OAuth2SessionTypeAuthorizeCode:
query = p.sqlDeactivateOAuth2AuthorizeCodeSession
case OAuth2SessionTypeAccessToken: case OAuth2SessionTypeAccessToken:
query = p.sqlDeactivateOAuth2AccessTokenSessionByRequestID query = p.sqlDeactivateOAuth2AccessTokenSessionByRequestID
case OAuth2SessionTypeRefreshToken: case OAuth2SessionTypeAuthorizeCode:
query = p.sqlDeactivateOAuth2RefreshTokenSessionByRequestID query = p.sqlDeactivateOAuth2AuthorizeCodeSession
case OAuth2SessionTypePKCEChallenge:
query = p.sqlDeactivateOAuth2PKCERequestSessionByRequestID
case OAuth2SessionTypeOpenIDConnect: case OAuth2SessionTypeOpenIDConnect:
query = p.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID query = p.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID
case OAuth2SessionTypePKCEChallenge:
query = p.sqlDeactivateOAuth2PKCERequestSessionByRequestID
case OAuth2SessionTypeRefreshToken:
query = p.sqlDeactivateOAuth2RefreshTokenSessionByRequestID
default: default:
return fmt.Errorf("error deactivating oauth2 session with request id '%s': unknown oauth2 session type '%s'", requestID, sessionType.String()) return fmt.Errorf("error deactivating oauth2 session with request id '%s': unknown oauth2 session type '%s'", requestID, sessionType.String())
} }
@ -645,16 +654,16 @@ func (p *SQLProvider) LoadOAuth2Session(ctx context.Context, sessionType OAuth2S
var query string var query string
switch sessionType { switch sessionType {
case OAuth2SessionTypeAuthorizeCode:
query = p.sqlSelectOAuth2AuthorizeCodeSession
case OAuth2SessionTypeAccessToken: case OAuth2SessionTypeAccessToken:
query = p.sqlSelectOAuth2AccessTokenSession query = p.sqlSelectOAuth2AccessTokenSession
case OAuth2SessionTypeRefreshToken: case OAuth2SessionTypeAuthorizeCode:
query = p.sqlSelectOAuth2RefreshTokenSession query = p.sqlSelectOAuth2AuthorizeCodeSession
case OAuth2SessionTypePKCEChallenge:
query = p.sqlSelectOAuth2PKCERequestSession
case OAuth2SessionTypeOpenIDConnect: case OAuth2SessionTypeOpenIDConnect:
query = p.sqlSelectOAuth2OpenIDConnectSession query = p.sqlSelectOAuth2OpenIDConnectSession
case OAuth2SessionTypePKCEChallenge:
query = p.sqlSelectOAuth2PKCERequestSession
case OAuth2SessionTypeRefreshToken:
query = p.sqlSelectOAuth2RefreshTokenSession
default: default:
return nil, fmt.Errorf("error selecting oauth2 session: unknown oauth2 session type '%s'", sessionType.String()) return nil, fmt.Errorf("error selecting oauth2 session: unknown oauth2 session type '%s'", sessionType.String())
} }
@ -672,6 +681,45 @@ func (p *SQLProvider) LoadOAuth2Session(ctx context.Context, sessionType OAuth2S
return session, nil return session, nil
} }
// SaveOAuth2PARContext save a OAuth2PARContext to the database.
func (p *SQLProvider) SaveOAuth2PARContext(ctx context.Context, par model.OAuth2PARContext) (err error) {
if par.Session, err = p.encrypt(par.Session); err != nil {
return fmt.Errorf("error encrypting oauth2 pushed authorization request context data for with signature '%s' and request id '%s': %w", par.Signature, par.RequestID, err)
}
if _, err = p.db.ExecContext(ctx, p.sqlInsertOAuth2PARContext,
par.Signature, par.RequestID, par.ClientID, par.RequestedAt, par.Scopes, par.Audience, par.HandledResponseTypes,
par.ResponseMode, par.DefaultResponseMode, par.Revoked, par.Form, par.Session); err != nil {
return fmt.Errorf("error inserting oauth2 pushed authorization request context data for with signature '%s' and request id '%s': %w", par.Signature, par.RequestID, err)
}
return nil
}
// LoadOAuth2PARContext loads a OAuth2PARContext from the database.
func (p *SQLProvider) LoadOAuth2PARContext(ctx context.Context, signature string) (par *model.OAuth2PARContext, err error) {
par = &model.OAuth2PARContext{}
if err = p.db.GetContext(ctx, par, p.sqlSelectOAuth2PARContext, signature); err != nil {
return nil, fmt.Errorf("error selecting oauth2 pushed authorization request context with signature '%s': %w", signature, err)
}
if par.Session, err = p.decrypt(par.Session); err != nil {
return nil, fmt.Errorf("error decrypting oauth2 oauth2 pushed authorization request context data with signature '%s' and request id '%s': %w", signature, par.RequestID, err)
}
return par, nil
}
// RevokeOAuth2PARContext marks a OAuth2PARContext as revoked in the database.
func (p *SQLProvider) RevokeOAuth2PARContext(ctx context.Context, signature string) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlRevokeOAuth2PARContext, signature); err != nil {
return fmt.Errorf("error revoking oauth2 pushed authorization request context with signature '%s': %w", signature, err)
}
return nil
}
// SaveOAuth2BlacklistedJTI saves a OAuth2BlacklistedJTI to the database. // SaveOAuth2BlacklistedJTI saves a OAuth2BlacklistedJTI to the database.
func (p *SQLProvider) SaveOAuth2BlacklistedJTI(ctx context.Context, blacklistedJTI model.OAuth2BlacklistedJTI) (err error) { func (p *SQLProvider) SaveOAuth2BlacklistedJTI(ctx context.Context, blacklistedJTI model.OAuth2BlacklistedJTI) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlUpsertOAuth2BlacklistedJTI, blacklistedJTI.Signature, blacklistedJTI.ExpiresAt); err != nil { if _, err = p.db.ExecContext(ctx, p.sqlUpsertOAuth2BlacklistedJTI, blacklistedJTI.Signature, blacklistedJTI.ExpiresAt); err != nil {
@ -771,7 +819,7 @@ func (p *SQLProvider) FindIdentityVerification(ctx context.Context, jti string)
// SaveTOTPConfiguration save a TOTP configuration of a given user in the database. // SaveTOTPConfiguration save a TOTP configuration of a given user in the database.
func (p *SQLProvider) SaveTOTPConfiguration(ctx context.Context, config model.TOTPConfiguration) (err error) { func (p *SQLProvider) SaveTOTPConfiguration(ctx context.Context, config model.TOTPConfiguration) (err error) {
if config.Secret, err = p.encrypt(config.Secret); err != nil { if config.Secret, err = p.encrypt(config.Secret); err != nil {
return fmt.Errorf("error encrypting the TOTP configuration secret for user '%s': %w", config.Username, err) return fmt.Errorf("error encrypting TOTP configuration secret for user '%s': %w", config.Username, err)
} }
if _, err = p.db.ExecContext(ctx, p.sqlUpsertTOTPConfig, if _, err = p.db.ExecContext(ctx, p.sqlUpsertTOTPConfig,
@ -815,7 +863,7 @@ func (p *SQLProvider) LoadTOTPConfiguration(ctx context.Context, username string
} }
if config.Secret, err = p.decrypt(config.Secret); err != nil { if config.Secret, err = p.decrypt(config.Secret); err != nil {
return nil, fmt.Errorf("error decrypting the TOTP secret for user '%s': %w", username, err) return nil, fmt.Errorf("error decrypting TOTP secret for user '%s': %w", username, err)
} }
return config, nil return config, nil
@ -870,7 +918,7 @@ func (p *SQLProvider) LoadWebauthnUser(ctx context.Context, rpid, username strin
// SaveWebauthnDevice saves a registered Webauthn device. // SaveWebauthnDevice saves a registered Webauthn device.
func (p *SQLProvider) SaveWebauthnDevice(ctx context.Context, device model.WebauthnDevice) (err error) { func (p *SQLProvider) SaveWebauthnDevice(ctx context.Context, device model.WebauthnDevice) (err error) {
if device.PublicKey, err = p.encrypt(device.PublicKey); err != nil { if device.PublicKey, err = p.encrypt(device.PublicKey); err != nil {
return fmt.Errorf("error encrypting the Webauthn device public key for user '%s' kid '%x': %w", device.Username, device.KID, err) return fmt.Errorf("error encrypting Webauthn device public key for user '%s' kid '%x': %w", device.Username, device.KID, err)
} }
if _, err = p.db.ExecContext(ctx, p.sqlInsertWebauthnDevice, if _, err = p.db.ExecContext(ctx, p.sqlInsertWebauthnDevice,

View File

@ -92,13 +92,6 @@ func NewPostgreSQLProvider(config *schema.Configuration, caCertPool *x509.CertPo
provider.sqlUpdateOAuth2ConsentSessionGranted = provider.db.Rebind(provider.sqlUpdateOAuth2ConsentSessionGranted) provider.sqlUpdateOAuth2ConsentSessionGranted = provider.db.Rebind(provider.sqlUpdateOAuth2ConsentSessionGranted)
provider.sqlSelectOAuth2ConsentSessionByChallengeID = provider.db.Rebind(provider.sqlSelectOAuth2ConsentSessionByChallengeID) provider.sqlSelectOAuth2ConsentSessionByChallengeID = provider.db.Rebind(provider.sqlSelectOAuth2ConsentSessionByChallengeID)
provider.sqlInsertOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlInsertOAuth2AuthorizeCodeSession)
provider.sqlRevokeOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlRevokeOAuth2AuthorizeCodeSession)
provider.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID)
provider.sqlDeactivateOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlDeactivateOAuth2AuthorizeCodeSession)
provider.sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID)
provider.sqlSelectOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlSelectOAuth2AuthorizeCodeSession)
provider.sqlInsertOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlInsertOAuth2AccessTokenSession) provider.sqlInsertOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlInsertOAuth2AccessTokenSession)
provider.sqlRevokeOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlRevokeOAuth2AccessTokenSession) provider.sqlRevokeOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlRevokeOAuth2AccessTokenSession)
provider.sqlRevokeOAuth2AccessTokenSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2AccessTokenSessionByRequestID) provider.sqlRevokeOAuth2AccessTokenSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2AccessTokenSessionByRequestID)
@ -106,12 +99,23 @@ func NewPostgreSQLProvider(config *schema.Configuration, caCertPool *x509.CertPo
provider.sqlDeactivateOAuth2AccessTokenSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2AccessTokenSessionByRequestID) provider.sqlDeactivateOAuth2AccessTokenSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2AccessTokenSessionByRequestID)
provider.sqlSelectOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlSelectOAuth2AccessTokenSession) provider.sqlSelectOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlSelectOAuth2AccessTokenSession)
provider.sqlInsertOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlInsertOAuth2RefreshTokenSession) provider.sqlInsertOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlInsertOAuth2AuthorizeCodeSession)
provider.sqlRevokeOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSession) provider.sqlRevokeOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlRevokeOAuth2AuthorizeCodeSession)
provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID) provider.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID)
provider.sqlDeactivateOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSession) provider.sqlDeactivateOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlDeactivateOAuth2AuthorizeCodeSession)
provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID) provider.sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID)
provider.sqlSelectOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlSelectOAuth2RefreshTokenSession) provider.sqlSelectOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlSelectOAuth2AuthorizeCodeSession)
provider.sqlInsertOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlInsertOAuth2OpenIDConnectSession)
provider.sqlRevokeOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlRevokeOAuth2OpenIDConnectSession)
provider.sqlRevokeOAuth2OpenIDConnectSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2OpenIDConnectSessionByRequestID)
provider.sqlDeactivateOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlDeactivateOAuth2OpenIDConnectSession)
provider.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID)
provider.sqlSelectOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlSelectOAuth2OpenIDConnectSession)
provider.sqlInsertOAuth2PARContext = provider.db.Rebind(provider.sqlInsertOAuth2PARContext)
provider.sqlRevokeOAuth2PARContext = provider.db.Rebind(provider.sqlRevokeOAuth2PARContext)
provider.sqlSelectOAuth2PARContext = provider.db.Rebind(provider.sqlSelectOAuth2PARContext)
provider.sqlInsertOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlInsertOAuth2PKCERequestSession) provider.sqlInsertOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlInsertOAuth2PKCERequestSession)
provider.sqlRevokeOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlRevokeOAuth2PKCERequestSession) provider.sqlRevokeOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlRevokeOAuth2PKCERequestSession)
@ -120,12 +124,12 @@ func NewPostgreSQLProvider(config *schema.Configuration, caCertPool *x509.CertPo
provider.sqlDeactivateOAuth2PKCERequestSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2PKCERequestSessionByRequestID) provider.sqlDeactivateOAuth2PKCERequestSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2PKCERequestSessionByRequestID)
provider.sqlSelectOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlSelectOAuth2PKCERequestSession) provider.sqlSelectOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlSelectOAuth2PKCERequestSession)
provider.sqlInsertOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlInsertOAuth2OpenIDConnectSession) provider.sqlInsertOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlInsertOAuth2RefreshTokenSession)
provider.sqlRevokeOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlRevokeOAuth2OpenIDConnectSession) provider.sqlRevokeOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSession)
provider.sqlRevokeOAuth2OpenIDConnectSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2OpenIDConnectSessionByRequestID) provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID)
provider.sqlDeactivateOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlDeactivateOAuth2OpenIDConnectSession) provider.sqlDeactivateOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSession)
provider.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID) provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID)
provider.sqlSelectOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlSelectOAuth2OpenIDConnectSession) provider.sqlSelectOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlSelectOAuth2RefreshTokenSession)
provider.sqlSelectOAuth2BlacklistedJTI = provider.db.Rebind(provider.sqlSelectOAuth2BlacklistedJTI) provider.sqlSelectOAuth2BlacklistedJTI = provider.db.Rebind(provider.sqlSelectOAuth2BlacklistedJTI)

View File

@ -327,6 +327,19 @@ const (
SET active = FALSE SET active = FALSE
WHERE request_id = ?;` WHERE request_id = ?;`
queryFmtSelectOAuth2PARContext = `
SELECT id, signature, request_id, client_id, requested_at, scopes, audience,
handled_response_types, response_mode, response_mode_default, revoked,
form_data, session_data
FROM %s
WHERE signature = ? AND revoked = FALSE;`
queryFmtInsertOAuth2PARContext = `
INSERT INTO %s (signature, request_id, client_id, requested_at, scopes, audience,
handled_response_types, response_mode, response_mode_default, revoked,
form_data, session_data)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`
queryFmtSelectOAuth2BlacklistedJTI = ` queryFmtSelectOAuth2BlacklistedJTI = `
SELECT id, signature, expires_at SELECT id, signature, expires_at
FROM %s FROM %s

View File

@ -2,7 +2,7 @@
version: '3' version: '3'
services: services:
envoy: envoy:
image: envoyproxy/envoy:v1.25.1 image: envoyproxy/envoy:v1.25.2
volumes: volumes:
- ./example/compose/envoy/envoy.yaml:/etc/envoy/envoy.yaml - ./example/compose/envoy/envoy.yaml:/etc/envoy/envoy.yaml
- ./common/pki:/pki - ./common/pki:/pki

View File

@ -2,7 +2,7 @@
version: '3' version: '3'
services: services:
k3d: k3d:
image: ghcr.io/k3d-io/k3d:5.4.7-dind image: ghcr.io/k3d-io/k3d:5.4.8-dind
volumes: volumes:
- './example/kube:/authelia' - './example/kube:/authelia'
- './example/kube/authelia/configs/configuration.yml:/configmaps/authelia/configuration.yml' - './example/kube/authelia/configs/configuration.yml:/configmaps/authelia/configuration.yml'

View File

@ -164,7 +164,7 @@ http {
# to the virtual endpoint introduced by nginx and declared in the next block. # to the virtual endpoint introduced by nginx and declared in the next block.
location / { location / {
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource. ## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
auth_request /authelia; auth_request /internal/authelia/authz;
## Set the $target_url variable based on the original request. ## Set the $target_url variable based on the original request.
set $target_url $scheme://$http_host$request_uri; set $target_url $scheme://$http_host$request_uri;
@ -209,7 +209,7 @@ http {
} }
# Virtual endpoint forwarding requests to Authelia server. # Virtual endpoint forwarding requests to Authelia server.
location /authelia { location /internal/authelia/authz {
## Essential Proxy Configuration ## Essential Proxy Configuration
internal; internal;
proxy_pass $upstream_authelia; proxy_pass $upstream_authelia;
@ -250,7 +250,7 @@ http {
# Used by suites to test the forwarded users and groups headers produced by Authelia. # Used by suites to test the forwarded users and groups headers produced by Authelia.
location /headers { location /headers {
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource. ## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
auth_request /authelia; auth_request /internal/authelia/authz;
## Set the $target_url variable based on the original request. ## Set the $target_url variable based on the original request.
set $target_url $scheme://$http_host$request_uri; set $target_url $scheme://$http_host$request_uri;
@ -307,7 +307,7 @@ http {
# to the virtual endpoint introduced by nginx and declared in the next block. # to the virtual endpoint introduced by nginx and declared in the next block.
location / { location / {
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource. ## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
auth_request /authelia; auth_request /internal/authelia/authz;
## Set the $target_url variable based on the original request. ## Set the $target_url variable based on the original request.
set $target_url $scheme://$http_host$request_uri; set $target_url $scheme://$http_host$request_uri;
@ -346,7 +346,7 @@ http {
} }
# Virtual endpoint forwarding requests to Authelia server. # Virtual endpoint forwarding requests to Authelia server.
location /authelia { location /internal/authelia/authz {
## Essential Proxy Configuration ## Essential Proxy Configuration
internal; internal;
proxy_pass $upstream_authelia; proxy_pass $upstream_authelia;
@ -356,7 +356,6 @@ http {
# Those headers will be used by Authelia to deduce the target url of the user. # Those headers will be used by Authelia to deduce the target url of the user.
# #
# X-Forwarded-Proto is mandatory since Authelia uses the "trust proxy" option. # X-Forwarded-Proto is mandatory since Authelia uses the "trust proxy" option.
# See https://expressjs.com/en/guide/behind-proxies.html
proxy_set_header X-Original-Method $request_method; proxy_set_header X-Original-Method $request_method;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri; proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

View File

@ -1132,6 +1132,7 @@ func (s *CLISuite) TestStorage05ShouldChangeEncryptionKey() {
s.Assert().Contains(output, "\n\n\tTable (oauth2_openid_connect_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n") s.Assert().Contains(output, "\n\n\tTable (oauth2_openid_connect_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
s.Assert().Contains(output, "\n\n\tTable (oauth2_pkce_request_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n") s.Assert().Contains(output, "\n\n\tTable (oauth2_pkce_request_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
s.Assert().Contains(output, "\n\n\tTable (oauth2_refresh_token_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n") s.Assert().Contains(output, "\n\n\tTable (oauth2_refresh_token_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
s.Assert().Contains(output, "\n\n\tTable (oauth2_par_context): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
s.Assert().Contains(output, "\n\n\tTable (totp_configurations): FAILURE\n\t\tInvalid Rows: 4\n\t\tTotal Rows: 4\n") s.Assert().Contains(output, "\n\n\tTable (totp_configurations): FAILURE\n\t\tInvalid Rows: 4\n\t\tTotal Rows: 4\n")
s.Assert().Contains(output, "\n\n\tTable (webauthn_devices): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n") s.Assert().Contains(output, "\n\n\tTable (webauthn_devices): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
@ -1149,6 +1150,7 @@ func (s *CLISuite) TestStorage05ShouldChangeEncryptionKey() {
s.Assert().Contains(output, "\n\n\tTable (oauth2_openid_connect_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n") s.Assert().Contains(output, "\n\n\tTable (oauth2_openid_connect_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
s.Assert().Contains(output, "\n\n\tTable (oauth2_pkce_request_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n") s.Assert().Contains(output, "\n\n\tTable (oauth2_pkce_request_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
s.Assert().Contains(output, "\n\n\tTable (oauth2_refresh_token_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n") s.Assert().Contains(output, "\n\n\tTable (oauth2_refresh_token_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
s.Assert().Contains(output, "\n\n\tTable (oauth2_par_context): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
s.Assert().Contains(output, "\n\n\tTable (totp_configurations): SUCCESS\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 4\n") s.Assert().Contains(output, "\n\n\tTable (totp_configurations): SUCCESS\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 4\n")
s.Assert().Contains(output, "\n\n\tTable (webauthn_devices): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n") s.Assert().Contains(output, "\n\n\tTable (webauthn_devices): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")

View File

@ -104,8 +104,13 @@ func IsStringSliceContainsAll(needles []string, haystack []string) (inSlice bool
// IsStringSliceContainsAny checks if the haystack contains any of the strings in the needles. // IsStringSliceContainsAny checks if the haystack contains any of the strings in the needles.
func IsStringSliceContainsAny(needles []string, haystack []string) (inSlice bool) { func IsStringSliceContainsAny(needles []string, haystack []string) (inSlice bool) {
return IsStringSliceContainsAnyF(needles, haystack, IsStringInSlice)
}
// IsStringSliceContainsAnyF checks if the haystack contains any of the strings in the needles using the isInSlice func.
func IsStringSliceContainsAnyF(needles []string, haystack []string, isInSlice func(needle string, haystack []string) bool) (inSlice bool) {
for _, n := range needles { for _, n := range needles {
if IsStringInSlice(n, haystack) { if isInSlice(n, haystack) {
return true return true
} }
} }

View File

@ -29,9 +29,9 @@
"@fortawesome/free-regular-svg-icons": "6.3.0", "@fortawesome/free-regular-svg-icons": "6.3.0",
"@fortawesome/free-solid-svg-icons": "6.3.0", "@fortawesome/free-solid-svg-icons": "6.3.0",
"@fortawesome/react-fontawesome": "0.2.0", "@fortawesome/react-fontawesome": "0.2.0",
"@mui/icons-material": "5.11.9", "@mui/icons-material": "5.11.11",
"@mui/material": "5.11.10", "@mui/material": "5.11.11",
"@mui/styles": "5.11.9", "@mui/styles": "5.11.11",
"@simplewebauthn/browser": "7.1.0", "@simplewebauthn/browser": "7.1.0",
"@simplewebauthn/typescript-types": "7.0.0", "@simplewebauthn/typescript-types": "7.0.0",
"axios": "1.3.4", "axios": "1.3.4",
@ -154,7 +154,7 @@
"@testing-library/jest-dom": "5.16.5", "@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "14.0.0", "@testing-library/react": "14.0.0",
"@types/jest": "29.4.0", "@types/jest": "29.4.0",
"@types/node": "18.14.2", "@types/node": "18.14.6",
"@types/qrcode.react": "1.0.2", "@types/qrcode.react": "1.0.2",
"@types/react": "18.0.28", "@types/react": "18.0.28",
"@types/react-dom": "18.0.11", "@types/react-dom": "18.0.11",
@ -162,10 +162,10 @@
"@typescript-eslint/eslint-plugin": "5.54.0", "@typescript-eslint/eslint-plugin": "5.54.0",
"@typescript-eslint/parser": "5.54.0", "@typescript-eslint/parser": "5.54.0",
"@vitejs/plugin-react": "3.1.0", "@vitejs/plugin-react": "3.1.0",
"esbuild": "0.17.10", "esbuild": "0.17.11",
"esbuild-jest": "0.5.0", "esbuild-jest": "0.5.0",
"eslint": "8.35.0", "eslint": "8.35.0",
"eslint-config-prettier": "8.6.0", "eslint-config-prettier": "8.7.0",
"eslint-config-react-app": "7.0.1", "eslint-config-react-app": "7.0.1",
"eslint-formatter-rdjson": "1.0.5", "eslint-formatter-rdjson": "1.0.5",
"eslint-import-resolver-typescript": "3.5.3", "eslint-import-resolver-typescript": "3.5.3",

File diff suppressed because it is too large Load Diff