Merge origin/master into feat-settings-ui
commit
b6883a337f
171
api/openapi.yml
171
api/openapi.yml
|
@ -6,7 +6,7 @@ info:
|
|||
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.
|
||||
contact:
|
||||
name: Authelia Support
|
||||
name: Support
|
||||
url: https://www.authelia.com/contact/
|
||||
email: team@authelia.com
|
||||
license:
|
||||
|
@ -2996,9 +2996,9 @@ components:
|
|||
- "address"
|
||||
- "phone"
|
||||
openid.spec.IntrospectionRequest:
|
||||
type: object
|
||||
required:
|
||||
- "token"
|
||||
type: object
|
||||
properties:
|
||||
token:
|
||||
description: >
|
||||
|
@ -3008,8 +3008,8 @@ components:
|
|||
this is the "refresh_token" value returned from the token endpoint
|
||||
as defined in OAuth 2.0 [RFC6749], Section 5.1. Other token types
|
||||
are outside the scope of this specification.
|
||||
type: string
|
||||
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
|
||||
type: string
|
||||
token_type_hint:
|
||||
description: >
|
||||
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
|
||||
field are defined in the "OAuth Token Type Hints" registry defined
|
||||
in OAuth Token Revocation [RFC7009].
|
||||
type: string
|
||||
example: "access_token"
|
||||
enum:
|
||||
- "access_token"
|
||||
- "refresh_token"
|
||||
example: "access_token"
|
||||
type: string
|
||||
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
|
||||
properties:
|
||||
client_id:
|
||||
description: >
|
||||
REQUIRED if the client is not authenticating with the
|
||||
authorization server as described in Section 3.2.1. of [RFC6749].
|
||||
The client identifier as described in Section 2.2 of [RFC6749].
|
||||
REQUIRED if the client is not authenticating with the authorization server as described in
|
||||
Section 3.2.1. of [RFC6749]. The client identifier as described in Section 2.2 of [RFC6749].
|
||||
example: "my_client"
|
||||
type: string
|
||||
example: "authelia_dc_mn123kjn12kj3123njk"
|
||||
openid.spec.AccessRequest.ClientAuth.Secret:
|
||||
required:
|
||||
- "client_secret"
|
||||
type: object
|
||||
properties:
|
||||
client_secret:
|
||||
description: >
|
||||
REQUIRED. The client secret. The client MAY omit the
|
||||
parameter if the client secret is an empty string.
|
||||
type: string
|
||||
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:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
|
||||
|
@ -3051,22 +3085,22 @@ components:
|
|||
- "grant_type"
|
||||
properties:
|
||||
grant_type:
|
||||
description: Value MUST be set to "urn:ietf:params:oauth:grant-type:device_code".
|
||||
type: string
|
||||
description: Value MUST be set to "code".
|
||||
enum:
|
||||
- "authorization_code"
|
||||
type: string
|
||||
code:
|
||||
description: The Authorization Code.
|
||||
type: string
|
||||
example: "authelia_ac_1j2kn3knj12n3kj12n"
|
||||
type: string
|
||||
code_verifier:
|
||||
description: The Authorization Code Verifier (PKCE).
|
||||
type: string
|
||||
example: "88a25754f7c0b3b3b88cf6cd4e29e8356b160524fdc1cb329a94471825628fd3"
|
||||
type: string
|
||||
redirect_uri:
|
||||
description: The original Redirect URI used in the Authorization Request.
|
||||
type: string
|
||||
example: "https://app.example.com/oidc/callback"
|
||||
type: string
|
||||
openid.spec.AccessRequest.DeviceCodeFlow:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
|
||||
|
@ -3077,13 +3111,13 @@ components:
|
|||
properties:
|
||||
grant_type:
|
||||
description: Value MUST be set to "urn:ietf:params:oauth:grant-type:device_code".
|
||||
type: string
|
||||
enum:
|
||||
- "urn:ietf:params:oauth:grant-type:device_code"
|
||||
type: string
|
||||
device_code:
|
||||
description: The Device Authorization Code.
|
||||
type: string
|
||||
example: "authelia_dc_mn123kjn12kj3123njk"
|
||||
type: string
|
||||
openid.spec.AccessRequest.RefreshTokenFlow:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
|
||||
|
@ -3094,12 +3128,13 @@ components:
|
|||
properties:
|
||||
grant_type:
|
||||
description: Value MUST be set to "refresh_token".
|
||||
type: string
|
||||
enum:
|
||||
- "refresh_token"
|
||||
type: string
|
||||
refresh_token:
|
||||
description: The Refresh Token.
|
||||
example: "authelia_rt_1n2j3kihn12kj3n12k"
|
||||
type: string
|
||||
scope:
|
||||
description: >
|
||||
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
|
||||
treated as equal to the scope originally granted by the
|
||||
resource owner.
|
||||
example: "openid profile groups"
|
||||
type: string
|
||||
openid.spec.AccessResponse:
|
||||
type: object
|
||||
required:
|
||||
- "access_token"
|
||||
- "token_type"
|
||||
- "expires_in"
|
||||
properties:
|
||||
access_token:
|
||||
description: The access token issued by the authorization server.
|
||||
type: string
|
||||
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
|
||||
refresh_token:
|
||||
type: string
|
||||
id_token:
|
||||
description: The id token issued by the authorization server.
|
||||
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
||||
type: string
|
||||
refresh_token:
|
||||
description: >
|
||||
The refresh token, which can be used to obtain new access tokens using the
|
||||
same authorization grant as described in Section 6.
|
||||
token_type:
|
||||
example: "authelia_rt_kGBoSMbfVGP2RR6Kvujv3Xg7uXV2i"
|
||||
type: string
|
||||
token_type:
|
||||
description: >
|
||||
The access token type provides the client with the information
|
||||
required to successfully utilize the access token to make a protected
|
||||
|
@ -3129,21 +3174,26 @@ components:
|
|||
type.
|
||||
enum:
|
||||
- "bearer"
|
||||
example: "bearer"
|
||||
type: string
|
||||
expires_in:
|
||||
type: integer
|
||||
description: >
|
||||
The lifetime in seconds of the access token. For
|
||||
example, the value "3600" denotes that the access token will
|
||||
expire in one hour from the time the response was generated.
|
||||
If omitted, the authorization server SHOULD provide the
|
||||
expiration time via other means or document the default value.
|
||||
example: 3600
|
||||
type: integer
|
||||
state:
|
||||
type: string
|
||||
description: Exactly the state value passed in the authorization request if present.
|
||||
scope:
|
||||
example: "5dVZhNfri5XZS6wadskuzUk4MHYCvEcUgidjMeBjsktAhY7EKB"
|
||||
type: string
|
||||
scope:
|
||||
description: >
|
||||
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:
|
||||
type: object
|
||||
required:
|
||||
|
@ -3154,14 +3204,14 @@ components:
|
|||
properties:
|
||||
scope:
|
||||
description: The requested scope.
|
||||
type: string
|
||||
example: "openid profile groups"
|
||||
type: string
|
||||
response_type:
|
||||
$ref: '#/components/schemas/openid.spec.ResponseType'
|
||||
client_id:
|
||||
description: The OAuth 2.0 client identifier.
|
||||
type: string
|
||||
example: "app"
|
||||
type: string
|
||||
redirect_uri:
|
||||
description: >
|
||||
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
|
||||
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.
|
||||
type: string
|
||||
example: "https://app.example.com"
|
||||
type: string
|
||||
state:
|
||||
description: >
|
||||
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
|
||||
parameter with a browser cookie.
|
||||
type: string
|
||||
example: "oV84Vsy7wyCgRk2h4aZBmXZq4q3g2f"
|
||||
type: string
|
||||
response_mode:
|
||||
$ref: '#/components/schemas/openid.spec.ResponseMode'
|
||||
nonce:
|
||||
|
@ -3188,14 +3238,23 @@ components:
|
|||
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
|
||||
implementation notes, see Section 15.5.2.
|
||||
type: string
|
||||
example: "TRMLqchoKGQNcooXvBvUy9PtmLdJGf"
|
||||
type: string
|
||||
display:
|
||||
$ref: '#/components/schemas/openid.spec.DisplayType'
|
||||
prompt:
|
||||
description: >
|
||||
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.
|
||||
enum:
|
||||
- "none"
|
||||
- "login"
|
||||
- "consent"
|
||||
- "select_account"
|
||||
- "login consent"
|
||||
- "login select_account"
|
||||
- "consent select_account"
|
||||
example: "consent"
|
||||
type: string
|
||||
max_age:
|
||||
description: >
|
||||
|
@ -3273,34 +3332,32 @@ components:
|
|||
description: >
|
||||
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.
|
||||
type: string
|
||||
enum:
|
||||
- "public"
|
||||
- "pairwise"
|
||||
type: string
|
||||
openid.spec.ClientAuthMethod:
|
||||
description: The OAuth 2.0 / OpenID Connect 1.0 Client Authentication Method.
|
||||
type: string
|
||||
enum:
|
||||
- "client_secret_basic"
|
||||
- "client_secret_post"
|
||||
- "client_secret_jwt"
|
||||
- "private_key_jwt"
|
||||
- "none"
|
||||
type: string
|
||||
openid.spec.DisplayType:
|
||||
description: >
|
||||
ASCII string value that specifies how the Authorization Server displays the authentication and consent user
|
||||
interface pages to the End-User.
|
||||
type: string
|
||||
example: "page"
|
||||
enum:
|
||||
- "page"
|
||||
- "popup"
|
||||
- "touch"
|
||||
- "wap"
|
||||
example: "page"
|
||||
type: string
|
||||
openid.spec.ResponseType:
|
||||
description: The OAuth 2.0 / OpenID Connect 1.0 Response Type.
|
||||
type: string
|
||||
example: "code"
|
||||
enum:
|
||||
- "code"
|
||||
- "id_token"
|
||||
|
@ -3310,21 +3367,21 @@ components:
|
|||
- "token id_token"
|
||||
- "code id_token token"
|
||||
- "none"
|
||||
example: "code"
|
||||
type: string
|
||||
openid.spec.ResponseMode:
|
||||
description: >
|
||||
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
|
||||
the default mode specified for the Response Type.
|
||||
type: string
|
||||
example: "query"
|
||||
enum:
|
||||
- "query"
|
||||
- "fragment"
|
||||
- "form_post"
|
||||
example: "query"
|
||||
type: string
|
||||
openid.spec.GrantType:
|
||||
description: The OAuth 2.0 / OpenID Connect 1.0 Grant Type.
|
||||
type: string
|
||||
example: "authorization_code"
|
||||
enum:
|
||||
- "authorization_code"
|
||||
- "refresh_token"
|
||||
|
@ -3332,21 +3389,23 @@ components:
|
|||
- "password"
|
||||
- "client_credentials"
|
||||
- "urn:ietf:params:oauth:grant-type:device_code"
|
||||
example: "authorization_code"
|
||||
type: string
|
||||
openid.spec.CodeChallengeMethod:
|
||||
description: The RFC7636 Code Challenge Verifier Method.
|
||||
type: string
|
||||
example: "S256"
|
||||
enum:
|
||||
- "plain"
|
||||
- "S256"
|
||||
example: "S256"
|
||||
type: string
|
||||
openid.spec.ClaimType:
|
||||
description: The representation of claims.
|
||||
type: string
|
||||
example: "normal"
|
||||
enum:
|
||||
- "normal"
|
||||
- "aggregated"
|
||||
- "distributed"
|
||||
example: "normal"
|
||||
type: string
|
||||
jose.spec.None:
|
||||
description: The JSON Web Signature Algorithm
|
||||
type: string
|
||||
|
@ -3354,13 +3413,12 @@ components:
|
|||
- "none"
|
||||
jose.spec.JWS.None:
|
||||
description: The JSON Web Signature Algorithm
|
||||
type: string
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/jose.spec.None'
|
||||
- $ref: '#/components/schemas/jose.spec.jws'
|
||||
type: string
|
||||
jose.spec.jws:
|
||||
description: The JSON Web Signature Algorithm
|
||||
type: string
|
||||
enum:
|
||||
- "HS256"
|
||||
- "HS384"
|
||||
|
@ -3374,9 +3432,9 @@ components:
|
|||
- "PS256"
|
||||
- "PS384"
|
||||
- "PS512"
|
||||
type: string
|
||||
jose.spec.JWE.alg:
|
||||
description: The JSON Web Encryption Algorithm (CEK)
|
||||
type: string
|
||||
enum:
|
||||
- "RSA1_5"
|
||||
- "RSA-OAEP"
|
||||
|
@ -3395,9 +3453,9 @@ components:
|
|||
- "PBES2-HS256+A128KW"
|
||||
- "PBES2-HS384+A192KW"
|
||||
- "PBES2-HS512+A256KW"
|
||||
type: string
|
||||
jose.spec.JWE.enc:
|
||||
description: The JSON Web Encryption Algorithm (Claims)
|
||||
type: string
|
||||
enum:
|
||||
- "A128CBC-HS256"
|
||||
- "A192CBC-HS384"
|
||||
|
@ -3406,6 +3464,7 @@ components:
|
|||
- "A256CBC"
|
||||
- "A128GCM"
|
||||
- "A256GCM"
|
||||
type: string
|
||||
jose.spec.JWK.base:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -3415,21 +3474,20 @@ components:
|
|||
the public key. The "use" parameter is employed to indicate whether
|
||||
a public key is used for encrypting data or verifying the signature
|
||||
on data.
|
||||
type: string
|
||||
example: "sig"
|
||||
enum:
|
||||
- "sig"
|
||||
- "enc"
|
||||
example: "sig"
|
||||
type: string
|
||||
key_ops:
|
||||
description: >
|
||||
The "key_ops" (key operations) parameter identifies the operation(s)
|
||||
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
|
||||
may be present.
|
||||
type: array
|
||||
example: ["sign"]
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum:
|
||||
- "sign"
|
||||
- "verify"
|
||||
|
@ -3439,6 +3497,7 @@ components:
|
|||
- "unwrapKey"
|
||||
- "deriveKey"
|
||||
- "deriveBits"
|
||||
type: string
|
||||
kid:
|
||||
description: >
|
||||
The "kid" (key ID) parameter is used to match a specific key. This
|
||||
|
@ -3483,8 +3542,8 @@ components:
|
|||
OPTIONAL.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: byte
|
||||
type: string
|
||||
x5t:
|
||||
description: >
|
||||
The "x5t" (X.509 certificate SHA-1 thumbprint) parameter is a
|
||||
|
@ -3493,8 +3552,8 @@ components:
|
|||
thumbprints are also sometimes known as certificate fingerprints.
|
||||
The key in the certificate MUST match the public key represented by
|
||||
other members of the JWK. Use of this member is OPTIONAL.
|
||||
type: string
|
||||
format: byte
|
||||
type: string
|
||||
x5t#S256:
|
||||
description: >
|
||||
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.
|
||||
The key in the certificate MUST match the public key represented by
|
||||
other members of the JWK. Use of this member is OPTIONAL.
|
||||
type: string
|
||||
format: byte
|
||||
type: string
|
||||
jose.spec.JWK.RSA:
|
||||
description: RSA Public Key in JSON Web Key format as defined by RFC7517 and RFC7518.
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/jose.spec.JWK.base'
|
||||
- type: object
|
||||
required:
|
||||
- required:
|
||||
- "kty"
|
||||
- "n"
|
||||
- "e"
|
||||
type: object
|
||||
properties:
|
||||
kty:
|
||||
description: >
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
package cmd
|
||||
|
||||
const (
|
||||
versionSwaggerUI = "4.16.1"
|
||||
versionSwaggerUI = "4.17.0"
|
||||
)
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
* authorization
|
||||
* pushed-authorization-request
|
||||
* token
|
||||
* revocation
|
||||
* 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`.
|
||||
|
||||
#### enforce_par
|
||||
|
||||
{{< confkey type="boolean" default="false" required="no" >}}
|
||||
|
||||
Enforces the use of a [Pushed Authorization Requests] flow for this client.
|
||||
|
||||
#### enforce_pkce
|
||||
|
||||
{{< 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
|
||||
[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
|
||||
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126
|
||||
|
|
|
@ -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 |
|
||||
| 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 |
|
||||
| 8 | 4.38.0 | OpenID Connect 1.0 Pushed Authorization Requests |
|
||||
|
|
|
@ -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.
|
|
@ -8,7 +8,7 @@ images: []
|
|||
menu:
|
||||
contributing:
|
||||
parent: "guidelines"
|
||||
weight: 320
|
||||
weight: 350
|
||||
toc: true
|
||||
aliases:
|
||||
- /docs/contributing/style-guide.html
|
||||
|
|
|
@ -210,14 +210,71 @@ These endpoints can be utilized to discover other endpoints and metadata about t
|
|||
These endpoints implement OpenID Connect elements.
|
||||
|
||||
| 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 |
|
||||
| [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 |
|
||||
| [UserInfo] | https://auth.example.com/api/oidc/userinfo | userinfo_endpoint |
|
||||
| [Introspection] | https://auth.example.com/api/oidc/introspection | introspection_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
|
||||
[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
|
||||
|
@ -230,14 +287,23 @@ These endpoints implement OpenID Connect elements.
|
|||
[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
|
||||
|
||||
[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
|
||||
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126
|
||||
[Token]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
||||
[UserInfo]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
|
||||
[Introspection]: https://datatracker.ietf.org/doc/html/rfc7662
|
||||
[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
|
||||
[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
|
||||
|
|
|
@ -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
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
|
|
@ -383,7 +383,7 @@ proxy_set_header X-Forwarded-For $remote_addr;
|
|||
set $upstream_authelia http://authelia:9091/api/authz/auth-request;
|
||||
|
||||
## Virtual endpoint created by nginx to forward auth requests.
|
||||
location /authelia {
|
||||
location /internal/authelia/authz {
|
||||
## Essential Proxy Configuration
|
||||
internal;
|
||||
proxy_pass $upstream_authelia;
|
||||
|
@ -423,7 +423,7 @@ and is paired with [authelia-location.conf](#authelia-locationconf).*
|
|||
{{< details "/config/nginx/snippets/authelia-authrequest.conf" >}}
|
||||
```nginx
|
||||
## 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.
|
||||
|
||||
|
@ -478,7 +478,7 @@ implementation `AuthRequest` which contains the `HeaderAuthorization` and `Heade
|
|||
set $upstream_authelia http://authelia:9091/api/authz/auth-request/basic;
|
||||
|
||||
# Virtual endpoint created by nginx to forward auth requests.
|
||||
location /authelia-basic {
|
||||
location /internal/authelia/authz/basic {
|
||||
## Essential Proxy Configuration
|
||||
internal;
|
||||
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" >}}
|
||||
```nginx
|
||||
## 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.
|
||||
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
|
||||
location /authelia-detect {
|
||||
location /internal/authelia/authz/detect {
|
||||
internal;
|
||||
|
||||
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" >}}
|
||||
```nginx
|
||||
## 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.
|
||||
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;
|
||||
|
||||
## 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 >}}
|
||||
|
||||
|
|
|
@ -16,16 +16,16 @@ aliases:
|
|||
---
|
||||
|
||||
| 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" %}} |
|
||||
| [Caddy] | [ForwardAuth] | {{% support support="full" link="caddy.md" %}} | {{% 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" %}} |
|
||||
| [NGINX] | [AuthRequest] | {{% support support="full" link="nginx.md" %}} | {{% 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" %}} |
|
||||
| [SWAG] | [AuthRequest] | {{% support support="full" link="swag.md" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
|
||||
| [HAProxy] | [AuthRequest] | {{% support support="full" link="haproxy.md" %}} | {{% 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" %}} |
|
||||
| [Traefik] 1.x | [ForwardAuth] | {{% support support="full" link="traefikv1.md" %}} | {{% support support="unknown" %}} | {{% 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] ([guide](/i/caddy)) | [ForwardAuth] | {{% support support="full" link="/i/caddy" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% 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] ([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] ([guide](/i/npm)) | [AuthRequest] | {{% support support="full" link="/i/npm" %}} | {{% 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] ([guide](/i/haproxy)) | [AuthRequest] | {{% support support="full" link="/i/haproxy" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
|
||||
| [Skipper] ([guide](/i/skipper)) | [ForwardAuth] | {{% support support="full" link="/i/skipper" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} |
|
||||
| [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 %}} |
|
||||
| [IIS] | N/A | {{% support link="#iis" %}} | {{% support %}} | {{% support %}} | {{% support %}} |
|
||||
|
||||
|
|
|
@ -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
|
||||
`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
|
||||
which case it also doesn't represent a good user experience as the `issuer` will be
|
||||
`https://app.example.com/authelia` for example
|
||||
|
@ -147,8 +147,8 @@ services:
|
|||
- '443:443'
|
||||
volumes:
|
||||
- ${PWD}/data/swag:/config
|
||||
## Uncomment the line below if you want to use the Authelia configuration snippets.
|
||||
#- ${PWD}/data/nginx/snippets:/snippets:ro
|
||||
## Uncomment the above line if you want to use the Authelia configuration snippets.
|
||||
environment:
|
||||
PUID: '1000'
|
||||
PGID: '1000'
|
||||
|
|
|
@ -11,6 +11,7 @@ menu:
|
|||
weight: 371
|
||||
toc: true
|
||||
aliases:
|
||||
- /i/traefik/v1
|
||||
- /docs/deployment/supported-proxies/traefik1.x.html
|
||||
---
|
||||
|
||||
|
|
|
@ -15,16 +15,16 @@ toc: false
|
|||
The following table is a support matrix for Authelia features and specific reverse proxies.
|
||||
|
||||
| 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" %}} |
|
||||
| [Caddy] | {{% support support="full" link="../../integration/proxies/caddy.md" %}} | {{% 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" %}} |
|
||||
| [NGINX] | {{% support support="full" link="../../integration/proxies/nginx.md" %}} | {{% 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" %}} |
|
||||
| [SWAG] | {{% support support="full" link="../../integration/proxies/swag.md" %}} | {{% support %}} | {{% support %}} | {{% support support="full" %}} |
|
||||
| [HAProxy] | {{% support support="full" link="../../integration/proxies/haproxy.md" %}} | {{% 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] | {{% support support="full" link="../../integration/proxies/skipper.md" %}} | {{% support %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} |
|
||||
|:---------------------------------------:|:-------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:|:---------------------------------:|:---------------------------------:|
|
||||
| [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] ([guide](/i/caddy)) | {{% support support="full" link="/i/caddy" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% 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] ([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] ([guide](/i/npm)) | {{% support support="full" link="/i/npm" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
|
||||
| [SWAG] ([guide](/i/swag)) | {{% support support="full" link="/i/swag" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
|
||||
| [HAProxy] ([guide](/i/haproxy)) | {{% support support="full" link="/i/haproxy" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
|
||||
| [Skipper] ([guide](/i/skipper)) | {{% support support="full" link="/i/skipper" %}} | {{% support support="unknown" %}} | {{% 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 %}} |
|
||||
| [IIS] | {{% support %}} | {{% support %}} | {{% support %}} | {{% support %}} |
|
||||
|
||||
|
|
|
@ -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
20
go.mod
|
@ -5,9 +5,9 @@ go 1.20
|
|||
require (
|
||||
github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4
|
||||
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/fasthttp/router v1.4.16
|
||||
github.com/fasthttp/router v1.4.17
|
||||
github.com/fasthttp/session/v2 v2.4.16
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4
|
||||
|
@ -33,7 +33,7 @@ require (
|
|||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||
github.com/ory/fosite v0.44.0
|
||||
github.com/ory/herodot v0.9.13
|
||||
github.com/ory/x v0.0.541
|
||||
github.com/ory/x v0.0.543
|
||||
github.com/otiai10/copy v1.9.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/otp v1.4.0
|
||||
|
@ -45,10 +45,10 @@ require (
|
|||
github.com/trustelem/zxcvbn v1.0.1
|
||||
github.com/valyala/fasthttp v1.44.0
|
||||
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/term v0.5.0
|
||||
golang.org/x/text v0.7.0
|
||||
golang.org/x/term v0.6.0
|
||||
golang.org/x/text v0.8.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
@ -101,7 +101,7 @@ require (
|
|||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/savsgio/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/cast v1.5.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/leakless v0.8.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/sys v0.5.0 // indirect
|
||||
golang.org/x/tools v0.4.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect
|
||||
google.golang.org/grpc v1.50.1 // indirect
|
||||
|
|
37
go.sum
37
go.sum
|
@ -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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||
github.com/deckarep/golang-set/v2 v2.2.0 h1:2pMQd3Soi6qfw7E5MMKaEh5W5ES18bW3AbFFnGl6LgQ=
|
||||
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.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
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/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/fasthttp/router v1.4.16 h1:faWJ9OtaHvAtodreyQLps58M80YFNzphMJtOJzeESXs=
|
||||
github.com/fasthttp/router v1.4.16/go.mod h1:NFNlTCilbRVkeLc+E5JDkcxUdkpiJGKDL8Zy7Ey2JTI=
|
||||
github.com/fasthttp/router v1.4.17 h1:Z8fndZotdwcPoYTt8BWwnRBts2UQPnKmOxbb94n0GUc=
|
||||
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/go.mod h1:nv8SD6pAx3n3KjJsEt4k1p0vstqclbNcrCwjc1OjuCI=
|
||||
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/go.mod h1:IWDs9kSvFQqw/cQ8zi5ksyYvITiUU4dI7glUrhZcJYo=
|
||||
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.541/go.mod h1:ktXUvx51Ok1gMGr3ysvktanqr+eiB4FXglt4nF4w2Uo=
|
||||
github.com/ory/x v0.0.543 h1:I6bl6IV2Ok07io6M2dnaRaJHP5oRU096T9FYoe8m48U=
|
||||
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/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI=
|
||||
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.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
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-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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
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.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.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||
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-20180826012351-8a410e7b638d/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-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.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
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-20190226205417-e64efc72b421/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-20221010170243-090e33056c14/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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
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-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.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
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.3.0/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.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.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
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-20190308202827-9d24e82272b4/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.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.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
|
||||
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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
@ -29,10 +29,17 @@ type OpenIDConnectConfiguration struct {
|
|||
EnablePKCEPlainChallenge bool `koanf:"enable_pkce_plain_challenge"`
|
||||
|
||||
CORS OpenIDConnectCORSConfiguration `koanf:"cors"`
|
||||
PAR OpenIDConnectPARConfiguration `koanf:"pushed_authorizations"`
|
||||
|
||||
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.
|
||||
type OpenIDConnectCORSConfiguration struct {
|
||||
Endpoints []string `koanf:"endpoints"`
|
||||
|
@ -59,6 +66,7 @@ type OpenIDConnectClientConfiguration struct {
|
|||
|
||||
Policy string `koanf:"authorization_policy"`
|
||||
|
||||
EnforcePAR bool `koanf:"enforce_par"`
|
||||
EnforcePKCE bool `koanf:"enforce_pkce"`
|
||||
|
||||
PKCEChallengeMethod string `koanf:"pkce_challenge_method"`
|
||||
|
|
|
@ -31,6 +31,8 @@ var Keys = []string{
|
|||
"identity_providers.oidc.cors.endpoints",
|
||||
"identity_providers.oidc.cors.allowed_origins",
|
||||
"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[].id",
|
||||
"identity_providers.oidc.clients[].description",
|
||||
|
@ -44,6 +46,7 @@ var Keys = []string{
|
|||
"identity_providers.oidc.clients[].response_types",
|
||||
"identity_providers.oidc.clients[].response_modes",
|
||||
"identity_providers.oidc.clients[].authorization_policy",
|
||||
"identity_providers.oidc.clients[].enforce_par",
|
||||
"identity_providers.oidc.clients[].enforce_pkce",
|
||||
"identity_providers.oidc.clients[].pkce_challenge_method",
|
||||
"identity_providers.oidc.clients[].userinfo_signing_algorithm",
|
||||
|
|
|
@ -392,7 +392,7 @@ var (
|
|||
validOIDCGrantTypes = []string{oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeAuthorizationCode, oidc.GrantTypePassword, oidc.GrantTypeClientCredentials}
|
||||
validOIDCResponseModes = []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery, oidc.ResponseModeFragment}
|
||||
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()}
|
||||
)
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ func TestShouldRaiseErrorWhenCORSEndpointsNotValid(t *testing.T) {
|
|||
|
||||
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) {
|
||||
|
|
|
@ -53,10 +53,20 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
|
|||
return
|
||||
}
|
||||
|
||||
if err = client.ValidateAuthorizationPolicy(requester); err != nil {
|
||||
if err = client.ValidatePARPolicy(requester, ctx.Providers.OpenIDConnect.GetPushedAuthorizeRequestURIPrefix(ctx)); err != nil {
|
||||
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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
|
|
@ -9,9 +9,8 @@ import (
|
|||
mail "net/mail"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
|
||||
templates "github.com/authelia/authelia/v4/internal/templates"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// MockNotifier is a mock of Notifier interface.
|
||||
|
|
|
@ -39,6 +39,7 @@ func (m *MockStorage) EXPECT() *MockStorageMockRecorder {
|
|||
return m.recorder
|
||||
}
|
||||
|
||||
|
||||
// AppendAuthenticationLog mocks base method.
|
||||
func (m *MockStorage) AppendAuthenticationLog(arg0 context.Context, arg1 model.AuthenticationAttempt) error {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (m *MockStorage) LoadOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 string) (*model.OAuth2Session, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (m *MockStorage) RevokeOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 string) error {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (m *MockStorage) SaveOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 model.OAuth2Session) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
|
|
@ -7,9 +7,8 @@ package mocks
|
|||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
|
||||
model "github.com/authelia/authelia/v4/internal/model"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// MockTOTP is a mock of Provider interface.
|
||||
|
|
|
@ -7,9 +7,8 @@ package mocks
|
|||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
|
||||
authentication "github.com/authelia/authelia/v4/internal/authentication"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// MockUserProvider is a mock of UserProvider interface.
|
||||
|
|
|
@ -39,6 +39,14 @@ func NewOAuth2ConsentSession(subject uuid.UUID, r fosite.Requester) (consent *OA
|
|||
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.
|
||||
func NewOAuth2SessionFromRequest(signature string, r fosite.Requester) (session *OAuth2Session, err error) {
|
||||
var (
|
||||
|
@ -77,12 +85,43 @@ func NewOAuth2SessionFromRequest(signature string, r fosite.Requester) (session
|
|||
}, 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,
|
||||
// NewOAuth2PARContext creates a new Pushed Authorization Request Context as a OAuth2PARContext.
|
||||
func NewOAuth2PARContext(contextID string, r fosite.AuthorizeRequester) (context *OAuth2PARContext, err error) {
|
||||
var (
|
||||
s *OpenIDSession
|
||||
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.
|
||||
|
@ -264,6 +303,70 @@ func (s *OAuth2Session) ToRequest(ctx context.Context, session fosite.Session, s
|
|||
}, 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.
|
||||
type OpenIDSession struct {
|
||||
*openid.DefaultSession `json:"id_token"`
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/x/errorsx"
|
||||
|
@ -32,6 +32,8 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client)
|
|||
ResponseTypes: config.ResponseTypes,
|
||||
ResponseModes: []fosite.ResponseModeType{fosite.ResponseModeDefault},
|
||||
|
||||
EnforcePAR: config.EnforcePAR,
|
||||
|
||||
UserinfoSigningAlgorithm: config.UserinfoSigningAlgorithm,
|
||||
|
||||
Policy: authorization.NewLevel(config.Policy),
|
||||
|
@ -46,22 +48,22 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client)
|
|||
return client
|
||||
}
|
||||
|
||||
// ValidateAuthorizationPolicy is a helper function to validate additional policy constraints on a per-client basis.
|
||||
func (c *Client) ValidateAuthorizationPolicy(r fosite.Requester) (err error) {
|
||||
// ValidatePKCEPolicy is a helper function to validate PKCE policy constraints on a per-client basis.
|
||||
func (c *Client) ValidatePKCEPolicy(r fosite.Requester) (err error) {
|
||||
form := r.GetRequestForm()
|
||||
|
||||
if c.EnforcePKCE {
|
||||
if form.Get("code_challenge") == "" {
|
||||
if form.Get(FormParameterCodeChallenge) == "" {
|
||||
return errorsx.WithStack(fosite.ErrInvalidRequest.
|
||||
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."))
|
||||
}
|
||||
|
||||
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.
|
||||
WithHint(fmt.Sprintf("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)))
|
||||
WithHintf("Client must use code_challenge_method=%s, %s is not allowed.", c.PKCEChallengeMethod, method).
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (c *Client) IsAuthenticationLevelSufficient(level authentication.Level) bool {
|
||||
if level == authentication.NotAuthenticated {
|
||||
|
@ -105,7 +124,7 @@ func (c *Client) GetID() string {
|
|||
}
|
||||
|
||||
// GetHashedSecret returns the Secret.
|
||||
func (c *Client) GetHashedSecret() []byte {
|
||||
func (c *Client) GetHashedSecret() (secret []byte) {
|
||||
if c.Secret == nil {
|
||||
return []byte(nil)
|
||||
}
|
||||
|
@ -114,7 +133,7 @@ func (c *Client) GetHashedSecret() []byte {
|
|||
}
|
||||
|
||||
// GetRedirectURIs returns the RedirectURIs.
|
||||
func (c *Client) GetRedirectURIs() []string {
|
||||
func (c *Client) GetRedirectURIs() (redirectURIs []string) {
|
||||
return c.RedirectURIs
|
||||
}
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ func TestNewClientPKCE(t *testing.T) {
|
|||
expectedEnforcePKCE bool
|
||||
expectedEnforcePKCEChallengeMethod bool
|
||||
expected string
|
||||
req *fosite.Request
|
||||
r *fosite.Request
|
||||
err string
|
||||
}{
|
||||
{
|
||||
|
@ -288,8 +288,8 @@ func TestNewClientPKCE(t *testing.T) {
|
|||
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.EnforcePKCEChallengeMethod)
|
||||
assert.Equal(t, tc.expected, client.PKCEChallengeMethod)
|
||||
|
||||
if tc.req != nil {
|
||||
err := client.ValidateAuthorizationPolicy(tc.req)
|
||||
if tc.r != nil {
|
||||
err := client.ValidatePKCEPolicy(tc.r)
|
||||
|
||||
if tc.err != "" {
|
||||
assert.EqualError(t, err, tc.err)
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"hash"
|
||||
"html/template"
|
||||
"net/url"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
|
@ -23,8 +24,8 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
func NewConfig(config *schema.OpenIDConnectConfiguration, templates *templates.Provider) *Config {
|
||||
c := &Config{
|
||||
func NewConfig(config *schema.OpenIDConnectConfiguration, templates *templates.Provider) (c *Config) {
|
||||
c = &Config{
|
||||
GlobalSecret: []byte(utils.HashSHA256FromString(config.HMACSecret)),
|
||||
SendDebugMessagesToClients: config.EnableClientDebugMessages,
|
||||
MinParameterEntropy: config.MinimumParameterEntropy,
|
||||
|
@ -39,18 +40,23 @@ func NewConfig(config *schema.OpenIDConnectConfiguration, templates *templates.P
|
|||
EnforcePublicClients: config.EnforcePKCE != "never",
|
||||
AllowPlainChallengeMethod: config.EnablePKCEPlainChallenge,
|
||||
},
|
||||
PAR: PARConfig{
|
||||
Enforced: config.PAR.Enforce,
|
||||
ContextLifespan: config.PAR.ContextLifespan,
|
||||
URIPrefix: urnPARPrefix,
|
||||
},
|
||||
Templates: templates,
|
||||
}
|
||||
|
||||
c.Strategy.Core = &HMACCoreStrategy{
|
||||
Enigma: &hmac.HMACStrategy{Config: c},
|
||||
Config: c,
|
||||
prefix: tokenPrefixFmt,
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Config is an implementation of the fosite.Configurator.
|
||||
type Config struct {
|
||||
// GlobalSecret is the global secret used to sign and verify signatures.
|
||||
GlobalSecret []byte
|
||||
|
@ -67,7 +73,7 @@ type Config struct {
|
|||
JWTScopeField jwt.JWTScopeFieldEnum
|
||||
JWTMaxDuration time.Duration
|
||||
|
||||
Hasher *AdaptiveHasher
|
||||
Hasher *Hasher
|
||||
Hash HashConfig
|
||||
Strategy StrategyConfig
|
||||
PAR PARConfig
|
||||
|
@ -91,11 +97,13 @@ type Config struct {
|
|||
Templates *templates.Provider
|
||||
}
|
||||
|
||||
// HashConfig holds specific fosite.Configurator information for hashing.
|
||||
type HashConfig struct {
|
||||
ClientSecrets fosite.Hasher
|
||||
HMAC func() (h hash.Hash)
|
||||
}
|
||||
|
||||
// StrategyConfig holds specific fosite.Configurator information for various strategies.
|
||||
type StrategyConfig struct {
|
||||
Core oauth2.CoreStrategy
|
||||
OpenID openid.OpenIDConnectTokenStrategy
|
||||
|
@ -105,17 +113,20 @@ type StrategyConfig struct {
|
|||
ClientAuthentication fosite.ClientAuthenticationStrategy
|
||||
}
|
||||
|
||||
// PARConfig holds specific fosite.Configurator information for Pushed Authorization Requests.
|
||||
type PARConfig struct {
|
||||
Enforced bool
|
||||
URIPrefix string
|
||||
ContextLifespan time.Duration
|
||||
}
|
||||
|
||||
// IssuersConfig holds specific fosite.Configurator information for the issuer.
|
||||
type IssuersConfig struct {
|
||||
IDToken string
|
||||
AccessToken string
|
||||
}
|
||||
|
||||
// HandlersConfig holds specific fosite.Configurator handlers configuration information.
|
||||
type HandlersConfig struct {
|
||||
// ResponseMode provides an extension handler for custom response modes.
|
||||
ResponseMode fosite.ResponseModeHandler
|
||||
|
@ -136,18 +147,21 @@ type HandlersConfig struct {
|
|||
PushedAuthorizeEndpoint fosite.PushedAuthorizeEndpointHandlers
|
||||
}
|
||||
|
||||
// GrantTypeJWTBearerConfig holds specific fosite.Configurator information for the JWT Bearer Grant Type.
|
||||
type GrantTypeJWTBearerConfig struct {
|
||||
OptionalClientAuth bool
|
||||
OptionalJTIClaim bool
|
||||
OptionalIssuedDate bool
|
||||
}
|
||||
|
||||
// ProofKeyCodeExchangeConfig holds specific fosite.Configurator information for PKCE.
|
||||
type ProofKeyCodeExchangeConfig struct {
|
||||
Enforce bool
|
||||
EnforcePublicClients bool
|
||||
AllowPlainChallengeMethod bool
|
||||
}
|
||||
|
||||
// LifespanConfig holds specific fosite.Configurator information for various lifespans.
|
||||
type LifespanConfig struct {
|
||||
AccessToken time.Duration
|
||||
AuthorizeCode time.Duration
|
||||
|
@ -161,6 +175,7 @@ const (
|
|||
PromptConsent = "consent"
|
||||
)
|
||||
|
||||
// LoadHandlers reloads the handlers based on the current configuration.
|
||||
func (c *Config) LoadHandlers(store *Store, strategy jwt.Signer) {
|
||||
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 {
|
||||
x.Revocation.Append(h)
|
||||
}
|
||||
|
||||
if h, ok := handler.(fosite.PushedAuthorizeEndpointHandler); ok {
|
||||
x.PushedAuthorizeEndpoint.Append(h)
|
||||
}
|
||||
}
|
||||
|
||||
c.Handlers = x
|
||||
|
@ -515,13 +534,24 @@ func (c *Config) GetFormPostHTMLTemplate(ctx context.Context) (tmpl *template.Te
|
|||
|
||||
// GetTokenURL returns the token URL.
|
||||
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
|
||||
}
|
||||
|
||||
// GetSecretsHasher returns the client secrets hashing function.
|
||||
func (c *Config) GetSecretsHasher(ctx context.Context) (hasher fosite.Hasher) {
|
||||
if c.Hash.ClientSecrets == nil {
|
||||
c.Hash.ClientSecrets, _ = NewAdaptiveHasher()
|
||||
c.Hash.ClientSecrets, _ = NewHasher()
|
||||
}
|
||||
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,25 @@ const (
|
|||
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.
|
||||
const (
|
||||
SigningAlgorithmNone = none
|
||||
|
@ -91,6 +110,12 @@ const (
|
|||
PKCEChallengeMethodSHA256 = "S256"
|
||||
)
|
||||
|
||||
const (
|
||||
FormParameterRequestURI = "request_uri"
|
||||
FormParameterCodeChallenge = "code_challenge"
|
||||
FormParameterCodeChallengeMethod = "code_challenge_method"
|
||||
)
|
||||
|
||||
// Endpoints.
|
||||
const (
|
||||
EndpointAuthorization = "authorization"
|
||||
|
@ -98,6 +123,7 @@ const (
|
|||
EndpointUserinfo = "userinfo"
|
||||
EndpointIntrospection = "introspection"
|
||||
EndpointRevocation = "revocation"
|
||||
EndpointPushedAuthorizationRequest = "pushed-authorization-request"
|
||||
)
|
||||
|
||||
// JWT Headers.
|
||||
|
@ -107,7 +133,9 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
tokenPrefixFmt = "authelia_%s_" //nolint:gosec
|
||||
tokenPrefixOrgAutheliaFmt = "authelia_%s_" //nolint:gosec
|
||||
tokenPrefixOrgOryFmt = "ory_%s_" //nolint:gosec
|
||||
|
||||
tokenPrefixPartAccessToken = "at"
|
||||
tokenPrefixPartRefreshToken = "rt"
|
||||
tokenPrefixPartAuthorizeCode = "ac"
|
||||
|
@ -127,6 +155,8 @@ const (
|
|||
EndpointPathUserinfo = EndpointPathRoot + "/" + EndpointUserinfo
|
||||
EndpointPathIntrospection = EndpointPathRoot + "/" + EndpointIntrospection
|
||||
EndpointPathRevocation = EndpointPathRoot + "/" + EndpointRevocation
|
||||
|
||||
EndpointPathPushedAuthorizationRequest = EndpointPathRoot + "/" + EndpointPushedAuthorizationRequest
|
||||
)
|
||||
|
||||
// Authentication Method Reference Values https://datatracker.ietf.org/doc/html/rfc8176
|
||||
|
|
|
@ -19,7 +19,6 @@ type HMACCoreStrategy struct {
|
|||
fosite.RefreshTokenLifespanProvider
|
||||
fosite.AuthorizeCodeLifespanProvider
|
||||
}
|
||||
prefix string
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if len(h.prefix) == 0 {
|
||||
return ""
|
||||
}
|
||||
return h.getCustomPrefix(tokenPrefixOrgAutheliaFmt, part)
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -124,5 +123,9 @@ func (h *HMACCoreStrategy) setPrefix(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))
|
||||
}
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,21 +1,29 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
)
|
||||
|
||||
// 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{
|
||||
CommonDiscoveryOptions: CommonDiscoveryOptions{
|
||||
SubjectTypesSupported: []string{
|
||||
SubjectTypePublic,
|
||||
},
|
||||
ResponseTypesSupported: []string{
|
||||
"code",
|
||||
"token",
|
||||
"id_token",
|
||||
"code token",
|
||||
"code id_token",
|
||||
"token id_token",
|
||||
"code token id_token",
|
||||
"none",
|
||||
ResponseTypeAuthorizationCodeFlow,
|
||||
ResponseTypeImplicitFlowIDToken,
|
||||
ResponseTypeImplicitFlowToken,
|
||||
ResponseTypeImplicitFlowBoth,
|
||||
ResponseTypeHybridFlowIDToken,
|
||||
ResponseTypeHybridFlowToken,
|
||||
ResponseTypeHybridFlowBoth,
|
||||
},
|
||||
GrantTypesSupported: []string{
|
||||
GrantTypeAuthorizationCode,
|
||||
GrantTypeImplicit,
|
||||
GrantTypeRefreshToken,
|
||||
},
|
||||
ResponseModesSupported: []string{
|
||||
ResponseModeFormPost,
|
||||
|
@ -49,6 +57,12 @@ func NewOpenIDConnectWellKnownConfiguration(enablePKCEPlainChallenge bool, clien
|
|||
ClaimPreferredUsername,
|
||||
ClaimFullName,
|
||||
},
|
||||
TokenEndpointAuthMethodsSupported: []string{
|
||||
ClientAuthMethodClientSecretBasic,
|
||||
ClientAuthMethodClientSecretPost,
|
||||
ClientAuthMethodClientSecretJWT,
|
||||
ClientAuthMethodNone,
|
||||
},
|
||||
},
|
||||
OAuth2DiscoveryOptions: OAuth2DiscoveryOptions{
|
||||
CodeChallengeMethodsSupported: []string{
|
||||
|
@ -68,6 +82,9 @@ func NewOpenIDConnectWellKnownConfiguration(enablePKCEPlainChallenge bool, clien
|
|||
SigningAlgorithmRSAWithSHA256,
|
||||
},
|
||||
},
|
||||
PushedAuthorizationDiscoveryOptions: PushedAuthorizationDiscoveryOptions{
|
||||
RequirePushedAuthorizationRequests: c.PAR.Enforce,
|
||||
},
|
||||
}
|
||||
|
||||
var pairwise, public bool
|
||||
|
@ -86,7 +103,7 @@ func NewOpenIDConnectWellKnownConfiguration(enablePKCEPlainChallenge bool, clien
|
|||
config.SubjectTypesSupported = append(config.SubjectTypesSupported, SubjectTypePairwise)
|
||||
}
|
||||
|
||||
if enablePKCEPlainChallenge {
|
||||
if c.EnablePKCEPlainChallenge {
|
||||
config.CodeChallengeMethodsSupported = append(config.CodeChallengeMethodsSupported, PKCEChallengeMethodPlain)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,15 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
)
|
||||
|
||||
func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
pkcePlainChallenge bool
|
||||
enforcePAR bool
|
||||
clients map[string]*Client
|
||||
|
||||
expectCodeChallengeMethodsSupported, expectSubjectTypesSupported []string
|
||||
|
@ -63,7 +66,14 @@ func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
|||
|
||||
for _, tc := range testCases {
|
||||
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 {
|
||||
assert.Contains(t, actual.CodeChallengeMethodsSupported, codeChallengeMethod)
|
||||
}
|
||||
|
|
|
@ -9,11 +9,27 @@ import (
|
|||
var errPasswordsDoNotMatch = errors.New("the passwords don't match")
|
||||
|
||||
var (
|
||||
// ErrIssuerCouldNotDerive is sent when the issuer couldn't be determined from the headers.
|
||||
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.")
|
||||
|
||||
// ErrConsentCouldNotPerform is sent when the Consent Session couldn't be performed for varying reasons.
|
||||
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.")
|
||||
|
||||
// ErrConsentCouldNotSave is sent when the Consent Session couldn't be saved to the database.
|
||||
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.")
|
||||
|
||||
// ErrConsentMalformedChallengeID is sent when the Consent ID is not a UUID.
|
||||
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.")
|
||||
)
|
||||
|
|
|
@ -8,8 +8,9 @@ import (
|
|||
"github.com/go-crypt/crypt/algorithm/plaintext"
|
||||
)
|
||||
|
||||
func NewAdaptiveHasher() (hasher *AdaptiveHasher, err error) {
|
||||
hasher = &AdaptiveHasher{}
|
||||
// NewHasher returns a new Hasher.
|
||||
func NewHasher() (hasher *Hasher, err error) {
|
||||
hasher = &Hasher{}
|
||||
|
||||
if hasher.decoder, err = crypt.NewDefaultDecoder(); err != nil {
|
||||
return nil, err
|
||||
|
@ -22,13 +23,13 @@ func NewAdaptiveHasher() (hasher *AdaptiveHasher, err error) {
|
|||
return hasher, nil
|
||||
}
|
||||
|
||||
// AdaptiveHasher implements the fosite.Hasher interface without an actual hashing algo.
|
||||
type AdaptiveHasher struct {
|
||||
// Hasher implements the fosite.Hasher interface and adaptively compares hashes.
|
||||
type Hasher struct {
|
||||
decoder algorithm.DecoderRegister
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
|
||||
hasher, err := NewAdaptiveHasher()
|
||||
hasher, err := NewHasher()
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -22,7 +22,7 @@ func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestShouldNotRaiseErrorOnEqualPasswordsPlainTextWithSeparator(t *testing.T) {
|
||||
hasher, err := NewAdaptiveHasher()
|
||||
hasher, err := NewHasher()
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -35,7 +35,7 @@ func TestShouldNotRaiseErrorOnEqualPasswordsPlainTextWithSeparator(t *testing.T)
|
|||
}
|
||||
|
||||
func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
|
||||
hasher, err := NewAdaptiveHasher()
|
||||
hasher, err := NewHasher()
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -48,7 +48,7 @@ func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestShouldHashPassword(t *testing.T) {
|
||||
hasher := AdaptiveHasher{}
|
||||
hasher := Hasher{}
|
||||
|
||||
data := []byte("abc")
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, store s
|
|||
|
||||
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
|
||||
}
|
||||
|
@ -50,12 +50,12 @@ func (p *OpenIDConnectProvider) GetOAuth2WellKnownConfiguration(issuer string) O
|
|||
}
|
||||
|
||||
options.Issuer = issuer
|
||||
|
||||
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.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)
|
||||
|
||||
return options
|
||||
|
@ -72,14 +72,14 @@ func (p *OpenIDConnectProvider) GetOpenIDConnectWellKnownConfiguration(issuer st
|
|||
}
|
||||
|
||||
options.Issuer = issuer
|
||||
|
||||
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.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.IntrospectionEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathIntrospection)
|
||||
options.RevocationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathRevocation)
|
||||
|
||||
return options
|
||||
}
|
||||
|
|
|
@ -142,15 +142,25 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
|||
assert.Len(t, disco.SubjectTypesSupported, 1)
|
||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
||||
|
||||
assert.Len(t, disco.ResponseTypesSupported, 8)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "code")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "id_token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "code token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "code id_token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "token id_token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "code token id_token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "none")
|
||||
assert.Len(t, disco.ResponseTypesSupported, 7)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowIDToken)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowToken)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowBoth)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowIDToken)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth)
|
||||
|
||||
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.Contains(t, disco.IDTokenSigningAlgValuesSupported, SigningAlgorithmRSAWithSHA256)
|
||||
|
@ -231,15 +241,25 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
|
|||
assert.Len(t, disco.SubjectTypesSupported, 1)
|
||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
||||
|
||||
assert.Len(t, disco.ResponseTypesSupported, 8)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "code")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "id_token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "code token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "code id_token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "token id_token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "code token id_token")
|
||||
assert.Contains(t, disco.ResponseTypesSupported, "none")
|
||||
assert.Len(t, disco.ResponseTypesSupported, 7)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowIDToken)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowToken)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowBoth)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowIDToken)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken)
|
||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth)
|
||||
|
||||
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.Contains(t, disco.ClaimsSupported, ClaimAuthenticationMethodsReference)
|
||||
|
|
|
@ -165,7 +165,7 @@ func (s *Store) InvalidateAuthorizeCodeSession(ctx context.Context, code string)
|
|||
// This implements a portion of oauth2.AuthorizeCodeStorage.
|
||||
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.
|
||||
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.
|
||||
|
@ -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.
|
||||
// This implements a portion of oauth2.AccessTokenStorage.
|
||||
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.
|
||||
|
@ -223,7 +223,7 @@ func (s *Store) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requestI
|
|||
// GetRefreshTokenSession gets the authorization request for a given refresh token.
|
||||
// This implements a portion of oauth2.RefreshTokenStorage.
|
||||
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.
|
||||
|
@ -241,7 +241,7 @@ func (s *Store) DeletePKCERequestSession(ctx context.Context, signature string)
|
|||
// GetPKCERequestSession gets the authorization request for a given PKCE request.
|
||||
// This implements a portion of pkce.PKCERequestStorage.
|
||||
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.
|
||||
|
@ -263,7 +263,37 @@ func (s *Store) DeleteOpenIDConnectSession(ctx context.Context, authorizeCode st
|
|||
// - or an arbitrary error if an error occurred.
|
||||
// This implements a portion of openid.OpenIDConnectRequestStorage.
|
||||
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.
|
||||
|
@ -280,7 +310,7 @@ func (s *Store) MarkJWTUsedForTime(ctx context.Context, jti string, exp time.Tim
|
|||
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 (
|
||||
sessionModel *model.OAuth2Session
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
|
@ -118,6 +119,8 @@ type Client struct {
|
|||
ResponseTypes []string
|
||||
ResponseModes []fosite.ResponseModeType
|
||||
|
||||
EnforcePAR bool
|
||||
|
||||
UserinfoSigningAlgorithm string
|
||||
|
||||
Policy authorization.Level
|
||||
|
@ -643,3 +646,10 @@ type OpenIDConnectWellKnownConfiguration struct {
|
|||
OpenIDConnectFrontChannelLogoutDiscoveryOptions
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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.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().
|
||||
WithAllowCredentials(true).
|
||||
WithAllowedMethods(fasthttp.MethodOptions, fasthttp.MethodPost).
|
||||
|
|
|
@ -14,15 +14,16 @@ const (
|
|||
tableWebauthnDevices = "webauthn_devices"
|
||||
tableWebauthnUsers = "webauthn_users"
|
||||
|
||||
tableOAuth2BlacklistedJTI = "oauth2_blacklisted_jti"
|
||||
tableOAuth2ConsentSession = "oauth2_consent_session"
|
||||
tableOAuth2ConsentPreConfiguration = "oauth2_consent_preconfiguration"
|
||||
|
||||
tableOAuth2AuthorizeCodeSession = "oauth2_authorization_code_session"
|
||||
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.
|
||||
tableOAuth2PKCERequestSession = "oauth2_pkce_request_session"
|
||||
tableOAuth2AuthorizeCodeSession = "oauth2_authorization_code_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"
|
||||
tableEncryption = "encryption"
|
||||
|
@ -33,26 +34,29 @@ type OAuth2SessionType int
|
|||
|
||||
// Representation of specific OAuth 2.0 session types.
|
||||
const (
|
||||
OAuth2SessionTypeAuthorizeCode OAuth2SessionType = iota
|
||||
OAuth2SessionTypeAccessToken
|
||||
OAuth2SessionTypeRefreshToken
|
||||
OAuth2SessionTypePKCEChallenge
|
||||
OAuth2SessionTypeAccessToken OAuth2SessionType = iota
|
||||
OAuth2SessionTypeAuthorizeCode
|
||||
OAuth2SessionTypeOpenIDConnect
|
||||
OAuth2SessionTypePAR
|
||||
OAuth2SessionTypePKCEChallenge
|
||||
OAuth2SessionTypeRefreshToken
|
||||
)
|
||||
|
||||
// String returns a string representation of this OAuth2SessionType.
|
||||
func (s OAuth2SessionType) String() string {
|
||||
switch s {
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
return "authorization code"
|
||||
case OAuth2SessionTypeAccessToken:
|
||||
return "access token"
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
return "refresh token"
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
return "pkce challenge"
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
return "authorization code"
|
||||
case OAuth2SessionTypeOpenIDConnect:
|
||||
return "openid connect"
|
||||
case OAuth2SessionTypePAR:
|
||||
return "pushed authorization request context"
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
return "pkce challenge"
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
return "refresh token"
|
||||
default:
|
||||
return "invalid"
|
||||
}
|
||||
|
@ -61,16 +65,18 @@ func (s OAuth2SessionType) String() string {
|
|||
// Table returns the table name for this session type.
|
||||
func (s OAuth2SessionType) Table() string {
|
||||
switch s {
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
return tableOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeAccessToken:
|
||||
return tableOAuth2AccessTokenSession
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
return tableOAuth2RefreshTokenSession
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
return tableOAuth2PKCERequestSession
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
return tableOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeOpenIDConnect:
|
||||
return tableOAuth2OpenIDConnectSession
|
||||
case OAuth2SessionTypePAR:
|
||||
return tableOAuth2PARContext
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
return tableOAuth2PKCERequestSession
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
return tableOAuth2RefreshTokenSession
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
@ -120,7 +126,7 @@ const (
|
|||
)
|
||||
|
||||
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 (
|
||||
|
|
|
@ -130,15 +130,15 @@ func skipMigration(providerName string, up bool, target, prior int, migration *m
|
|||
}
|
||||
|
||||
func scanMigration(m string) (migration model.SchemaMigration, err error) {
|
||||
result := reMigration.FindStringSubmatch(m)
|
||||
|
||||
if result == nil || len(result) != 5 {
|
||||
if !reMigration.MatchString(m) {
|
||||
return model.SchemaMigration{}, errors.New("invalid migration: could not parse the format")
|
||||
}
|
||||
|
||||
result := reMigration.FindStringSubmatch(m)
|
||||
|
||||
migration = model.SchemaMigration{
|
||||
Name: strings.ReplaceAll(result[2], "_", " "),
|
||||
Provider: result[3],
|
||||
Name: strings.ReplaceAll(result[reMigration.SubexpIndex("Name")], "_", " "),
|
||||
Provider: result[reMigration.SubexpIndex("Provider")],
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
switch result[4] {
|
||||
switch direction := result[reMigration.SubexpIndex("Direction")]; direction {
|
||||
case "up":
|
||||
migration.Up = true
|
||||
case "down":
|
||||
migration.Up = false
|
||||
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 {
|
||||
case providerAll, providerSQLite, providerMySQL, providerPostgres:
|
||||
break
|
||||
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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE IF EXISTS oauth2_par_context;
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -24,8 +24,8 @@ type Provider interface {
|
|||
LoadUserInfo(ctx context.Context, username string) (info model.UserInfo, err error)
|
||||
|
||||
SaveUserOpaqueIdentifier(ctx context.Context, subject model.UserOpaqueIdentifier) (err error)
|
||||
LoadUserOpaqueIdentifier(ctx context.Context, opaqueUUID uuid.UUID) (subject *model.UserOpaqueIdentifier, err error)
|
||||
LoadUserOpaqueIdentifiers(ctx context.Context) (opaqueIDs []model.UserOpaqueIdentifier, err error)
|
||||
LoadUserOpaqueIdentifier(ctx context.Context, identifier uuid.UUID) (subject *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)
|
||||
|
||||
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)
|
||||
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)
|
||||
LoadOAuth2BlacklistedJTI(ctx context.Context, signature string) (blacklistedJTI *model.OAuth2BlacklistedJTI, err error)
|
||||
|
||||
|
|
|
@ -73,6 +73,13 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
|
|||
sqlSelectUserOpaqueIdentifiers: fmt.Sprintf(queryFmtSelectUserOpaqueIdentifiers, 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),
|
||||
sqlSelectOAuth2ConsentPreConfigurations: fmt.Sprintf(queryFmtSelectOAuth2ConsentPreConfigurations, tableOAuth2ConsentPreConfiguration),
|
||||
|
||||
|
@ -82,13 +89,6 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
|
|||
sqlUpdateOAuth2ConsentSessionGranted: fmt.Sprintf(queryFmtUpdateOAuth2ConsentSessionGranted, 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),
|
||||
sqlSelectOAuth2AccessTokenSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2AccessTokenSession),
|
||||
sqlRevokeOAuth2AccessTokenSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2AccessTokenSession),
|
||||
|
@ -96,19 +96,12 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
|
|||
sqlDeactivateOAuth2AccessTokenSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2AccessTokenSession),
|
||||
sqlDeactivateOAuth2AccessTokenSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2AccessTokenSession),
|
||||
|
||||
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),
|
||||
|
||||
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),
|
||||
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),
|
||||
|
||||
sqlInsertOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2OpenIDConnectSession),
|
||||
sqlSelectOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2OpenIDConnectSession),
|
||||
|
@ -117,8 +110,19 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
|
|||
sqlDeactivateOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2OpenIDConnectSession),
|
||||
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2OpenIDConnectSession),
|
||||
|
||||
sqlUpsertOAuth2BlacklistedJTI: fmt.Sprintf(queryFmtUpsertOAuth2BlacklistedJTI, tableOAuth2BlacklistedJTI),
|
||||
sqlSelectOAuth2BlacklistedJTI: fmt.Sprintf(queryFmtSelectOAuth2BlacklistedJTI, tableOAuth2BlacklistedJTI),
|
||||
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),
|
||||
|
||||
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),
|
||||
sqlSelectMigrations: fmt.Sprintf(queryFmtSelectMigrations, tableMigrations),
|
||||
|
@ -233,13 +237,18 @@ type SQLProvider struct {
|
|||
sqlDeactivateOAuth2AccessTokenSession string
|
||||
sqlDeactivateOAuth2AccessTokenSessionByRequestID string
|
||||
|
||||
// Table: oauth2_refresh_token_session.
|
||||
sqlInsertOAuth2RefreshTokenSession string
|
||||
sqlSelectOAuth2RefreshTokenSession string
|
||||
sqlRevokeOAuth2RefreshTokenSession string
|
||||
sqlRevokeOAuth2RefreshTokenSessionByRequestID string
|
||||
sqlDeactivateOAuth2RefreshTokenSession string
|
||||
sqlDeactivateOAuth2RefreshTokenSessionByRequestID string
|
||||
// Table: oauth2_openid_connect_session.
|
||||
sqlInsertOAuth2OpenIDConnectSession string
|
||||
sqlSelectOAuth2OpenIDConnectSession string
|
||||
sqlRevokeOAuth2OpenIDConnectSession string
|
||||
sqlRevokeOAuth2OpenIDConnectSessionByRequestID string
|
||||
sqlDeactivateOAuth2OpenIDConnectSession string
|
||||
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID string
|
||||
|
||||
// Table: oauth2_par_context.
|
||||
sqlInsertOAuth2PARContext string
|
||||
sqlSelectOAuth2PARContext string
|
||||
sqlRevokeOAuth2PARContext string
|
||||
|
||||
// Table: oauth2_pkce_request_session.
|
||||
sqlInsertOAuth2PKCERequestSession string
|
||||
|
@ -249,13 +258,13 @@ type SQLProvider struct {
|
|||
sqlDeactivateOAuth2PKCERequestSession string
|
||||
sqlDeactivateOAuth2PKCERequestSessionByRequestID string
|
||||
|
||||
// Table: oauth2_openid_connect_session.
|
||||
sqlInsertOAuth2OpenIDConnectSession string
|
||||
sqlSelectOAuth2OpenIDConnectSession string
|
||||
sqlRevokeOAuth2OpenIDConnectSession string
|
||||
sqlRevokeOAuth2OpenIDConnectSessionByRequestID string
|
||||
sqlDeactivateOAuth2OpenIDConnectSession string
|
||||
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID string
|
||||
// Table: oauth2_refresh_token_session.
|
||||
sqlInsertOAuth2RefreshTokenSession string
|
||||
sqlSelectOAuth2RefreshTokenSession string
|
||||
sqlRevokeOAuth2RefreshTokenSession string
|
||||
sqlRevokeOAuth2RefreshTokenSessionByRequestID string
|
||||
sqlDeactivateOAuth2RefreshTokenSession string
|
||||
sqlDeactivateOAuth2RefreshTokenSessionByRequestID string
|
||||
|
||||
sqlUpsertOAuth2BlacklistedJTI 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.
|
||||
func (p *SQLProvider) SaveUserOpaqueIdentifier(ctx context.Context, opaqueID model.UserOpaqueIdentifier) (err error) {
|
||||
if _, err = p.db.ExecContext(ctx, p.sqlInsertUserOpaqueIdentifier, opaqueID.Service, opaqueID.SectorID, opaqueID.Username, opaqueID.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)
|
||||
func (p *SQLProvider) SaveUserOpaqueIdentifier(ctx context.Context, subject model.UserOpaqueIdentifier) (err error) {
|
||||
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", subject.Username, subject.Identifier.String(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadUserOpaqueIdentifier selects an opaque user identifier from the database.
|
||||
func (p *SQLProvider) LoadUserOpaqueIdentifier(ctx context.Context, opaqueUUID uuid.UUID) (opaqueID *model.UserOpaqueIdentifier, err error) {
|
||||
opaqueID = &model.UserOpaqueIdentifier{}
|
||||
func (p *SQLProvider) LoadUserOpaqueIdentifier(ctx context.Context, identifier uuid.UUID) (subject *model.UserOpaqueIdentifier, err error) {
|
||||
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 {
|
||||
case errors.Is(err, sql.ErrNoRows):
|
||||
return nil, nil
|
||||
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.
|
||||
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
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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.
|
||||
func (p *SQLProvider) LoadUserOpaqueIdentifierBySignature(ctx context.Context, service, sectorID, username string) (opaqueID *model.UserOpaqueIdentifier, err error) {
|
||||
opaqueID = &model.UserOpaqueIdentifier{}
|
||||
func (p *SQLProvider) LoadUserOpaqueIdentifierBySignature(ctx context.Context, service, sectorID, username string) (subject *model.UserOpaqueIdentifier, err error) {
|
||||
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 {
|
||||
case errors.Is(err, sql.ErrNoRows):
|
||||
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.
|
||||
|
@ -505,22 +514,22 @@ func (p *SQLProvider) SaveOAuth2Session(ctx context.Context, sessionType OAuth2S
|
|||
var query string
|
||||
|
||||
switch sessionType {
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlInsertOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeAccessToken:
|
||||
query = p.sqlInsertOAuth2AccessTokenSession
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlInsertOAuth2RefreshTokenSession
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlInsertOAuth2PKCERequestSession
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlInsertOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeOpenIDConnect:
|
||||
query = p.sqlInsertOAuth2OpenIDConnectSession
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlInsertOAuth2PKCERequestSession
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlInsertOAuth2RefreshTokenSession
|
||||
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)
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -541,16 +550,16 @@ func (p *SQLProvider) RevokeOAuth2Session(ctx context.Context, sessionType OAuth
|
|||
var query string
|
||||
|
||||
switch sessionType {
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlRevokeOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeAccessToken:
|
||||
query = p.sqlRevokeOAuth2AccessTokenSession
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlRevokeOAuth2RefreshTokenSession
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlRevokeOAuth2PKCERequestSession
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlRevokeOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeOpenIDConnect:
|
||||
query = p.sqlRevokeOAuth2OpenIDConnectSession
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlRevokeOAuth2PKCERequestSession
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlRevokeOAuth2RefreshTokenSession
|
||||
default:
|
||||
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
|
||||
|
||||
switch sessionType {
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID
|
||||
case OAuth2SessionTypeAccessToken:
|
||||
query = p.sqlRevokeOAuth2AccessTokenSessionByRequestID
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlRevokeOAuth2RefreshTokenSessionByRequestID
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlRevokeOAuth2PKCERequestSessionByRequestID
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID
|
||||
case OAuth2SessionTypeOpenIDConnect:
|
||||
query = p.sqlRevokeOAuth2OpenIDConnectSessionByRequestID
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlRevokeOAuth2PKCERequestSessionByRequestID
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlRevokeOAuth2RefreshTokenSessionByRequestID
|
||||
default:
|
||||
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
|
||||
|
||||
switch sessionType {
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlDeactivateOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeAccessToken:
|
||||
query = p.sqlDeactivateOAuth2AccessTokenSession
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlDeactivateOAuth2RefreshTokenSession
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlDeactivateOAuth2PKCERequestSession
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlDeactivateOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeOpenIDConnect:
|
||||
query = p.sqlDeactivateOAuth2OpenIDConnectSession
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlDeactivateOAuth2PKCERequestSession
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlDeactivateOAuth2RefreshTokenSession
|
||||
default:
|
||||
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
|
||||
|
||||
switch sessionType {
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlDeactivateOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeAccessToken:
|
||||
query = p.sqlDeactivateOAuth2AccessTokenSessionByRequestID
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlDeactivateOAuth2RefreshTokenSessionByRequestID
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlDeactivateOAuth2PKCERequestSessionByRequestID
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlDeactivateOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeOpenIDConnect:
|
||||
query = p.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlDeactivateOAuth2PKCERequestSessionByRequestID
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlDeactivateOAuth2RefreshTokenSessionByRequestID
|
||||
default:
|
||||
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
|
||||
|
||||
switch sessionType {
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlSelectOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeAccessToken:
|
||||
query = p.sqlSelectOAuth2AccessTokenSession
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlSelectOAuth2RefreshTokenSession
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlSelectOAuth2PKCERequestSession
|
||||
case OAuth2SessionTypeAuthorizeCode:
|
||||
query = p.sqlSelectOAuth2AuthorizeCodeSession
|
||||
case OAuth2SessionTypeOpenIDConnect:
|
||||
query = p.sqlSelectOAuth2OpenIDConnectSession
|
||||
case OAuth2SessionTypePKCEChallenge:
|
||||
query = p.sqlSelectOAuth2PKCERequestSession
|
||||
case OAuth2SessionTypeRefreshToken:
|
||||
query = p.sqlSelectOAuth2RefreshTokenSession
|
||||
default:
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
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 {
|
||||
|
@ -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.
|
||||
func (p *SQLProvider) SaveTOTPConfiguration(ctx context.Context, config model.TOTPConfiguration) (err error) {
|
||||
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,
|
||||
|
@ -815,7 +863,7 @@ func (p *SQLProvider) LoadTOTPConfiguration(ctx context.Context, username string
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -870,7 +918,7 @@ func (p *SQLProvider) LoadWebauthnUser(ctx context.Context, rpid, username strin
|
|||
// SaveWebauthnDevice saves a registered Webauthn device.
|
||||
func (p *SQLProvider) SaveWebauthnDevice(ctx context.Context, device model.WebauthnDevice) (err error) {
|
||||
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,
|
||||
|
|
|
@ -92,13 +92,6 @@ func NewPostgreSQLProvider(config *schema.Configuration, caCertPool *x509.CertPo
|
|||
provider.sqlUpdateOAuth2ConsentSessionGranted = provider.db.Rebind(provider.sqlUpdateOAuth2ConsentSessionGranted)
|
||||
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.sqlRevokeOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlRevokeOAuth2AccessTokenSession)
|
||||
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.sqlSelectOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlSelectOAuth2AccessTokenSession)
|
||||
|
||||
provider.sqlInsertOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlInsertOAuth2RefreshTokenSession)
|
||||
provider.sqlRevokeOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSession)
|
||||
provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID)
|
||||
provider.sqlDeactivateOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSession)
|
||||
provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID)
|
||||
provider.sqlSelectOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlSelectOAuth2RefreshTokenSession)
|
||||
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.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.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.sqlSelectOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlSelectOAuth2PKCERequestSession)
|
||||
|
||||
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.sqlInsertOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlInsertOAuth2RefreshTokenSession)
|
||||
provider.sqlRevokeOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSession)
|
||||
provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID)
|
||||
provider.sqlDeactivateOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSession)
|
||||
provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID)
|
||||
provider.sqlSelectOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlSelectOAuth2RefreshTokenSession)
|
||||
|
||||
provider.sqlSelectOAuth2BlacklistedJTI = provider.db.Rebind(provider.sqlSelectOAuth2BlacklistedJTI)
|
||||
|
||||
|
|
|
@ -327,6 +327,19 @@ const (
|
|||
SET active = FALSE
|
||||
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 = `
|
||||
SELECT id, signature, expires_at
|
||||
FROM %s
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
version: '3'
|
||||
services:
|
||||
envoy:
|
||||
image: envoyproxy/envoy:v1.25.1
|
||||
image: envoyproxy/envoy:v1.25.2
|
||||
volumes:
|
||||
- ./example/compose/envoy/envoy.yaml:/etc/envoy/envoy.yaml
|
||||
- ./common/pki:/pki
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
version: '3'
|
||||
services:
|
||||
k3d:
|
||||
image: ghcr.io/k3d-io/k3d:5.4.7-dind
|
||||
image: ghcr.io/k3d-io/k3d:5.4.8-dind
|
||||
volumes:
|
||||
- './example/kube:/authelia'
|
||||
- './example/kube/authelia/configs/configuration.yml:/configmaps/authelia/configuration.yml'
|
||||
|
|
|
@ -164,7 +164,7 @@ http {
|
|||
# to the virtual endpoint introduced by nginx and declared in the next block.
|
||||
location / {
|
||||
## 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 $target_url $scheme://$http_host$request_uri;
|
||||
|
@ -209,7 +209,7 @@ http {
|
|||
}
|
||||
|
||||
# Virtual endpoint forwarding requests to Authelia server.
|
||||
location /authelia {
|
||||
location /internal/authelia/authz {
|
||||
## Essential Proxy Configuration
|
||||
internal;
|
||||
proxy_pass $upstream_authelia;
|
||||
|
@ -250,7 +250,7 @@ http {
|
|||
# Used by suites to test the forwarded users and groups headers produced by Authelia.
|
||||
location /headers {
|
||||
## 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 $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.
|
||||
location / {
|
||||
## 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 $target_url $scheme://$http_host$request_uri;
|
||||
|
@ -346,7 +346,7 @@ http {
|
|||
}
|
||||
|
||||
# Virtual endpoint forwarding requests to Authelia server.
|
||||
location /authelia {
|
||||
location /internal/authelia/authz {
|
||||
## Essential Proxy Configuration
|
||||
internal;
|
||||
proxy_pass $upstream_authelia;
|
||||
|
@ -356,7 +356,6 @@ http {
|
|||
# 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.
|
||||
# See https://expressjs.com/en/guide/behind-proxies.html
|
||||
proxy_set_header X-Original-Method $request_method;
|
||||
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
|
|
@ -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_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_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 (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_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_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 (webauthn_devices): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
||||
|
||||
|
|
|
@ -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.
|
||||
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 {
|
||||
if IsStringInSlice(n, haystack) {
|
||||
if isInSlice(n, haystack) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,9 +29,9 @@
|
|||
"@fortawesome/free-regular-svg-icons": "6.3.0",
|
||||
"@fortawesome/free-solid-svg-icons": "6.3.0",
|
||||
"@fortawesome/react-fontawesome": "0.2.0",
|
||||
"@mui/icons-material": "5.11.9",
|
||||
"@mui/material": "5.11.10",
|
||||
"@mui/styles": "5.11.9",
|
||||
"@mui/icons-material": "5.11.11",
|
||||
"@mui/material": "5.11.11",
|
||||
"@mui/styles": "5.11.11",
|
||||
"@simplewebauthn/browser": "7.1.0",
|
||||
"@simplewebauthn/typescript-types": "7.0.0",
|
||||
"axios": "1.3.4",
|
||||
|
@ -154,7 +154,7 @@
|
|||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "14.0.0",
|
||||
"@types/jest": "29.4.0",
|
||||
"@types/node": "18.14.2",
|
||||
"@types/node": "18.14.6",
|
||||
"@types/qrcode.react": "1.0.2",
|
||||
"@types/react": "18.0.28",
|
||||
"@types/react-dom": "18.0.11",
|
||||
|
@ -162,10 +162,10 @@
|
|||
"@typescript-eslint/eslint-plugin": "5.54.0",
|
||||
"@typescript-eslint/parser": "5.54.0",
|
||||
"@vitejs/plugin-react": "3.1.0",
|
||||
"esbuild": "0.17.10",
|
||||
"esbuild": "0.17.11",
|
||||
"esbuild-jest": "0.5.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-formatter-rdjson": "1.0.5",
|
||||
"eslint-import-resolver-typescript": "3.5.3",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue