Merge remote-tracking branch 'origin/master' into refactor-trust-provider
# Conflicts: # docs/data/configkeys.jsonpull/4713/head
commit
1a52dde169
|
@ -15,7 +15,7 @@ RUN yarn global add pnpm && \
|
||||||
# =======================================
|
# =======================================
|
||||||
# ===== Build image for the backend =====
|
# ===== Build image for the backend =====
|
||||||
# =======================================
|
# =======================================
|
||||||
FROM golang:1.20.1-alpine AS builder-backend
|
FROM golang:1.20.2-alpine AS builder-backend
|
||||||
|
|
||||||
WORKDIR /go/src/app
|
WORKDIR /go/src/app
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ RUN yarn install --frozen-lockfile && yarn build
|
||||||
# =======================================
|
# =======================================
|
||||||
# ===== Build image for the backend =====
|
# ===== Build image for the backend =====
|
||||||
# =======================================
|
# =======================================
|
||||||
FROM golang:1.20.1-alpine AS builder-backend
|
FROM golang:1.20.2-alpine AS builder-backend
|
||||||
|
|
||||||
WORKDIR /go/src/app
|
WORKDIR /go/src/app
|
||||||
|
|
||||||
|
|
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
|
Authelia is an open-source authentication and authorization server providing 2-factor authentication and single
|
||||||
sign-on (SSO) for your applications via a web portal.
|
sign-on (SSO) for your applications via a web portal.
|
||||||
contact:
|
contact:
|
||||||
name: Authelia Support
|
name: Support
|
||||||
url: https://www.authelia.com/contact/
|
url: https://www.authelia.com/contact/
|
||||||
email: team@authelia.com
|
email: team@authelia.com
|
||||||
license:
|
license:
|
||||||
|
@ -2940,9 +2940,9 @@ components:
|
||||||
- "address"
|
- "address"
|
||||||
- "phone"
|
- "phone"
|
||||||
openid.spec.IntrospectionRequest:
|
openid.spec.IntrospectionRequest:
|
||||||
type: object
|
|
||||||
required:
|
required:
|
||||||
- "token"
|
- "token"
|
||||||
|
type: object
|
||||||
properties:
|
properties:
|
||||||
token:
|
token:
|
||||||
description: >
|
description: >
|
||||||
|
@ -2952,8 +2952,8 @@ components:
|
||||||
this is the "refresh_token" value returned from the token endpoint
|
this is the "refresh_token" value returned from the token endpoint
|
||||||
as defined in OAuth 2.0 [RFC6749], Section 5.1. Other token types
|
as defined in OAuth 2.0 [RFC6749], Section 5.1. Other token types
|
||||||
are outside the scope of this specification.
|
are outside the scope of this specification.
|
||||||
type: string
|
|
||||||
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
|
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
|
||||||
|
type: string
|
||||||
token_type_hint:
|
token_type_hint:
|
||||||
description: >
|
description: >
|
||||||
A hint about the type of the token submitted for
|
A hint about the type of the token submitted for
|
||||||
|
@ -2965,27 +2965,61 @@ components:
|
||||||
is able to detect the token type automatically. Values for this
|
is able to detect the token type automatically. Values for this
|
||||||
field are defined in the "OAuth Token Type Hints" registry defined
|
field are defined in the "OAuth Token Type Hints" registry defined
|
||||||
in OAuth Token Revocation [RFC7009].
|
in OAuth Token Revocation [RFC7009].
|
||||||
type: string
|
|
||||||
example: "access_token"
|
|
||||||
enum:
|
enum:
|
||||||
- "access_token"
|
- "access_token"
|
||||||
- "refresh_token"
|
- "refresh_token"
|
||||||
|
example: "access_token"
|
||||||
|
type: string
|
||||||
openid.spec.AccessRequest.ClientAuth:
|
openid.spec.AccessRequest.ClientAuth:
|
||||||
|
oneOf:
|
||||||
|
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth.Base'
|
||||||
|
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth.Secret'
|
||||||
|
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth.JWT'
|
||||||
|
openid.spec.AccessRequest.ClientAuth.Base:
|
||||||
|
required:
|
||||||
|
- "client_id"
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
client_id:
|
client_id:
|
||||||
description: >
|
description: >
|
||||||
REQUIRED if the client is not authenticating with the
|
REQUIRED if the client is not authenticating with the authorization server as described in
|
||||||
authorization server as described in Section 3.2.1. of [RFC6749].
|
Section 3.2.1. of [RFC6749]. The client identifier as described in Section 2.2 of [RFC6749].
|
||||||
The client identifier as described in Section 2.2 of [RFC6749].
|
example: "my_client"
|
||||||
type: string
|
type: string
|
||||||
example: "authelia_dc_mn123kjn12kj3123njk"
|
openid.spec.AccessRequest.ClientAuth.Secret:
|
||||||
|
required:
|
||||||
|
- "client_secret"
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
client_secret:
|
client_secret:
|
||||||
description: >
|
description: >
|
||||||
REQUIRED. The client secret. The client MAY omit the
|
REQUIRED. The client secret. The client MAY omit the
|
||||||
parameter if the client secret is an empty string.
|
parameter if the client secret is an empty string.
|
||||||
type: string
|
|
||||||
format: password
|
format: password
|
||||||
|
type: string
|
||||||
|
openid.spec.AccessRequest.ClientAuth.JWT:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth.Base'
|
||||||
|
- type: object
|
||||||
|
required:
|
||||||
|
- "client_assertion"
|
||||||
|
- "client_assertion_type"
|
||||||
|
properties:
|
||||||
|
client_assertion:
|
||||||
|
description: >
|
||||||
|
The value of the client_assertion_type parameter MUST be
|
||||||
|
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
||||||
|
enum:
|
||||||
|
- "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
||||||
|
example: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
||||||
|
type: string
|
||||||
|
client_assertion_type:
|
||||||
|
description: >
|
||||||
|
A JWT signed with HS256 using the client secret value or RS256 using a registered public key.
|
||||||
|
Theoretically a properly formed JWT signed using HS256 with the client secret as the HMAC key should
|
||||||
|
work but this has not been tested.
|
||||||
|
format: password
|
||||||
|
type: string
|
||||||
openid.spec.AccessRequest.AuthorizationCodeFlow:
|
openid.spec.AccessRequest.AuthorizationCodeFlow:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
|
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
|
||||||
|
@ -2995,22 +3029,22 @@ components:
|
||||||
- "grant_type"
|
- "grant_type"
|
||||||
properties:
|
properties:
|
||||||
grant_type:
|
grant_type:
|
||||||
description: Value MUST be set to "urn:ietf:params:oauth:grant-type:device_code".
|
description: Value MUST be set to "code".
|
||||||
type: string
|
|
||||||
enum:
|
enum:
|
||||||
- "authorization_code"
|
- "authorization_code"
|
||||||
|
type: string
|
||||||
code:
|
code:
|
||||||
description: The Authorization Code.
|
description: The Authorization Code.
|
||||||
type: string
|
|
||||||
example: "authelia_ac_1j2kn3knj12n3kj12n"
|
example: "authelia_ac_1j2kn3knj12n3kj12n"
|
||||||
|
type: string
|
||||||
code_verifier:
|
code_verifier:
|
||||||
description: The Authorization Code Verifier (PKCE).
|
description: The Authorization Code Verifier (PKCE).
|
||||||
type: string
|
|
||||||
example: "88a25754f7c0b3b3b88cf6cd4e29e8356b160524fdc1cb329a94471825628fd3"
|
example: "88a25754f7c0b3b3b88cf6cd4e29e8356b160524fdc1cb329a94471825628fd3"
|
||||||
|
type: string
|
||||||
redirect_uri:
|
redirect_uri:
|
||||||
description: The original Redirect URI used in the Authorization Request.
|
description: The original Redirect URI used in the Authorization Request.
|
||||||
type: string
|
|
||||||
example: "https://app.example.com/oidc/callback"
|
example: "https://app.example.com/oidc/callback"
|
||||||
|
type: string
|
||||||
openid.spec.AccessRequest.DeviceCodeFlow:
|
openid.spec.AccessRequest.DeviceCodeFlow:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
|
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
|
||||||
|
@ -3021,13 +3055,13 @@ components:
|
||||||
properties:
|
properties:
|
||||||
grant_type:
|
grant_type:
|
||||||
description: Value MUST be set to "urn:ietf:params:oauth:grant-type:device_code".
|
description: Value MUST be set to "urn:ietf:params:oauth:grant-type:device_code".
|
||||||
type: string
|
|
||||||
enum:
|
enum:
|
||||||
- "urn:ietf:params:oauth:grant-type:device_code"
|
- "urn:ietf:params:oauth:grant-type:device_code"
|
||||||
|
type: string
|
||||||
device_code:
|
device_code:
|
||||||
description: The Device Authorization Code.
|
description: The Device Authorization Code.
|
||||||
type: string
|
|
||||||
example: "authelia_dc_mn123kjn12kj3123njk"
|
example: "authelia_dc_mn123kjn12kj3123njk"
|
||||||
|
type: string
|
||||||
openid.spec.AccessRequest.RefreshTokenFlow:
|
openid.spec.AccessRequest.RefreshTokenFlow:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
|
- $ref: '#/components/schemas/openid.spec.AccessRequest.ClientAuth'
|
||||||
|
@ -3038,12 +3072,13 @@ components:
|
||||||
properties:
|
properties:
|
||||||
grant_type:
|
grant_type:
|
||||||
description: Value MUST be set to "refresh_token".
|
description: Value MUST be set to "refresh_token".
|
||||||
type: string
|
|
||||||
enum:
|
enum:
|
||||||
- "refresh_token"
|
- "refresh_token"
|
||||||
|
type: string
|
||||||
refresh_token:
|
refresh_token:
|
||||||
description: The Refresh Token.
|
description: The Refresh Token.
|
||||||
example: "authelia_rt_1n2j3kihn12kj3n12k"
|
example: "authelia_rt_1n2j3kihn12kj3n12k"
|
||||||
|
type: string
|
||||||
scope:
|
scope:
|
||||||
description: >
|
description: >
|
||||||
The scope of the access request as described by
|
The scope of the access request as described by
|
||||||
|
@ -3051,20 +3086,30 @@ components:
|
||||||
not originally granted by the resource owner, and if omitted is
|
not originally granted by the resource owner, and if omitted is
|
||||||
treated as equal to the scope originally granted by the
|
treated as equal to the scope originally granted by the
|
||||||
resource owner.
|
resource owner.
|
||||||
|
example: "openid profile groups"
|
||||||
|
type: string
|
||||||
openid.spec.AccessResponse:
|
openid.spec.AccessResponse:
|
||||||
type: object
|
type: object
|
||||||
|
required:
|
||||||
|
- "access_token"
|
||||||
|
- "token_type"
|
||||||
|
- "expires_in"
|
||||||
properties:
|
properties:
|
||||||
access_token:
|
access_token:
|
||||||
description: The access token issued by the authorization server.
|
description: The access token issued by the authorization server.
|
||||||
type: string
|
|
||||||
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
|
example: "authelia_at_cr4i4EtTn2F4k6mX4XzxbsBewkxCGn"
|
||||||
refresh_token:
|
|
||||||
type: string
|
type: string
|
||||||
|
id_token:
|
||||||
|
description: The id token issued by the authorization server.
|
||||||
|
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
||||||
|
type: string
|
||||||
|
refresh_token:
|
||||||
description: >
|
description: >
|
||||||
The refresh token, which can be used to obtain new access tokens using the
|
The refresh token, which can be used to obtain new access tokens using the
|
||||||
same authorization grant as described in Section 6.
|
same authorization grant as described in Section 6.
|
||||||
token_type:
|
example: "authelia_rt_kGBoSMbfVGP2RR6Kvujv3Xg7uXV2i"
|
||||||
type: string
|
type: string
|
||||||
|
token_type:
|
||||||
description: >
|
description: >
|
||||||
The access token type provides the client with the information
|
The access token type provides the client with the information
|
||||||
required to successfully utilize the access token to make a protected
|
required to successfully utilize the access token to make a protected
|
||||||
|
@ -3073,21 +3118,26 @@ components:
|
||||||
type.
|
type.
|
||||||
enum:
|
enum:
|
||||||
- "bearer"
|
- "bearer"
|
||||||
|
example: "bearer"
|
||||||
|
type: string
|
||||||
expires_in:
|
expires_in:
|
||||||
type: integer
|
|
||||||
description: >
|
description: >
|
||||||
The lifetime in seconds of the access token. For
|
The lifetime in seconds of the access token. For
|
||||||
example, the value "3600" denotes that the access token will
|
example, the value "3600" denotes that the access token will
|
||||||
expire in one hour from the time the response was generated.
|
expire in one hour from the time the response was generated.
|
||||||
If omitted, the authorization server SHOULD provide the
|
If omitted, the authorization server SHOULD provide the
|
||||||
expiration time via other means or document the default value.
|
expiration time via other means or document the default value.
|
||||||
|
example: 3600
|
||||||
|
type: integer
|
||||||
state:
|
state:
|
||||||
type: string
|
|
||||||
description: Exactly the state value passed in the authorization request if present.
|
description: Exactly the state value passed in the authorization request if present.
|
||||||
scope:
|
example: "5dVZhNfri5XZS6wadskuzUk4MHYCvEcUgidjMeBjsktAhY7EKB"
|
||||||
type: string
|
type: string
|
||||||
|
scope:
|
||||||
description: >
|
description: >
|
||||||
The scope of the access token as described by Section 3.3 if it differs from the requested scope.
|
The scope of the access token as described by Section 3.3 if it differs from the requested scope.
|
||||||
|
example: "openid profile groups"
|
||||||
|
type: string
|
||||||
openid.spec.AuthorizeRequest:
|
openid.spec.AuthorizeRequest:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
@ -3098,14 +3148,14 @@ components:
|
||||||
properties:
|
properties:
|
||||||
scope:
|
scope:
|
||||||
description: The requested scope.
|
description: The requested scope.
|
||||||
type: string
|
|
||||||
example: "openid profile groups"
|
example: "openid profile groups"
|
||||||
|
type: string
|
||||||
response_type:
|
response_type:
|
||||||
$ref: '#/components/schemas/openid.spec.ResponseType'
|
$ref: '#/components/schemas/openid.spec.ResponseType'
|
||||||
client_id:
|
client_id:
|
||||||
description: The OAuth 2.0 client identifier.
|
description: The OAuth 2.0 client identifier.
|
||||||
type: string
|
|
||||||
example: "app"
|
example: "app"
|
||||||
|
type: string
|
||||||
redirect_uri:
|
redirect_uri:
|
||||||
description: >
|
description: >
|
||||||
Redirection URI to which the response will be sent. This URI MUST exactly match one of the
|
Redirection URI to which the response will be sent. This URI MUST exactly match one of the
|
||||||
|
@ -3115,15 +3165,15 @@ components:
|
||||||
that the Client Type is confidential, as defined in Section 2.1 of OAuth 2.0, and provided the OP
|
that the Client Type is confidential, as defined in Section 2.1 of OAuth 2.0, and provided the OP
|
||||||
allows the use of http Redirection URIs in this case. The Redirection URI MAY use an alternate
|
allows the use of http Redirection URIs in this case. The Redirection URI MAY use an alternate
|
||||||
scheme, such as one that is intended to identify a callback into a native application.
|
scheme, such as one that is intended to identify a callback into a native application.
|
||||||
type: string
|
|
||||||
example: "https://app.example.com"
|
example: "https://app.example.com"
|
||||||
|
type: string
|
||||||
state:
|
state:
|
||||||
description: >
|
description: >
|
||||||
Opaque value used to maintain state between the request and the callback. Typically, Cross-Site
|
Opaque value used to maintain state between the request and the callback. Typically, Cross-Site
|
||||||
Request Forgery (CSRF, XSRF) mitigation is done by cryptographically binding the value of this
|
Request Forgery (CSRF, XSRF) mitigation is done by cryptographically binding the value of this
|
||||||
parameter with a browser cookie.
|
parameter with a browser cookie.
|
||||||
type: string
|
|
||||||
example: "oV84Vsy7wyCgRk2h4aZBmXZq4q3g2f"
|
example: "oV84Vsy7wyCgRk2h4aZBmXZq4q3g2f"
|
||||||
|
type: string
|
||||||
response_mode:
|
response_mode:
|
||||||
$ref: '#/components/schemas/openid.spec.ResponseMode'
|
$ref: '#/components/schemas/openid.spec.ResponseMode'
|
||||||
nonce:
|
nonce:
|
||||||
|
@ -3132,14 +3182,23 @@ components:
|
||||||
The value is passed through unmodified from the Authentication Request to the ID Token. Sufficient
|
The value is passed through unmodified from the Authentication Request to the ID Token. Sufficient
|
||||||
entropy MUST be present in the nonce values used to prevent attackers from guessing values. For
|
entropy MUST be present in the nonce values used to prevent attackers from guessing values. For
|
||||||
implementation notes, see Section 15.5.2.
|
implementation notes, see Section 15.5.2.
|
||||||
type: string
|
|
||||||
example: "TRMLqchoKGQNcooXvBvUy9PtmLdJGf"
|
example: "TRMLqchoKGQNcooXvBvUy9PtmLdJGf"
|
||||||
|
type: string
|
||||||
display:
|
display:
|
||||||
$ref: '#/components/schemas/openid.spec.DisplayType'
|
$ref: '#/components/schemas/openid.spec.DisplayType'
|
||||||
prompt:
|
prompt:
|
||||||
description: >
|
description: >
|
||||||
Not Supported: Space delimited, case sensitive list of ASCII string values that specifies whether
|
Not Supported: Space delimited, case sensitive list of ASCII string values that specifies whether
|
||||||
the Authorization Server prompts the End-User for reauthentication and consent.
|
the Authorization Server prompts the End-User for reauthentication and consent.
|
||||||
|
enum:
|
||||||
|
- "none"
|
||||||
|
- "login"
|
||||||
|
- "consent"
|
||||||
|
- "select_account"
|
||||||
|
- "login consent"
|
||||||
|
- "login select_account"
|
||||||
|
- "consent select_account"
|
||||||
|
example: "consent"
|
||||||
type: string
|
type: string
|
||||||
max_age:
|
max_age:
|
||||||
description: >
|
description: >
|
||||||
|
@ -3217,34 +3276,32 @@ components:
|
||||||
description: >
|
description: >
|
||||||
A Subject Identifier is a locally unique and never reassigned identifier within the Issuer for the
|
A Subject Identifier is a locally unique and never reassigned identifier within the Issuer for the
|
||||||
End-User, which is intended to be consumed by the Client.
|
End-User, which is intended to be consumed by the Client.
|
||||||
type: string
|
|
||||||
enum:
|
enum:
|
||||||
- "public"
|
- "public"
|
||||||
- "pairwise"
|
- "pairwise"
|
||||||
|
type: string
|
||||||
openid.spec.ClientAuthMethod:
|
openid.spec.ClientAuthMethod:
|
||||||
description: The OAuth 2.0 / OpenID Connect 1.0 Client Authentication Method.
|
description: The OAuth 2.0 / OpenID Connect 1.0 Client Authentication Method.
|
||||||
type: string
|
|
||||||
enum:
|
enum:
|
||||||
- "client_secret_basic"
|
- "client_secret_basic"
|
||||||
- "client_secret_post"
|
- "client_secret_post"
|
||||||
- "client_secret_jwt"
|
- "client_secret_jwt"
|
||||||
- "private_key_jwt"
|
- "private_key_jwt"
|
||||||
- "none"
|
- "none"
|
||||||
|
type: string
|
||||||
openid.spec.DisplayType:
|
openid.spec.DisplayType:
|
||||||
description: >
|
description: >
|
||||||
ASCII string value that specifies how the Authorization Server displays the authentication and consent user
|
ASCII string value that specifies how the Authorization Server displays the authentication and consent user
|
||||||
interface pages to the End-User.
|
interface pages to the End-User.
|
||||||
type: string
|
|
||||||
example: "page"
|
|
||||||
enum:
|
enum:
|
||||||
- "page"
|
- "page"
|
||||||
- "popup"
|
- "popup"
|
||||||
- "touch"
|
- "touch"
|
||||||
- "wap"
|
- "wap"
|
||||||
|
example: "page"
|
||||||
|
type: string
|
||||||
openid.spec.ResponseType:
|
openid.spec.ResponseType:
|
||||||
description: The OAuth 2.0 / OpenID Connect 1.0 Response Type.
|
description: The OAuth 2.0 / OpenID Connect 1.0 Response Type.
|
||||||
type: string
|
|
||||||
example: "code"
|
|
||||||
enum:
|
enum:
|
||||||
- "code"
|
- "code"
|
||||||
- "id_token"
|
- "id_token"
|
||||||
|
@ -3254,21 +3311,21 @@ components:
|
||||||
- "token id_token"
|
- "token id_token"
|
||||||
- "code id_token token"
|
- "code id_token token"
|
||||||
- "none"
|
- "none"
|
||||||
|
example: "code"
|
||||||
|
type: string
|
||||||
openid.spec.ResponseMode:
|
openid.spec.ResponseMode:
|
||||||
description: >
|
description: >
|
||||||
Informs the Authorization Server of the mechanism to be used for returning parameters from the Authorization
|
Informs the Authorization Server of the mechanism to be used for returning parameters from the Authorization
|
||||||
Endpoint. This use of this parameter is NOT RECOMMENDED when the Response Mode that would be requested is
|
Endpoint. This use of this parameter is NOT RECOMMENDED when the Response Mode that would be requested is
|
||||||
the default mode specified for the Response Type.
|
the default mode specified for the Response Type.
|
||||||
type: string
|
|
||||||
example: "query"
|
|
||||||
enum:
|
enum:
|
||||||
- "query"
|
- "query"
|
||||||
- "fragment"
|
- "fragment"
|
||||||
- "form_post"
|
- "form_post"
|
||||||
|
example: "query"
|
||||||
|
type: string
|
||||||
openid.spec.GrantType:
|
openid.spec.GrantType:
|
||||||
description: The OAuth 2.0 / OpenID Connect 1.0 Grant Type.
|
description: The OAuth 2.0 / OpenID Connect 1.0 Grant Type.
|
||||||
type: string
|
|
||||||
example: "authorization_code"
|
|
||||||
enum:
|
enum:
|
||||||
- "authorization_code"
|
- "authorization_code"
|
||||||
- "refresh_token"
|
- "refresh_token"
|
||||||
|
@ -3276,21 +3333,23 @@ components:
|
||||||
- "password"
|
- "password"
|
||||||
- "client_credentials"
|
- "client_credentials"
|
||||||
- "urn:ietf:params:oauth:grant-type:device_code"
|
- "urn:ietf:params:oauth:grant-type:device_code"
|
||||||
|
example: "authorization_code"
|
||||||
|
type: string
|
||||||
openid.spec.CodeChallengeMethod:
|
openid.spec.CodeChallengeMethod:
|
||||||
description: The RFC7636 Code Challenge Verifier Method.
|
description: The RFC7636 Code Challenge Verifier Method.
|
||||||
type: string
|
|
||||||
example: "S256"
|
|
||||||
enum:
|
enum:
|
||||||
- "plain"
|
- "plain"
|
||||||
- "S256"
|
- "S256"
|
||||||
|
example: "S256"
|
||||||
|
type: string
|
||||||
openid.spec.ClaimType:
|
openid.spec.ClaimType:
|
||||||
description: The representation of claims.
|
description: The representation of claims.
|
||||||
type: string
|
|
||||||
example: "normal"
|
|
||||||
enum:
|
enum:
|
||||||
- "normal"
|
- "normal"
|
||||||
- "aggregated"
|
- "aggregated"
|
||||||
- "distributed"
|
- "distributed"
|
||||||
|
example: "normal"
|
||||||
|
type: string
|
||||||
jose.spec.None:
|
jose.spec.None:
|
||||||
description: The JSON Web Signature Algorithm
|
description: The JSON Web Signature Algorithm
|
||||||
type: string
|
type: string
|
||||||
|
@ -3298,13 +3357,12 @@ components:
|
||||||
- "none"
|
- "none"
|
||||||
jose.spec.JWS.None:
|
jose.spec.JWS.None:
|
||||||
description: The JSON Web Signature Algorithm
|
description: The JSON Web Signature Algorithm
|
||||||
type: string
|
|
||||||
oneOf:
|
oneOf:
|
||||||
- $ref: '#/components/schemas/jose.spec.None'
|
- $ref: '#/components/schemas/jose.spec.None'
|
||||||
- $ref: '#/components/schemas/jose.spec.jws'
|
- $ref: '#/components/schemas/jose.spec.jws'
|
||||||
|
type: string
|
||||||
jose.spec.jws:
|
jose.spec.jws:
|
||||||
description: The JSON Web Signature Algorithm
|
description: The JSON Web Signature Algorithm
|
||||||
type: string
|
|
||||||
enum:
|
enum:
|
||||||
- "HS256"
|
- "HS256"
|
||||||
- "HS384"
|
- "HS384"
|
||||||
|
@ -3318,9 +3376,9 @@ components:
|
||||||
- "PS256"
|
- "PS256"
|
||||||
- "PS384"
|
- "PS384"
|
||||||
- "PS512"
|
- "PS512"
|
||||||
|
type: string
|
||||||
jose.spec.JWE.alg:
|
jose.spec.JWE.alg:
|
||||||
description: The JSON Web Encryption Algorithm (CEK)
|
description: The JSON Web Encryption Algorithm (CEK)
|
||||||
type: string
|
|
||||||
enum:
|
enum:
|
||||||
- "RSA1_5"
|
- "RSA1_5"
|
||||||
- "RSA-OAEP"
|
- "RSA-OAEP"
|
||||||
|
@ -3339,9 +3397,9 @@ components:
|
||||||
- "PBES2-HS256+A128KW"
|
- "PBES2-HS256+A128KW"
|
||||||
- "PBES2-HS384+A192KW"
|
- "PBES2-HS384+A192KW"
|
||||||
- "PBES2-HS512+A256KW"
|
- "PBES2-HS512+A256KW"
|
||||||
|
type: string
|
||||||
jose.spec.JWE.enc:
|
jose.spec.JWE.enc:
|
||||||
description: The JSON Web Encryption Algorithm (Claims)
|
description: The JSON Web Encryption Algorithm (Claims)
|
||||||
type: string
|
|
||||||
enum:
|
enum:
|
||||||
- "A128CBC-HS256"
|
- "A128CBC-HS256"
|
||||||
- "A192CBC-HS384"
|
- "A192CBC-HS384"
|
||||||
|
@ -3350,6 +3408,7 @@ components:
|
||||||
- "A256CBC"
|
- "A256CBC"
|
||||||
- "A128GCM"
|
- "A128GCM"
|
||||||
- "A256GCM"
|
- "A256GCM"
|
||||||
|
type: string
|
||||||
jose.spec.JWK.base:
|
jose.spec.JWK.base:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -3359,21 +3418,20 @@ components:
|
||||||
the public key. The "use" parameter is employed to indicate whether
|
the public key. The "use" parameter is employed to indicate whether
|
||||||
a public key is used for encrypting data or verifying the signature
|
a public key is used for encrypting data or verifying the signature
|
||||||
on data.
|
on data.
|
||||||
type: string
|
|
||||||
example: "sig"
|
|
||||||
enum:
|
enum:
|
||||||
- "sig"
|
- "sig"
|
||||||
- "enc"
|
- "enc"
|
||||||
|
example: "sig"
|
||||||
|
type: string
|
||||||
key_ops:
|
key_ops:
|
||||||
description: >
|
description: >
|
||||||
The "key_ops" (key operations) parameter identifies the operation(s)
|
The "key_ops" (key operations) parameter identifies the operation(s)
|
||||||
for which the key is intended to be used. The "key_ops" parameter is
|
for which the key is intended to be used. The "key_ops" parameter is
|
||||||
intended for use cases in which public, private, or symmetric keys
|
intended for use cases in which public, private, or symmetric keys
|
||||||
may be present.
|
may be present.
|
||||||
type: array
|
|
||||||
example: ["sign"]
|
example: ["sign"]
|
||||||
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
|
||||||
enum:
|
enum:
|
||||||
- "sign"
|
- "sign"
|
||||||
- "verify"
|
- "verify"
|
||||||
|
@ -3383,6 +3441,7 @@ components:
|
||||||
- "unwrapKey"
|
- "unwrapKey"
|
||||||
- "deriveKey"
|
- "deriveKey"
|
||||||
- "deriveBits"
|
- "deriveBits"
|
||||||
|
type: string
|
||||||
kid:
|
kid:
|
||||||
description: >
|
description: >
|
||||||
The "kid" (key ID) parameter is used to match a specific key. This
|
The "kid" (key ID) parameter is used to match a specific key. This
|
||||||
|
@ -3427,8 +3486,8 @@ components:
|
||||||
OPTIONAL.
|
OPTIONAL.
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
|
||||||
format: byte
|
format: byte
|
||||||
|
type: string
|
||||||
x5t:
|
x5t:
|
||||||
description: >
|
description: >
|
||||||
The "x5t" (X.509 certificate SHA-1 thumbprint) parameter is a
|
The "x5t" (X.509 certificate SHA-1 thumbprint) parameter is a
|
||||||
|
@ -3437,8 +3496,8 @@ components:
|
||||||
thumbprints are also sometimes known as certificate fingerprints.
|
thumbprints are also sometimes known as certificate fingerprints.
|
||||||
The key in the certificate MUST match the public key represented by
|
The key in the certificate MUST match the public key represented by
|
||||||
other members of the JWK. Use of this member is OPTIONAL.
|
other members of the JWK. Use of this member is OPTIONAL.
|
||||||
type: string
|
|
||||||
format: byte
|
format: byte
|
||||||
|
type: string
|
||||||
x5t#S256:
|
x5t#S256:
|
||||||
description: >
|
description: >
|
||||||
The "x5t#S256" (X.509 certificate SHA-256 thumbprint) parameter is a
|
The "x5t#S256" (X.509 certificate SHA-256 thumbprint) parameter is a
|
||||||
|
@ -3447,17 +3506,17 @@ components:
|
||||||
thumbprints are also sometimes known as certificate fingerprints.
|
thumbprints are also sometimes known as certificate fingerprints.
|
||||||
The key in the certificate MUST match the public key represented by
|
The key in the certificate MUST match the public key represented by
|
||||||
other members of the JWK. Use of this member is OPTIONAL.
|
other members of the JWK. Use of this member is OPTIONAL.
|
||||||
type: string
|
|
||||||
format: byte
|
format: byte
|
||||||
|
type: string
|
||||||
jose.spec.JWK.RSA:
|
jose.spec.JWK.RSA:
|
||||||
description: RSA Public Key in JSON Web Key format as defined by RFC7517 and RFC7518.
|
description: RSA Public Key in JSON Web Key format as defined by RFC7517 and RFC7518.
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/jose.spec.JWK.base'
|
- $ref: '#/components/schemas/jose.spec.JWK.base'
|
||||||
- type: object
|
- required:
|
||||||
required:
|
|
||||||
- "kty"
|
- "kty"
|
||||||
- "n"
|
- "n"
|
||||||
- "e"
|
- "e"
|
||||||
|
type: object
|
||||||
properties:
|
properties:
|
||||||
kty:
|
kty:
|
||||||
description: >
|
description: >
|
||||||
|
|
|
@ -272,6 +272,23 @@ Allows [PKCE] `plain` challenges when set to `true`.
|
||||||
*__Security Notice:__* Changing this value is generally discouraged. Applications should use the `S256` [PKCE] challenge
|
*__Security Notice:__* Changing this value is generally discouraged. Applications should use the `S256` [PKCE] challenge
|
||||||
method instead.
|
method instead.
|
||||||
|
|
||||||
|
### pushed_authorizations
|
||||||
|
|
||||||
|
Controls the behaviour of [Pushed Authorization Requests].
|
||||||
|
|
||||||
|
#### enforce
|
||||||
|
|
||||||
|
{{< confkey type="boolean" default="false" required="no" >}}
|
||||||
|
|
||||||
|
When enabled all authorization requests must use the [Pushed Authorization Requests] flow.
|
||||||
|
|
||||||
|
#### context_lifespan
|
||||||
|
|
||||||
|
{{< confkey type="duration" default="5m" required="no" >}}
|
||||||
|
|
||||||
|
The maximum amount of time between the [Pushed Authorization Requests] flow being initiated and the generated
|
||||||
|
`request_uri` being utilized by a client.
|
||||||
|
|
||||||
### cors
|
### cors
|
||||||
|
|
||||||
Some [OpenID Connect 1.0] Endpoints need to allow cross-origin resource sharing, however some are optional. This section allows
|
Some [OpenID Connect 1.0] Endpoints need to allow cross-origin resource sharing, however some are optional. This section allows
|
||||||
|
@ -285,6 +302,7 @@ A list of endpoints to configure with cross-origin resource sharing headers. It
|
||||||
option is at least in this list. The potential endpoints which this can be enabled on are as follows:
|
option is at least in this list. The potential endpoints which this can be enabled on are as follows:
|
||||||
|
|
||||||
* authorization
|
* authorization
|
||||||
|
* pushed-authorization-request
|
||||||
* token
|
* token
|
||||||
* revocation
|
* revocation
|
||||||
* introspection
|
* introspection
|
||||||
|
@ -472,6 +490,12 @@ See the [Response Modes](../../integration/openid-connect/introduction.md#respon
|
||||||
|
|
||||||
The authorization policy for this client: either `one_factor` or `two_factor`.
|
The authorization policy for this client: either `one_factor` or `two_factor`.
|
||||||
|
|
||||||
|
#### enforce_par
|
||||||
|
|
||||||
|
{{< confkey type="boolean" default="false" required="no" >}}
|
||||||
|
|
||||||
|
Enforces the use of a [Pushed Authorization Requests] flow for this client.
|
||||||
|
|
||||||
#### enforce_pkce
|
#### enforce_pkce
|
||||||
|
|
||||||
{{< confkey type="bool" default="false" required="no" >}}
|
{{< confkey type="bool" default="false" required="no" >}}
|
||||||
|
@ -550,3 +574,4 @@ To integrate Authelia's [OpenID Connect 1.0] implementation with a relying party
|
||||||
[Authorization Code Flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
|
[Authorization Code Flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
|
||||||
[Subject Identifier Type]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
|
[Subject Identifier Type]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
|
||||||
[Pairwise Identifier Algorithm]: https://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg
|
[Pairwise Identifier Algorithm]: https://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg
|
||||||
|
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126
|
||||||
|
|
|
@ -36,3 +36,4 @@ this instance if you wanted to downgrade to pre1 you would need to use an Authel
|
||||||
| 5 | 4.35.1 | Fixed the oauth2_consent_session table to accept NULL subjects for users who are not yet signed in |
|
| 5 | 4.35.1 | Fixed the oauth2_consent_session table to accept NULL subjects for users who are not yet signed in |
|
||||||
| 6 | 4.37.0 | Adjusted the OpenID Connect tables to allow pre-configured consent improvements |
|
| 6 | 4.37.0 | Adjusted the OpenID Connect tables to allow pre-configured consent improvements |
|
||||||
| 7 | 4.37.3 | Fixed some schema inconsistencies most notably the MySQL/MariaDB Engine and Collation |
|
| 7 | 4.37.3 | Fixed some schema inconsistencies most notably the MySQL/MariaDB Engine and Collation |
|
||||||
|
| 8 | 4.38.0 | OpenID Connect 1.0 Pushed Authorization Requests |
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
---
|
||||||
|
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.
|
||||||
|
|
||||||
|
Recommendations on resolutions which are common:
|
||||||
|
|
||||||
|
- Desktop/Laptop:
|
||||||
|
1. 1920x1080
|
||||||
|
2. 1366x768
|
||||||
|
3. 2560x1440
|
||||||
|
4. 1280x720
|
||||||
|
- Tablet Devices (With Touch and Landscape):
|
||||||
|
1. 768x1024
|
||||||
|
2. 810x1080
|
||||||
|
3. 800x1280
|
||||||
|
- Mobile Devices (With Touch and Landscape):
|
||||||
|
1. 360x800
|
||||||
|
2. 390x844
|
||||||
|
3. 414x896
|
||||||
|
4. 412x915
|
|
@ -8,7 +8,7 @@ images: []
|
||||||
menu:
|
menu:
|
||||||
contributing:
|
contributing:
|
||||||
parent: "guidelines"
|
parent: "guidelines"
|
||||||
weight: 320
|
weight: 350
|
||||||
toc: true
|
toc: true
|
||||||
aliases:
|
aliases:
|
||||||
- /docs/contributing/style-guide.html
|
- /docs/contributing/style-guide.html
|
||||||
|
|
|
@ -209,14 +209,71 @@ These endpoints can be utilized to discover other endpoints and metadata about t
|
||||||
|
|
||||||
These endpoints implement OpenID Connect elements.
|
These endpoints implement OpenID Connect elements.
|
||||||
|
|
||||||
| Endpoint | Path | Discovery Attribute |
|
| Endpoint | Path | Discovery Attribute |
|
||||||
|:-------------------:|:-----------------------------------------------:|:----------------------:|
|
|:-------------------------------:|:--------------------------------------------------------------:|:-------------------------------------:|
|
||||||
| [JSON Web Key Sets] | https://auth.example.com/jwks.json | jwks_uri |
|
| [JSON Web Key Set] | https://auth.example.com/jwks.json | jwks_uri |
|
||||||
| [Authorization] | https://auth.example.com/api/oidc/authorization | authorization_endpoint |
|
| [Authorization] | https://auth.example.com/api/oidc/authorization | authorization_endpoint |
|
||||||
| [Token] | https://auth.example.com/api/oidc/token | token_endpoint |
|
| [Pushed Authorization Requests] | https://auth.example.com/api/oidc/pushed-authorization-request | pushed_authorization_request_endpoint |
|
||||||
| [UserInfo] | https://auth.example.com/api/oidc/userinfo | userinfo_endpoint |
|
| [Token] | https://auth.example.com/api/oidc/token | token_endpoint |
|
||||||
| [Introspection] | https://auth.example.com/api/oidc/introspection | introspection_endpoint |
|
| [UserInfo] | https://auth.example.com/api/oidc/userinfo | userinfo_endpoint |
|
||||||
| [Revocation] | https://auth.example.com/api/oidc/revocation | revocation_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
|
[ID Token]: https://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||||
[Access Token]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.4
|
[Access Token]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.4
|
||||||
|
@ -230,14 +287,23 @@ These endpoints implement OpenID Connect elements.
|
||||||
[OpenID Connect Discovery]: https://openid.net/specs/openid-connect-discovery-1_0.html
|
[OpenID Connect Discovery]: https://openid.net/specs/openid-connect-discovery-1_0.html
|
||||||
[OAuth 2.0 Authorization Server Metadata]: https://datatracker.ietf.org/doc/html/rfc8414
|
[OAuth 2.0 Authorization Server Metadata]: https://datatracker.ietf.org/doc/html/rfc8414
|
||||||
|
|
||||||
[JSON Web Key Sets]: https://datatracker.ietf.org/doc/html/rfc7517#section-5
|
[JSON Web Key Set]: https://datatracker.ietf.org/doc/html/rfc7517#section-5
|
||||||
|
|
||||||
[Authorization]: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
|
[Authorization]: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
|
||||||
|
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126
|
||||||
[Token]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
[Token]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
||||||
[UserInfo]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
|
[UserInfo]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
|
||||||
[Introspection]: https://datatracker.ietf.org/doc/html/rfc7662
|
[Introspection]: https://datatracker.ietf.org/doc/html/rfc7662
|
||||||
[Revocation]: https://datatracker.ietf.org/doc/html/rfc7009
|
[Revocation]: https://datatracker.ietf.org/doc/html/rfc7009
|
||||||
|
[Proof Key Code Exchange]: https://www.rfc-editor.org/rfc/rfc7636.html
|
||||||
|
|
||||||
[RFC8176]: https://datatracker.ietf.org/doc/html/rfc8176
|
|
||||||
[RFC4122]: https://datatracker.ietf.org/doc/html/rfc4122
|
|
||||||
[Subject Identifier Types]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
|
[Subject Identifier Types]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
|
||||||
|
[Client Authentication]: https://datatracker.ietf.org/doc/html/rfc6749#section-2.3
|
||||||
|
[Client Type]: https://oauth.net/2/client-types/
|
||||||
|
[HTTP POST method]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST
|
||||||
|
[Proof Key Code Exchange]: #proof-key-code-exchange
|
||||||
|
|
||||||
|
[RFC4122]: https://datatracker.ietf.org/doc/html/rfc4122
|
||||||
|
[RFC7636]: https://datatracker.ietf.org/doc/html/rfc7636
|
||||||
|
[RFC8176]: https://datatracker.ietf.org/doc/html/rfc8176
|
||||||
|
[RFC9126]: https://datatracker.ietf.org/doc/html/rfc9126
|
||||||
|
|
|
@ -53,3 +53,20 @@ deprecated__* and we instead recommended that users remove this from their confi
|
||||||
Plaintext is either denoted by the `$plaintext$` prefix where everything after the prefix is the secret. In addition if
|
Plaintext is either denoted by the `$plaintext$` prefix where everything after the prefix is the secret. In addition if
|
||||||
the secret does not start with the `$` character it's considered as a plaintext secret for the time being but is
|
the secret does not start with the `$` character it's considered as a plaintext secret for the time being but is
|
||||||
deprecated as is the `$plaintext$` prefix.
|
deprecated as is the `$plaintext$` prefix.
|
||||||
|
|
||||||
|
## Frequently Asked Questions
|
||||||
|
|
||||||
|
### Why isn't my application able to retrieve the token even though I've consented?
|
||||||
|
|
||||||
|
The most common cause for this issue is when the affected application can not make requests to the Token [Endpoint].
|
||||||
|
This becomes obvious when the log level is set to `debug` or `trace` and a presence of requests to the Authorization
|
||||||
|
[Endpoint] without errors but an absence of requests made to the Token [Endpoint].
|
||||||
|
|
||||||
|
These requests can be identified by looking at the `path` field in the logs, or by messages prefixed with
|
||||||
|
`Authorization Request` indicating a request to the Authorization [Endpoint] and `Access Request` indicating a request
|
||||||
|
to the Token [Endpoint].
|
||||||
|
|
||||||
|
All causes should be clearly logged by the client application, and all errors that do not match this scenario are
|
||||||
|
clearly logged by Authelia. It's not possible for us to log requests that never occur however.
|
||||||
|
|
||||||
|
[Endpoint]: ./introduction.md#discoverable-endpoints
|
||||||
|
|
|
@ -383,7 +383,7 @@ proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
set $upstream_authelia http://authelia:9091/api/authz/auth-request;
|
set $upstream_authelia http://authelia:9091/api/authz/auth-request;
|
||||||
|
|
||||||
## Virtual endpoint created by nginx to forward auth requests.
|
## Virtual endpoint created by nginx to forward auth requests.
|
||||||
location /authelia {
|
location /internal/authelia/authz {
|
||||||
## Essential Proxy Configuration
|
## Essential Proxy Configuration
|
||||||
internal;
|
internal;
|
||||||
proxy_pass $upstream_authelia;
|
proxy_pass $upstream_authelia;
|
||||||
|
@ -423,7 +423,7 @@ and is paired with [authelia-location.conf](#authelia-locationconf).*
|
||||||
{{< details "/config/nginx/snippets/authelia-authrequest.conf" >}}
|
{{< details "/config/nginx/snippets/authelia-authrequest.conf" >}}
|
||||||
```nginx
|
```nginx
|
||||||
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
||||||
auth_request /authelia;
|
auth_request /internal/authelia/authz;
|
||||||
|
|
||||||
## Set the $target_url variable based on the original request.
|
## Set the $target_url variable based on the original request.
|
||||||
|
|
||||||
|
@ -478,7 +478,7 @@ implementation `AuthRequest` which contains the `HeaderAuthorization` and `Heade
|
||||||
set $upstream_authelia http://authelia:9091/api/authz/auth-request/basic;
|
set $upstream_authelia http://authelia:9091/api/authz/auth-request/basic;
|
||||||
|
|
||||||
# Virtual endpoint created by nginx to forward auth requests.
|
# Virtual endpoint created by nginx to forward auth requests.
|
||||||
location /authelia-basic {
|
location /internal/authelia/authz/basic {
|
||||||
## Essential Proxy Configuration
|
## Essential Proxy Configuration
|
||||||
internal;
|
internal;
|
||||||
proxy_pass $upstream_authelia;
|
proxy_pass $upstream_authelia;
|
||||||
|
@ -526,7 +526,7 @@ endpoint. It's recommended to use [authelia-authrequest.conf](#authelia-authrequ
|
||||||
{{< details "/config/nginx/snippets/authelia-authrequest-basic.conf" >}}
|
{{< details "/config/nginx/snippets/authelia-authrequest-basic.conf" >}}
|
||||||
```nginx
|
```nginx
|
||||||
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
||||||
auth_request /authelia-basic;
|
auth_request /internal/authelia/authz/basic;
|
||||||
|
|
||||||
## Comment this line if you're using nginx without the http_set_misc module.
|
## Comment this line if you're using nginx without the http_set_misc module.
|
||||||
set_escape_uri $target_url $scheme://$http_host$request_uri;
|
set_escape_uri $target_url $scheme://$http_host$request_uri;
|
||||||
|
@ -570,7 +570,7 @@ if ($request_uri = "/force-basic") {
|
||||||
}
|
}
|
||||||
|
|
||||||
## A new virtual endpoint to used if the auth_request failed
|
## A new virtual endpoint to used if the auth_request failed
|
||||||
location /authelia-detect {
|
location /internal/authelia/authz/detect {
|
||||||
internal;
|
internal;
|
||||||
|
|
||||||
if ($is_basic_auth) {
|
if ($is_basic_auth) {
|
||||||
|
@ -598,7 +598,7 @@ endpoint. It's recommended to use [authelia-authrequest.conf](#authelia-authrequ
|
||||||
{{< details "/config/nginx/snippets/authelia-authrequest-detect.conf" >}}
|
{{< details "/config/nginx/snippets/authelia-authrequest-detect.conf" >}}
|
||||||
```nginx
|
```nginx
|
||||||
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
||||||
auth_request /authelia;
|
auth_request /internal/authelia/authz;
|
||||||
|
|
||||||
## Comment this line if you're using nginx without the http_set_misc module.
|
## Comment this line if you're using nginx without the http_set_misc module.
|
||||||
set_escape_uri $target_url $scheme://$http_host$request_uri;
|
set_escape_uri $target_url $scheme://$http_host$request_uri;
|
||||||
|
@ -619,7 +619,7 @@ proxy_set_header Remote-Name $name;
|
||||||
proxy_set_header Remote-Email $email;
|
proxy_set_header Remote-Email $email;
|
||||||
|
|
||||||
## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal.
|
## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal.
|
||||||
error_page 401 =302 /authelia-detect?rd=$target_url;
|
error_page 401 =302 /internal/authelia/authz/detect?rd=$target_url;
|
||||||
```
|
```
|
||||||
{{< /details >}}
|
{{< /details >}}
|
||||||
|
|
||||||
|
|
|
@ -15,19 +15,19 @@ aliases:
|
||||||
- /docs/home/supported-proxies.html
|
- /docs/home/supported-proxies.html
|
||||||
---
|
---
|
||||||
|
|
||||||
| Proxy | [Implementation] | [Standard](#standard) | [Kubernetes](#kubernetes) | [XHR Redirect](#xhr-redirect) | [Request Method](#request-method) |
|
| Proxy | [Implementation] | [Standard](#standard) | [Kubernetes](#kubernetes) | [XHR Redirect](#xhr-redirect) | [Request Method](#request-method) |
|
||||||
|:---------------------:|:----------------:|:------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:|:---------------------------------:|:---------------------------------:|
|
|:---------------------------------------:|:----------------:|:---------------------------------------------------:|:-------------------------------------------------------------------------------------:|:---------------------------------:|:---------------------------------:|
|
||||||
| [Traefik] | [ForwardAuth] | {{% support support="full" link="traefik.md" %}} | {{% support support="full" link="../../integration/kubernetes/traefik-ingress.md" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
| [Traefik] ([guide](/i/traefik)) | [ForwardAuth] | {{% support support="full" link="/i/traefik" %}} | {{% support support="full" link="../../integration/kubernetes/traefik-ingress.md" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
||||||
| [Caddy] | [ForwardAuth] | {{% support support="full" link="caddy.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
| [Caddy] ([guide](/i/caddy)) | [ForwardAuth] | {{% support support="full" link="/i/caddy" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
||||||
| [Envoy] | [ExtAuthz] | {{% support support="full" link="envoy.md" %}} | {{% support support="full" link="../../integration/kubernetes/istio.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
|
| [Envoy] ([guide](/i/envoy)) | [ExtAuthz] | {{% support support="full" link="/i/envoy" %}} | {{% support support="full" link="../../integration/kubernetes/istio.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
|
||||||
| [NGINX] | [AuthRequest] | {{% support support="full" link="nginx.md" %}} | {{% support support="full" link="../../integration/kubernetes/nginx-ingress.md" %}} | {{% support %}} | {{% support support="full" %}} |
|
| [NGINX] ([guide](/i/nginx)) | [AuthRequest] | {{% support support="full" link="/i/nginx" %}} | {{% support support="full" link="../../integration/kubernetes/nginx-ingress.md" %}} | {{% support %}} | {{% support support="full" %}} |
|
||||||
| [NGINX Proxy Manager] | [AuthRequest] | {{% support support="full" link="nginx-proxy-manager/index.md" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
|
| [NGINX Proxy Manager] ([guide](/i/npm)) | [AuthRequest] | {{% support support="full" link="/i/npm" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
|
||||||
| [SWAG] | [AuthRequest] | {{% support support="full" link="swag.md" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
|
| [SWAG] ([guide](/i/swag)) | [AuthRequest] | {{% support support="full" link="/i/swag" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
|
||||||
| [HAProxy] | [AuthRequest] | {{% support support="full" link="haproxy.md" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
|
| [HAProxy] ([guide](/i/haproxy)) | [AuthRequest] | {{% support support="full" link="/i/haproxy" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
|
||||||
| [Skipper] | [ForwardAuth] | {{% support support="full" link="skipper.md" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} |
|
| [Skipper] ([guide](/i/skipper)) | [ForwardAuth] | {{% support support="full" link="/i/skipper" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} |
|
||||||
| [Traefik] 1.x | [ForwardAuth] | {{% support support="full" link="traefikv1.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
| [Traefik] 1.x ([guide](/i/traefik/v1)) | [ForwardAuth] | {{% support support="full" link="/i/traefik/v1" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
||||||
| [Apache] | N/A | {{% support link="#apache" %}} | {{% support %}} | {{% support %}} | {{% support %}} |
|
| [Apache] | N/A | {{% support link="#apache" %}} | {{% support %}} | {{% support %}} | {{% support %}} |
|
||||||
| [IIS] | N/A | {{% support link="#iis" %}} | {{% support %}} | {{% support %}} | {{% support %}} |
|
| [IIS] | N/A | {{% support link="#iis" %}} | {{% support %}} | {{% support %}} | {{% support %}} |
|
||||||
|
|
||||||
[ForwardAuth]: ../../reference/guides/proxy-authorization.md#forwardauth
|
[ForwardAuth]: ../../reference/guides/proxy-authorization.md#forwardauth
|
||||||
[AuthRequest]: ../../reference/guides/proxy-authorization.md#authrequest
|
[AuthRequest]: ../../reference/guides/proxy-authorization.md#authrequest
|
||||||
|
|
|
@ -57,7 +57,7 @@ In addition this represents a bad user experience in some instances such as:
|
||||||
|
|
||||||
- Users sometimes visit the `https://app.example.com/authelia` URL which doesn't automatically redirect the user to
|
- Users sometimes visit the `https://app.example.com/authelia` URL which doesn't automatically redirect the user to
|
||||||
`https://app.example.com` (if they visit `https://app.example.com` then they'll be redirected to authenticate then
|
`https://app.example.com` (if they visit `https://app.example.com` then they'll be redirected to authenticate then
|
||||||
redirected back to their original URL).
|
redirected back to their original URL)
|
||||||
- Administrators may wish to setup [OpenID Connect 1.0](../../configuration/identity-providers/open-id-connect.md) in
|
- Administrators may wish to setup [OpenID Connect 1.0](../../configuration/identity-providers/open-id-connect.md) in
|
||||||
which case it also doesn't represent a good user experience as the `issuer` will be
|
which case it also doesn't represent a good user experience as the `issuer` will be
|
||||||
`https://app.example.com/authelia` for example
|
`https://app.example.com/authelia` for example
|
||||||
|
@ -147,8 +147,8 @@ services:
|
||||||
- '443:443'
|
- '443:443'
|
||||||
volumes:
|
volumes:
|
||||||
- ${PWD}/data/swag:/config
|
- ${PWD}/data/swag:/config
|
||||||
|
## Uncomment the line below if you want to use the Authelia configuration snippets.
|
||||||
#- ${PWD}/data/nginx/snippets:/snippets:ro
|
#- ${PWD}/data/nginx/snippets:/snippets:ro
|
||||||
## Uncomment the above line if you want to use the Authelia configuration snippets.
|
|
||||||
environment:
|
environment:
|
||||||
PUID: '1000'
|
PUID: '1000'
|
||||||
PGID: '1000'
|
PGID: '1000'
|
||||||
|
|
|
@ -11,6 +11,7 @@ menu:
|
||||||
weight: 371
|
weight: 371
|
||||||
toc: true
|
toc: true
|
||||||
aliases:
|
aliases:
|
||||||
|
- /i/traefik/v1
|
||||||
- /docs/deployment/supported-proxies/traefik1.x.html
|
- /docs/deployment/supported-proxies/traefik1.x.html
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -14,19 +14,19 @@ toc: false
|
||||||
|
|
||||||
The following table is a support matrix for Authelia features and specific reverse proxies.
|
The following table is a support matrix for Authelia features and specific reverse proxies.
|
||||||
|
|
||||||
| Proxy | Standard | Kubernetes | XHR Redirect | Request Method |
|
| Proxy | Standard | Kubernetes | XHR Redirect | Request Method |
|
||||||
|:---------------------:|:--------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:|:---------------------------------:|:---------------------------------:|
|
|:---------------------------------------:|:-------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:|:---------------------------------:|:---------------------------------:|
|
||||||
| [Traefik] | {{% support support="full" link="../../integration/proxies/traefik.md" %}} | {{% support support="full" link="../../integration/kubernetes/traefik-ingress.md" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
| [Traefik] ([guide](/i/traefik)) | {{% support support="full" link="/i/traefik" %}} | {{% support support="full" link="../../integration/kubernetes/traefik-ingress.md" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
||||||
| [Caddy] | {{% support support="full" link="../../integration/proxies/caddy.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
| [Caddy] ([guide](/i/caddy)) | {{% support support="full" link="/i/caddy" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
||||||
| [Envoy] | {{% support support="full" link="../../integration/proxies/envoy.md" %}} | {{% support support="full" link="../../integration/kubernetes/istio.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
|
| [Envoy] ([guide](/i/envoy)) | {{% support support="full" link="/i/envoy" %}} | {{% support support="full" link="../../integration/kubernetes/istio.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
|
||||||
| [NGINX] | {{% support support="full" link="../../integration/proxies/nginx.md" %}} | {{% support support="full" link="../../integration/kubernetes/nginx-ingress.md" %}} | {{% support %}} | {{% support support="full" %}} |
|
| [NGINX] ([guide](/i/nginx)) | {{% support support="full" link="/i/nginx" %}} | {{% support support="full" link="../../integration/kubernetes/nginx-ingress.md" %}} | {{% support %}} | {{% support support="full" %}} |
|
||||||
| [NGINX Proxy Manager] | {{% support support="full" link="../../integration/proxies/nginx-proxy-manager/index.md" %}} | {{% support %}} | {{% support %}} | {{% support support="full" %}} |
|
| [NGINX Proxy Manager] ([guide](/i/npm)) | {{% support support="full" link="/i/npm" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
|
||||||
| [SWAG] | {{% support support="full" link="../../integration/proxies/swag.md" %}} | {{% support %}} | {{% support %}} | {{% support support="full" %}} |
|
| [SWAG] ([guide](/i/swag)) | {{% support support="full" link="/i/swag" %}} | {{% support support="unknown" %}} | {{% support %}} | {{% support support="full" %}} |
|
||||||
| [HAProxy] | {{% support support="full" link="../../integration/proxies/haproxy.md" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
|
| [HAProxy] ([guide](/i/haproxy)) | {{% support support="full" link="/i/haproxy" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} |
|
||||||
| [Traefik] 1.x | {{% support support="full" link="../../integration/proxies/traefikv1.md" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
| [Skipper] ([guide](/i/skipper)) | {{% support support="full" link="/i/skipper" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} |
|
||||||
| [Skipper] | {{% support support="full" link="../../integration/proxies/skipper.md" %}} | {{% support %}} | {{% support support="unknown" %}} | {{% support support="unknown" %}} |
|
| [Traefik] 1.x ([guide](/i/traefik/v1)) | {{% support support="full" link="/i/traefik/v1" %}} | {{% support support="unknown" %}} | {{% support support="full" %}} | {{% support support="full" %}} |
|
||||||
| [Apache] | {{% support %}} | {{% support %}} | {{% support %}} | {{% support %}} |
|
| [Apache] | {{% support %}} | {{% support %}} | {{% support %}} | {{% support %}} |
|
||||||
| [IIS] | {{% support %}} | {{% support %}} | {{% support %}} | {{% support %}} |
|
| [IIS] | {{% support %}} | {{% support %}} | {{% support %}} | {{% support %}} |
|
||||||
|
|
||||||
Legend:
|
Legend:
|
||||||
|
|
||||||
|
|
|
@ -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
|
@ -38,8 +38,8 @@
|
||||||
"version": "auto-changelog -p && git add CHANGELOG.md"
|
"version": "auto-changelog -p && git add CHANGELOG.md"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.20.7",
|
"@babel/cli": "7.21.0",
|
||||||
"@babel/core": "7.20.12",
|
"@babel/core": "7.21.0",
|
||||||
"@babel/preset-env": "7.20.2",
|
"@babel/preset-env": "7.20.2",
|
||||||
"@fullhuman/postcss-purgecss": "5.0.0",
|
"@fullhuman/postcss-purgecss": "5.0.0",
|
||||||
"@hyas/images": "0.3.2",
|
"@hyas/images": "0.3.2",
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
"bootstrap": "5.2.3",
|
"bootstrap": "5.2.3",
|
||||||
"bootstrap-icons": "1.10.3",
|
"bootstrap-icons": "1.10.3",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
"eslint": "8.32.0",
|
"eslint": "8.35.0",
|
||||||
"exec-bin": "1.0.0",
|
"exec-bin": "1.0.0",
|
||||||
"flexsearch": "0.7.31",
|
"flexsearch": "0.7.31",
|
||||||
"highlight.js": "11.7.0",
|
"highlight.js": "11.7.0",
|
||||||
|
@ -68,6 +68,6 @@
|
||||||
"stylelint-config-standard-scss": "6.1.0"
|
"stylelint-config-standard-scss": "6.1.0"
|
||||||
},
|
},
|
||||||
"otherDependencies": {
|
"otherDependencies": {
|
||||||
"hugo": "0.110.0"
|
"hugo": "0.111.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
16
go.mod
16
go.mod
|
@ -5,7 +5,7 @@ go 1.20
|
||||||
require (
|
require (
|
||||||
github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4
|
github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4
|
||||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||||
github.com/deckarep/golang-set/v2 v2.1.0
|
github.com/deckarep/golang-set/v2 v2.2.0
|
||||||
github.com/duosecurity/duo_api_golang v0.0.0-20230203160531-b221c950c2b0
|
github.com/duosecurity/duo_api_golang v0.0.0-20230203160531-b221c950c2b0
|
||||||
github.com/fasthttp/router v1.4.17
|
github.com/fasthttp/router v1.4.17
|
||||||
github.com/fasthttp/session/v2 v2.4.16
|
github.com/fasthttp/session/v2 v2.4.16
|
||||||
|
@ -33,7 +33,7 @@ require (
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||||
github.com/ory/fosite v0.44.0
|
github.com/ory/fosite v0.44.0
|
||||||
github.com/ory/herodot v0.9.13
|
github.com/ory/herodot v0.9.13
|
||||||
github.com/ory/x v0.0.542
|
github.com/ory/x v0.0.543
|
||||||
github.com/otiai10/copy v1.9.0
|
github.com/otiai10/copy v1.9.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pquerna/otp v1.4.0
|
github.com/pquerna/otp v1.4.0
|
||||||
|
@ -45,10 +45,10 @@ require (
|
||||||
github.com/trustelem/zxcvbn v1.0.1
|
github.com/trustelem/zxcvbn v1.0.1
|
||||||
github.com/valyala/fasthttp v1.44.0
|
github.com/valyala/fasthttp v1.44.0
|
||||||
github.com/wneessen/go-mail v0.3.8
|
github.com/wneessen/go-mail v0.3.8
|
||||||
golang.org/x/net v0.7.0
|
golang.org/x/net v0.8.0
|
||||||
golang.org/x/sync v0.1.0
|
golang.org/x/sync v0.1.0
|
||||||
golang.org/x/term v0.5.0
|
golang.org/x/term v0.6.0
|
||||||
golang.org/x/text v0.7.0
|
golang.org/x/text v0.8.0
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
@ -115,10 +115,10 @@ require (
|
||||||
github.com/ysmood/gson v0.7.3 // indirect
|
github.com/ysmood/gson v0.7.3 // indirect
|
||||||
github.com/ysmood/leakless v0.8.0 // indirect
|
github.com/ysmood/leakless v0.8.0 // indirect
|
||||||
golang.org/x/crypto v0.6.0 // indirect
|
golang.org/x/crypto v0.6.0 // indirect
|
||||||
golang.org/x/mod v0.7.0 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
|
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
golang.org/x/sys v0.6.0 // indirect
|
||||||
golang.org/x/tools v0.4.0 // indirect
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect
|
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect
|
||||||
google.golang.org/grpc v1.50.1 // indirect
|
google.golang.org/grpc v1.50.1 // indirect
|
||||||
|
|
30
go.sum
30
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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
|
github.com/deckarep/golang-set/v2 v2.2.0 h1:2pMQd3Soi6qfw7E5MMKaEh5W5ES18bW3AbFFnGl6LgQ=
|
||||||
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
github.com/deckarep/golang-set/v2 v2.2.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||||
github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE=
|
github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE=
|
||||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||||
|
@ -387,8 +387,8 @@ github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8
|
||||||
github.com/ory/herodot v0.9.13 h1:cN/Z4eOkErl/9W7hDIDLb79IO/bfsH+8yscBjRpB4IU=
|
github.com/ory/herodot v0.9.13 h1:cN/Z4eOkErl/9W7hDIDLb79IO/bfsH+8yscBjRpB4IU=
|
||||||
github.com/ory/herodot v0.9.13/go.mod h1:IWDs9kSvFQqw/cQ8zi5ksyYvITiUU4dI7glUrhZcJYo=
|
github.com/ory/herodot v0.9.13/go.mod h1:IWDs9kSvFQqw/cQ8zi5ksyYvITiUU4dI7glUrhZcJYo=
|
||||||
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
|
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
|
||||||
github.com/ory/x v0.0.542 h1:3moNM1xRT3GYUSoNet4ZF8bFWE5jY/G526mR9lQVmA0=
|
github.com/ory/x v0.0.543 h1:I6bl6IV2Ok07io6M2dnaRaJHP5oRU096T9FYoe8m48U=
|
||||||
github.com/ory/x v0.0.542/go.mod h1:ktXUvx51Ok1gMGr3ysvktanqr+eiB4FXglt4nF4w2Uo=
|
github.com/ory/x v0.0.543/go.mod h1:ktXUvx51Ok1gMGr3ysvktanqr+eiB4FXglt4nF4w2Uo=
|
||||||
github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4=
|
github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4=
|
||||||
github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI=
|
github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI=
|
||||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||||
|
@ -608,8 +608,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
|
||||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -657,8 +658,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -751,13 +752,13 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -768,8 +769,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -827,8 +828,9 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
|
|
||||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||||
|
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|
|
@ -29,10 +29,17 @@ type OpenIDConnectConfiguration struct {
|
||||||
EnablePKCEPlainChallenge bool `koanf:"enable_pkce_plain_challenge"`
|
EnablePKCEPlainChallenge bool `koanf:"enable_pkce_plain_challenge"`
|
||||||
|
|
||||||
CORS OpenIDConnectCORSConfiguration `koanf:"cors"`
|
CORS OpenIDConnectCORSConfiguration `koanf:"cors"`
|
||||||
|
PAR OpenIDConnectPARConfiguration `koanf:"pushed_authorizations"`
|
||||||
|
|
||||||
Clients []OpenIDConnectClientConfiguration `koanf:"clients"`
|
Clients []OpenIDConnectClientConfiguration `koanf:"clients"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenIDConnectPARConfiguration represents an OpenID Connect PAR config.
|
||||||
|
type OpenIDConnectPARConfiguration struct {
|
||||||
|
Enforce bool `koanf:"enforce"`
|
||||||
|
ContextLifespan time.Duration `koanf:"context_lifespan"`
|
||||||
|
}
|
||||||
|
|
||||||
// OpenIDConnectCORSConfiguration represents an OpenID Connect CORS config.
|
// OpenIDConnectCORSConfiguration represents an OpenID Connect CORS config.
|
||||||
type OpenIDConnectCORSConfiguration struct {
|
type OpenIDConnectCORSConfiguration struct {
|
||||||
Endpoints []string `koanf:"endpoints"`
|
Endpoints []string `koanf:"endpoints"`
|
||||||
|
@ -59,6 +66,7 @@ type OpenIDConnectClientConfiguration struct {
|
||||||
|
|
||||||
Policy string `koanf:"authorization_policy"`
|
Policy string `koanf:"authorization_policy"`
|
||||||
|
|
||||||
|
EnforcePAR bool `koanf:"enforce_par"`
|
||||||
EnforcePKCE bool `koanf:"enforce_pkce"`
|
EnforcePKCE bool `koanf:"enforce_pkce"`
|
||||||
|
|
||||||
PKCEChallengeMethod string `koanf:"pkce_challenge_method"`
|
PKCEChallengeMethod string `koanf:"pkce_challenge_method"`
|
||||||
|
|
|
@ -30,6 +30,8 @@ var Keys = []string{
|
||||||
"identity_providers.oidc.cors.endpoints",
|
"identity_providers.oidc.cors.endpoints",
|
||||||
"identity_providers.oidc.cors.allowed_origins",
|
"identity_providers.oidc.cors.allowed_origins",
|
||||||
"identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris",
|
"identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris",
|
||||||
|
"identity_providers.oidc.pushed_authorizations.enforce",
|
||||||
|
"identity_providers.oidc.pushed_authorizations.context_lifespan",
|
||||||
"identity_providers.oidc.clients",
|
"identity_providers.oidc.clients",
|
||||||
"identity_providers.oidc.clients[].id",
|
"identity_providers.oidc.clients[].id",
|
||||||
"identity_providers.oidc.clients[].description",
|
"identity_providers.oidc.clients[].description",
|
||||||
|
@ -43,6 +45,7 @@ var Keys = []string{
|
||||||
"identity_providers.oidc.clients[].response_types",
|
"identity_providers.oidc.clients[].response_types",
|
||||||
"identity_providers.oidc.clients[].response_modes",
|
"identity_providers.oidc.clients[].response_modes",
|
||||||
"identity_providers.oidc.clients[].authorization_policy",
|
"identity_providers.oidc.clients[].authorization_policy",
|
||||||
|
"identity_providers.oidc.clients[].enforce_par",
|
||||||
"identity_providers.oidc.clients[].enforce_pkce",
|
"identity_providers.oidc.clients[].enforce_pkce",
|
||||||
"identity_providers.oidc.clients[].pkce_challenge_method",
|
"identity_providers.oidc.clients[].pkce_challenge_method",
|
||||||
"identity_providers.oidc.clients[].userinfo_signing_algorithm",
|
"identity_providers.oidc.clients[].userinfo_signing_algorithm",
|
||||||
|
|
|
@ -140,6 +140,20 @@ type PasswordDigest struct {
|
||||||
algorithm.Digest
|
algorithm.Digest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPlainText returns true if the underlying algorithm.Digest is a *plaintext.Digest.
|
||||||
|
func (d *PasswordDigest) IsPlainText() bool {
|
||||||
|
if d == nil || d.Digest == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch d.Digest.(type) {
|
||||||
|
case *plaintext.Digest:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewX509CertificateChain creates a new *X509CertificateChain from a given string, parsing each PEM block one by one.
|
// NewX509CertificateChain creates a new *X509CertificateChain from a given string, parsing each PEM block one by one.
|
||||||
func NewX509CertificateChain(in string) (chain *X509CertificateChain, err error) {
|
func NewX509CertificateChain(in string) (chain *X509CertificateChain, err error) {
|
||||||
if in == "" {
|
if in == "" {
|
||||||
|
|
|
@ -168,7 +168,7 @@ func TestShouldNotRaiseErrorOnValidCertificatesDirectory(t *testing.T) {
|
||||||
require.Len(t, validator.Warnings(), 1)
|
require.Len(t, validator.Warnings(), 1)
|
||||||
|
|
||||||
assert.EqualError(t, validator.Warnings()[0], "access control: no rules have been specified so the 'default_policy' of 'two_factor' is going to be applied to all requests")
|
assert.EqualError(t, validator.Warnings()[0], "access control: no rules have been specified so the 'default_policy' of 'two_factor' is going to be applied to all requests")
|
||||||
}
|
}.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func TestValidateDefault2FAMethod(t *testing.T) {
|
func TestValidateDefault2FAMethod(t *testing.T) {
|
||||||
|
|
|
@ -159,8 +159,9 @@ const (
|
||||||
errFmtOIDCClientsWithEmptyID = "identity_providers: oidc: one or more clients have been configured with " +
|
errFmtOIDCClientsWithEmptyID = "identity_providers: oidc: one or more clients have been configured with " +
|
||||||
"an empty id"
|
"an empty id"
|
||||||
|
|
||||||
errFmtOIDCClientInvalidSecret = "identity_providers: oidc: client '%s': option 'secret' is required"
|
errFmtOIDCClientInvalidSecret = "identity_providers: oidc: client '%s': option 'secret' is required"
|
||||||
errFmtOIDCClientPublicInvalidSecret = "identity_providers: oidc: client '%s': option 'secret' is " +
|
errFmtOIDCClientInvalidSecretPlainText = "identity_providers: oidc: client '%s': option 'secret' is plaintext but it should be a hashed value as plaintext values are deprecated and will be removed when oidc becomes stable"
|
||||||
|
errFmtOIDCClientPublicInvalidSecret = "identity_providers: oidc: client '%s': option 'secret' is " +
|
||||||
"required to be empty when option 'public' is true"
|
"required to be empty when option 'public' is true"
|
||||||
errFmtOIDCClientRedirectURICantBeParsed = "identity_providers: oidc: client '%s': option 'redirect_uris' has an " +
|
errFmtOIDCClientRedirectURICantBeParsed = "identity_providers: oidc: client '%s': option 'redirect_uris' has an " +
|
||||||
"invalid value: redirect uri '%s' could not be parsed: %v"
|
"invalid value: redirect uri '%s' could not be parsed: %v"
|
||||||
|
@ -392,7 +393,7 @@ var (
|
||||||
validOIDCGrantTypes = []string{oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeAuthorizationCode, oidc.GrantTypePassword, oidc.GrantTypeClientCredentials}
|
validOIDCGrantTypes = []string{oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeAuthorizationCode, oidc.GrantTypePassword, oidc.GrantTypeClientCredentials}
|
||||||
validOIDCResponseModes = []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery, oidc.ResponseModeFragment}
|
validOIDCResponseModes = []string{oidc.ResponseModeFormPost, oidc.ResponseModeQuery, oidc.ResponseModeFragment}
|
||||||
validOIDCUserinfoAlgorithms = []string{oidc.SigningAlgorithmNone, oidc.SigningAlgorithmRSAWithSHA256}
|
validOIDCUserinfoAlgorithms = []string{oidc.SigningAlgorithmNone, oidc.SigningAlgorithmRSAWithSHA256}
|
||||||
validOIDCCORSEndpoints = []string{oidc.EndpointAuthorization, oidc.EndpointToken, oidc.EndpointIntrospection, oidc.EndpointRevocation, oidc.EndpointUserinfo}
|
validOIDCCORSEndpoints = []string{oidc.EndpointAuthorization, oidc.EndpointPushedAuthorizationRequest, oidc.EndpointToken, oidc.EndpointIntrospection, oidc.EndpointRevocation, oidc.EndpointUserinfo}
|
||||||
validOIDCClientConsentModes = []string{"auto", oidc.ClientConsentModeImplicit.String(), oidc.ClientConsentModeExplicit.String(), oidc.ClientConsentModePreConfigured.String()}
|
validOIDCClientConsentModes = []string{"auto", oidc.ClientConsentModeImplicit.String(), oidc.ClientConsentModeExplicit.String(), oidc.ClientConsentModePreConfigured.String()}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,8 @@ func validateOIDCClients(config *schema.OpenIDConnectConfiguration, val *schema.
|
||||||
} else {
|
} else {
|
||||||
if client.Secret == nil {
|
if client.Secret == nil {
|
||||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSecret, client.ID))
|
val.Push(fmt.Errorf(errFmtOIDCClientInvalidSecret, client.ID))
|
||||||
|
} else if client.Secret.IsPlainText() {
|
||||||
|
val.PushWarning(fmt.Errorf(errFmtOIDCClientInvalidSecretPlainText, client.ID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ func TestShouldRaiseErrorWhenCORSEndpointsNotValid(t *testing.T) {
|
||||||
|
|
||||||
require.Len(t, validator.Errors(), 1)
|
require.Len(t, validator.Errors(), 1)
|
||||||
|
|
||||||
assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: cors: option 'endpoints' contains an invalid value 'invalid_endpoint': must be one of 'authorization', 'token', 'introspection', 'revocation', 'userinfo'")
|
assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: cors: option 'endpoints' contains an invalid value 'invalid_endpoint': must be one of 'authorization', 'pushed-authorization-request', 'token', 'introspection', 'revocation', 'userinfo'")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldRaiseErrorWhenOIDCPKCEEnforceValueInvalid(t *testing.T) {
|
func TestShouldRaiseErrorWhenOIDCPKCEEnforceValueInvalid(t *testing.T) {
|
||||||
|
@ -179,8 +179,8 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Errors: []string{
|
Errors: []string{
|
||||||
fmt.Sprintf(errFmtOIDCClientInvalidSecret, ""),
|
"identity_providers: oidc: client '': option 'secret' is required",
|
||||||
errFmtOIDCClientsWithEmptyID,
|
"identity_providers: oidc: one or more clients have been configured with an empty id",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -195,7 +195,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Errors: []string{fmt.Sprintf(errFmtOIDCClientInvalidPolicy, "client-1", "a-policy")},
|
Errors: []string{"identity_providers: oidc: client 'client-1': option 'policy' must be 'one_factor' or 'two_factor' but it is configured as 'a-policy'"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "ClientIDDuplicated",
|
Name: "ClientIDDuplicated",
|
||||||
|
@ -427,7 +427,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadGrantTypes(t *testing.T)
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
GrantTypes: []string{"bad_grant_type"},
|
GrantTypes: []string{"bad_grant_type"},
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -454,7 +454,7 @@ func TestShouldNotErrorOnCertificateValid(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com/callback",
|
"https://google.com/callback",
|
||||||
|
@ -480,7 +480,7 @@ func TestShouldRaiseErrorOnCertificateNotValid(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com/callback",
|
"https://google.com/callback",
|
||||||
|
@ -507,7 +507,7 @@ func TestShouldRaiseErrorOnKeySizeTooSmall(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com/callback",
|
"https://google.com/callback",
|
||||||
|
@ -587,7 +587,7 @@ func TestValidateIdentityProvidersShouldRaiseWarningOnSecurityIssue(t *testing.T
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com/callback",
|
"https://google.com/callback",
|
||||||
|
@ -623,7 +623,7 @@ func TestValidateIdentityProvidersShouldRaiseErrorsOnInvalidClientTypes(t *testi
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "client-with-bad-redirect-uri",
|
ID: "client-with-bad-redirect-uri",
|
||||||
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||||
Public: false,
|
Public: false,
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -702,6 +702,33 @@ func TestValidateIdentityProvidersShouldNotRaiseErrorsOnValidClientOptions(t *te
|
||||||
assert.Len(t, validator.Warnings(), 0)
|
assert.Len(t, validator.Warnings(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateIdentityProvidersShouldRaiseWarningOnPlainTextClients(t *testing.T) {
|
||||||
|
validator := schema.NewStructValidator()
|
||||||
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
|
HMACSecret: "hmac1",
|
||||||
|
IssuerPrivateKey: MustParseRSAPrivateKey(testKey1),
|
||||||
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
{
|
||||||
|
ID: "client-with-invalid-secret_standard",
|
||||||
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
|
Policy: "two_factor",
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://localhost",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateIdentityProviders(config, validator)
|
||||||
|
|
||||||
|
assert.Len(t, validator.Errors(), 0)
|
||||||
|
require.Len(t, validator.Warnings(), 1)
|
||||||
|
|
||||||
|
assert.EqualError(t, validator.Warnings()[0], "identity_providers: oidc: client 'client-with-invalid-secret_standard': option 'secret' is plaintext but it should be a hashed value as plaintext values are deprecated and will be removed when oidc becomes stable")
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
||||||
timeDay := time.Hour * 24
|
timeDay := time.Hour * 24
|
||||||
|
|
||||||
|
@ -713,7 +740,7 @@ func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
},
|
},
|
||||||
|
@ -722,7 +749,7 @@ func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
||||||
{
|
{
|
||||||
ID: "b-client",
|
ID: "b-client",
|
||||||
Description: "Normal Description",
|
Description: "Normal Description",
|
||||||
Secret: MustDecodeSecret("$plaintext$b-client-secret"),
|
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||||
Policy: policyOneFactor,
|
Policy: policyOneFactor,
|
||||||
UserinfoSigningAlgorithm: "RS256",
|
UserinfoSigningAlgorithm: "RS256",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -745,7 +772,7 @@ func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "c-client",
|
ID: "c-client",
|
||||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
},
|
},
|
||||||
|
@ -753,7 +780,7 @@ func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "d-client",
|
ID: "d-client",
|
||||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
},
|
},
|
||||||
|
@ -761,7 +788,7 @@ func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "e-client",
|
ID: "e-client",
|
||||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
Secret: MustDecodeSecret(goodOpenIDConnectClientSecret),
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
},
|
},
|
||||||
|
@ -1019,4 +1046,6 @@ AQmB98tdGLggbyXiODV2h+Rd37aFGb0QHzerIIsVNtMwlPCcp733D4kWJqTUYWZ+
|
||||||
KBL3XEahgs6Os5EYZ4aBAkEAjKE+2/nBYUdHVusjMXeNsE5rqwJND5zvYzmToG7+
|
KBL3XEahgs6Os5EYZ4aBAkEAjKE+2/nBYUdHVusjMXeNsE5rqwJND5zvYzmToG7+
|
||||||
xhv4RUAe4dHL4IDQoQRjhr3Nw+JYvtzBx0Iq/178xMnGKg==
|
xhv4RUAe4dHL4IDQoQRjhr3Nw+JYvtzBx0Iq/178xMnGKg==
|
||||||
-----END RSA PRIVATE KEY-----`
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
|
||||||
|
goodOpenIDConnectClientSecret = "$pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng" //nolint:gosec
|
||||||
)
|
)
|
||||||
|
|
|
@ -53,10 +53,20 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = client.ValidateAuthorizationPolicy(requester); err != nil {
|
if err = client.ValidatePARPolicy(requester, ctx.Providers.OpenIDConnect.GetPushedAuthorizeRequestURIPrefix(ctx)); err != nil {
|
||||||
rfc := fosite.ErrorToRFC6749Error(err)
|
rfc := fosite.ErrorToRFC6749Error(err)
|
||||||
|
|
||||||
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' failed to validate the authorization policy: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
|
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' failed to validate the PAR policy: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
|
||||||
|
|
||||||
|
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = client.ValidatePKCEPolicy(requester); err != nil {
|
||||||
|
rfc := fosite.ErrorToRFC6749Error(err)
|
||||||
|
|
||||||
|
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' failed to validate the PKCE policy: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
|
||||||
|
|
||||||
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, err)
|
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, err)
|
||||||
|
|
||||||
|
@ -95,13 +105,13 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
|
||||||
|
|
||||||
ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' was successfully processed, proceeding to build Authorization Response", requester.GetID(), clientID)
|
ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' was successfully processed, proceeding to build Authorization Response", requester.GetID(), clientID)
|
||||||
|
|
||||||
oidcSession := oidc.NewSessionWithAuthorizeRequest(issuer, ctx.Providers.OpenIDConnect.KeyManager.GetActiveKeyID(),
|
session := oidc.NewSessionWithAuthorizeRequest(issuer, ctx.Providers.OpenIDConnect.KeyManager.GetActiveKeyID(),
|
||||||
userSession.Username, userSession.AuthenticationMethodRefs.MarshalRFC8176(), extraClaims, authTime, consent, requester)
|
userSession.Username, userSession.AuthenticationMethodRefs.MarshalRFC8176(), extraClaims, authTime, consent, requester)
|
||||||
|
|
||||||
ctx.Logger.Tracef("Authorization Request with id '%s' on client with id '%s' creating session for Authorization Response for subject '%s' with username '%s' with claims: %+v",
|
ctx.Logger.Tracef("Authorization Request with id '%s' on client with id '%s' creating session for Authorization Response for subject '%s' with username '%s' with claims: %+v",
|
||||||
requester.GetID(), oidcSession.ClientID, oidcSession.Subject, oidcSession.Username, oidcSession.Claims)
|
requester.GetID(), session.ClientID, session.Subject, session.Username, session.Claims)
|
||||||
|
|
||||||
if responder, err = ctx.Providers.OpenIDConnect.NewAuthorizeResponse(ctx, requester, oidcSession); err != nil {
|
if responder, err = ctx.Providers.OpenIDConnect.NewAuthorizeResponse(ctx, requester, session); err != nil {
|
||||||
rfc := fosite.ErrorToRFC6749Error(err)
|
rfc := fosite.ErrorToRFC6749Error(err)
|
||||||
|
|
||||||
ctx.Logger.Errorf("Authorization Response for Request with id '%s' on client with id '%s' could not be created: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
|
ctx.Logger.Errorf("Authorization Response for Request with id '%s' on client with id '%s' could not be created: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
|
||||||
|
@ -125,3 +135,62 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
|
||||||
|
|
||||||
ctx.Providers.OpenIDConnect.WriteAuthorizeResponse(ctx, rw, requester, responder)
|
ctx.Providers.OpenIDConnect.WriteAuthorizeResponse(ctx, rw, requester, responder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenIDConnectPushedAuthorizationRequest handles POST requests to the OAuth 2.0 Pushed Authorization Requests endpoint.
|
||||||
|
//
|
||||||
|
// RFC9126 https://www.rfc-editor.org/rfc/rfc9126.html
|
||||||
|
func OpenIDConnectPushedAuthorizationRequest(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, r *http.Request) {
|
||||||
|
var (
|
||||||
|
requester fosite.AuthorizeRequester
|
||||||
|
responder fosite.PushedAuthorizeResponder
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if requester, err = ctx.Providers.OpenIDConnect.NewPushedAuthorizeRequest(ctx, r); err != nil {
|
||||||
|
rfc := fosite.ErrorToRFC6749Error(err)
|
||||||
|
|
||||||
|
ctx.Logger.Errorf("Pushed Authorization Request failed with error: %s", rfc.WithExposeDebug(true).GetDescription())
|
||||||
|
|
||||||
|
ctx.Providers.OpenIDConnect.WritePushedAuthorizeError(ctx, rw, requester, err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var client *oidc.Client
|
||||||
|
|
||||||
|
clientID := requester.GetClient().GetID()
|
||||||
|
|
||||||
|
if client, err = ctx.Providers.OpenIDConnect.GetFullClient(clientID); err != nil {
|
||||||
|
if errors.Is(err, fosite.ErrNotFound) {
|
||||||
|
ctx.Logger.Errorf("Pushed Authorization Request with id '%s' on client with id '%s' could not be processed: client was not found", requester.GetID(), clientID)
|
||||||
|
} else {
|
||||||
|
ctx.Logger.Errorf("Pushed Authorization Request with id '%s' on client with id '%s' could not be processed: failed to find client: %+v", requester.GetID(), clientID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Providers.OpenIDConnect.WritePushedAuthorizeError(ctx, rw, requester, err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = client.ValidatePKCEPolicy(requester); err != nil {
|
||||||
|
rfc := fosite.ErrorToRFC6749Error(err)
|
||||||
|
|
||||||
|
ctx.Logger.Errorf("Pushed Authorization Request with id '%s' on client with id '%s' failed to validate the PKCE policy: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
|
||||||
|
|
||||||
|
ctx.Providers.OpenIDConnect.WritePushedAuthorizeError(ctx, rw, requester, err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if responder, err = ctx.Providers.OpenIDConnect.NewPushedAuthorizeResponse(ctx, requester, oidc.NewSession()); err != nil {
|
||||||
|
rfc := fosite.ErrorToRFC6749Error(err)
|
||||||
|
|
||||||
|
ctx.Logger.Errorf("Pushed Authorization Request failed with error: %s", rfc.WithExposeDebug(true).GetDescription())
|
||||||
|
|
||||||
|
ctx.Providers.OpenIDConnect.WritePushedAuthorizeError(ctx, rw, requester, err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Providers.OpenIDConnect.WritePushedAuthorizeResponse(ctx, rw, requester, responder)
|
||||||
|
}
|
||||||
|
|
|
@ -9,9 +9,8 @@ import (
|
||||||
mail "net/mail"
|
mail "net/mail"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
|
|
||||||
templates "github.com/authelia/authelia/v4/internal/templates"
|
templates "github.com/authelia/authelia/v4/internal/templates"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockNotifier is a mock of Notifier interface.
|
// MockNotifier is a mock of Notifier interface.
|
||||||
|
|
|
@ -10,11 +10,10 @@ import (
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
time "time"
|
time "time"
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
uuid "github.com/google/uuid"
|
|
||||||
|
|
||||||
model "github.com/authelia/authelia/v4/internal/model"
|
model "github.com/authelia/authelia/v4/internal/model"
|
||||||
storage "github.com/authelia/authelia/v4/internal/storage"
|
storage "github.com/authelia/authelia/v4/internal/storage"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
uuid "github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockStorage is a mock of Provider interface.
|
// MockStorage is a mock of Provider interface.
|
||||||
|
@ -40,6 +39,7 @@ func (m *MockStorage) EXPECT() *MockStorageMockRecorder {
|
||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// AppendAuthenticationLog mocks base method.
|
// AppendAuthenticationLog mocks base method.
|
||||||
func (m *MockStorage) AppendAuthenticationLog(arg0 context.Context, arg1 model.AuthenticationAttempt) error {
|
func (m *MockStorage) AppendAuthenticationLog(arg0 context.Context, arg1 model.AuthenticationAttempt) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
@ -270,6 +270,21 @@ func (mr *MockStorageMockRecorder) LoadOAuth2ConsentSessionByChallengeID(arg0, a
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOAuth2ConsentSessionByChallengeID", reflect.TypeOf((*MockStorage)(nil).LoadOAuth2ConsentSessionByChallengeID), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOAuth2ConsentSessionByChallengeID", reflect.TypeOf((*MockStorage)(nil).LoadOAuth2ConsentSessionByChallengeID), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadOAuth2PARContext mocks base method.
|
||||||
|
func (m *MockStorage) LoadOAuth2PARContext(arg0 context.Context, arg1 string) (*model.OAuth2PARContext, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "LoadOAuth2PARContext", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*model.OAuth2PARContext)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadOAuth2PARContext indicates an expected call of LoadOAuth2PARContext.
|
||||||
|
func (mr *MockStorageMockRecorder) LoadOAuth2PARContext(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOAuth2PARContext", reflect.TypeOf((*MockStorage)(nil).LoadOAuth2PARContext), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// LoadOAuth2Session mocks base method.
|
// LoadOAuth2Session mocks base method.
|
||||||
func (m *MockStorage) LoadOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 string) (*model.OAuth2Session, error) {
|
func (m *MockStorage) LoadOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 string) (*model.OAuth2Session, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
@ -435,6 +450,20 @@ func (mr *MockStorageMockRecorder) LoadWebauthnDevicesByUsername(arg0, arg1 inte
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebauthnDevicesByUsername", reflect.TypeOf((*MockStorage)(nil).LoadWebauthnDevicesByUsername), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebauthnDevicesByUsername", reflect.TypeOf((*MockStorage)(nil).LoadWebauthnDevicesByUsername), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RevokeOAuth2PARContext mocks base method.
|
||||||
|
func (m *MockStorage) RevokeOAuth2PARContext(arg0 context.Context, arg1 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "RevokeOAuth2PARContext", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevokeOAuth2PARContext indicates an expected call of RevokeOAuth2PARContext.
|
||||||
|
func (mr *MockStorageMockRecorder) RevokeOAuth2PARContext(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeOAuth2PARContext", reflect.TypeOf((*MockStorage)(nil).RevokeOAuth2PARContext), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// RevokeOAuth2Session mocks base method.
|
// RevokeOAuth2Session mocks base method.
|
||||||
func (m *MockStorage) RevokeOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 string) error {
|
func (m *MockStorage) RevokeOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
@ -576,6 +605,20 @@ func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSessionSubject(arg0, arg1 in
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentSessionSubject", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentSessionSubject), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentSessionSubject", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentSessionSubject), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveOAuth2PARContext mocks base method.
|
||||||
|
func (m *MockStorage) SaveOAuth2PARContext(arg0 context.Context, arg1 model.OAuth2PARContext) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SaveOAuth2PARContext", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveOAuth2PARContext indicates an expected call of SaveOAuth2PARContext.
|
||||||
|
func (mr *MockStorageMockRecorder) SaveOAuth2PARContext(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2PARContext", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2PARContext), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// SaveOAuth2Session mocks base method.
|
// SaveOAuth2Session mocks base method.
|
||||||
func (m *MockStorage) SaveOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 model.OAuth2Session) error {
|
func (m *MockStorage) SaveOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 model.OAuth2Session) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|
|
@ -7,9 +7,8 @@ package mocks
|
||||||
import (
|
import (
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
|
|
||||||
model "github.com/authelia/authelia/v4/internal/model"
|
model "github.com/authelia/authelia/v4/internal/model"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockTOTP is a mock of Provider interface.
|
// MockTOTP is a mock of Provider interface.
|
||||||
|
|
|
@ -7,9 +7,8 @@ package mocks
|
||||||
import (
|
import (
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
|
|
||||||
authentication "github.com/authelia/authelia/v4/internal/authentication"
|
authentication "github.com/authelia/authelia/v4/internal/authentication"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockUserProvider is a mock of UserProvider interface.
|
// MockUserProvider is a mock of UserProvider interface.
|
||||||
|
|
|
@ -39,6 +39,14 @@ func NewOAuth2ConsentSession(subject uuid.UUID, r fosite.Requester) (consent *OA
|
||||||
return consent, nil
|
return consent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewOAuth2BlacklistedJTI creates a new OAuth2BlacklistedJTI.
|
||||||
|
func NewOAuth2BlacklistedJTI(jti string, exp time.Time) (jtiBlacklist OAuth2BlacklistedJTI) {
|
||||||
|
return OAuth2BlacklistedJTI{
|
||||||
|
Signature: fmt.Sprintf("%x", sha256.Sum256([]byte(jti))),
|
||||||
|
ExpiresAt: exp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewOAuth2SessionFromRequest creates a new OAuth2Session from a signature and fosite.Requester.
|
// NewOAuth2SessionFromRequest creates a new OAuth2Session from a signature and fosite.Requester.
|
||||||
func NewOAuth2SessionFromRequest(signature string, r fosite.Requester) (session *OAuth2Session, err error) {
|
func NewOAuth2SessionFromRequest(signature string, r fosite.Requester) (session *OAuth2Session, err error) {
|
||||||
var (
|
var (
|
||||||
|
@ -77,12 +85,43 @@ func NewOAuth2SessionFromRequest(signature string, r fosite.Requester) (session
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOAuth2BlacklistedJTI creates a new OAuth2BlacklistedJTI.
|
// NewOAuth2PARContext creates a new Pushed Authorization Request Context as a OAuth2PARContext.
|
||||||
func NewOAuth2BlacklistedJTI(jti string, exp time.Time) (jtiBlacklist OAuth2BlacklistedJTI) {
|
func NewOAuth2PARContext(contextID string, r fosite.AuthorizeRequester) (context *OAuth2PARContext, err error) {
|
||||||
return OAuth2BlacklistedJTI{
|
var (
|
||||||
Signature: fmt.Sprintf("%x", sha256.Sum256([]byte(jti))),
|
s *OpenIDSession
|
||||||
ExpiresAt: exp,
|
ok bool
|
||||||
|
req *fosite.AuthorizeRequest
|
||||||
|
session []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
if s, ok = r.GetSession().(*OpenIDSession); !ok {
|
||||||
|
return nil, fmt.Errorf("can't convert type '%T' to an *OAuth2Session", r.GetSession())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if session, err = json.Marshal(s); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var handled StringSlicePipeDelimited
|
||||||
|
|
||||||
|
if req, ok = r.(*fosite.AuthorizeRequest); ok {
|
||||||
|
handled = StringSlicePipeDelimited(req.HandledResponseTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &OAuth2PARContext{
|
||||||
|
Signature: contextID,
|
||||||
|
RequestID: r.GetID(),
|
||||||
|
ClientID: r.GetClient().GetID(),
|
||||||
|
RequestedAt: r.GetRequestedAt(),
|
||||||
|
Scopes: StringSlicePipeDelimited(r.GetRequestedScopes()),
|
||||||
|
Audience: StringSlicePipeDelimited(r.GetRequestedAudience()),
|
||||||
|
HandledResponseTypes: handled,
|
||||||
|
ResponseMode: string(r.GetResponseMode()),
|
||||||
|
DefaultResponseMode: string(r.GetDefaultResponseMode()),
|
||||||
|
Revoked: false,
|
||||||
|
Form: r.GetRequestForm().Encode(),
|
||||||
|
Session: session,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OAuth2ConsentPreConfig stores information about an OAuth2.0 Pre-Configured Consent.
|
// OAuth2ConsentPreConfig stores information about an OAuth2.0 Pre-Configured Consent.
|
||||||
|
@ -264,6 +303,70 @@ func (s *OAuth2Session) ToRequest(ctx context.Context, session fosite.Session, s
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OAuth2PARContext holds relevant information about a Pushed Authorization Request in order to process the authorization.
|
||||||
|
type OAuth2PARContext struct {
|
||||||
|
ID int `db:"id"`
|
||||||
|
Signature string `db:"signature"`
|
||||||
|
RequestID string `db:"request_id"`
|
||||||
|
ClientID string `db:"client_id"`
|
||||||
|
RequestedAt time.Time `db:"requested_at"`
|
||||||
|
Scopes StringSlicePipeDelimited `db:"scopes"`
|
||||||
|
Audience StringSlicePipeDelimited `db:"audience"`
|
||||||
|
HandledResponseTypes StringSlicePipeDelimited `db:"handled_response_types"`
|
||||||
|
ResponseMode string `db:"response_mode"`
|
||||||
|
DefaultResponseMode string `db:"response_mode_default"`
|
||||||
|
Revoked bool `db:"revoked"`
|
||||||
|
Form string `db:"form_data"`
|
||||||
|
Session []byte `db:"session_data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (par *OAuth2PARContext) ToAuthorizeRequest(ctx context.Context, session fosite.Session, store fosite.Storage) (request *fosite.AuthorizeRequest, err error) {
|
||||||
|
if session != nil {
|
||||||
|
if err = json.Unmarshal(par.Session, session); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
client fosite.Client
|
||||||
|
form url.Values
|
||||||
|
)
|
||||||
|
|
||||||
|
if client, err = store.GetClient(ctx, par.ClientID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if form, err = url.ParseQuery(par.Form); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
request = fosite.NewAuthorizeRequest()
|
||||||
|
|
||||||
|
request.Request = fosite.Request{
|
||||||
|
ID: par.RequestID,
|
||||||
|
RequestedAt: par.RequestedAt,
|
||||||
|
Client: client,
|
||||||
|
RequestedScope: fosite.Arguments(par.Scopes),
|
||||||
|
RequestedAudience: fosite.Arguments(par.Audience),
|
||||||
|
Form: form,
|
||||||
|
Session: session,
|
||||||
|
}
|
||||||
|
|
||||||
|
if par.ResponseMode != "" {
|
||||||
|
request.ResponseMode = fosite.ResponseModeType(par.ResponseMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if par.DefaultResponseMode != "" {
|
||||||
|
request.DefaultResponseMode = fosite.ResponseModeType(par.DefaultResponseMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(par.HandledResponseTypes) != 0 {
|
||||||
|
request.HandledResponseTypes = fosite.Arguments(par.HandledResponseTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
// OpenIDSession holds OIDC Session information.
|
// OpenIDSession holds OIDC Session information.
|
||||||
type OpenIDSession struct {
|
type OpenIDSession struct {
|
||||||
*openid.DefaultSession `json:"id_token"`
|
*openid.DefaultSession `json:"id_token"`
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package oidc
|
package oidc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"strings"
|
||||||
|
|
||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
"github.com/ory/x/errorsx"
|
"github.com/ory/x/errorsx"
|
||||||
|
@ -32,6 +32,8 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client)
|
||||||
ResponseTypes: config.ResponseTypes,
|
ResponseTypes: config.ResponseTypes,
|
||||||
ResponseModes: []fosite.ResponseModeType{fosite.ResponseModeDefault},
|
ResponseModes: []fosite.ResponseModeType{fosite.ResponseModeDefault},
|
||||||
|
|
||||||
|
EnforcePAR: config.EnforcePAR,
|
||||||
|
|
||||||
UserinfoSigningAlgorithm: config.UserinfoSigningAlgorithm,
|
UserinfoSigningAlgorithm: config.UserinfoSigningAlgorithm,
|
||||||
|
|
||||||
Policy: authorization.NewLevel(config.Policy),
|
Policy: authorization.NewLevel(config.Policy),
|
||||||
|
@ -46,22 +48,22 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client)
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateAuthorizationPolicy is a helper function to validate additional policy constraints on a per-client basis.
|
// ValidatePKCEPolicy is a helper function to validate PKCE policy constraints on a per-client basis.
|
||||||
func (c *Client) ValidateAuthorizationPolicy(r fosite.Requester) (err error) {
|
func (c *Client) ValidatePKCEPolicy(r fosite.Requester) (err error) {
|
||||||
form := r.GetRequestForm()
|
form := r.GetRequestForm()
|
||||||
|
|
||||||
if c.EnforcePKCE {
|
if c.EnforcePKCE {
|
||||||
if form.Get("code_challenge") == "" {
|
if form.Get(FormParameterCodeChallenge) == "" {
|
||||||
return errorsx.WithStack(fosite.ErrInvalidRequest.
|
return errorsx.WithStack(fosite.ErrInvalidRequest.
|
||||||
WithHint("Clients must include a code_challenge when performing the authorize code flow, but it is missing.").
|
WithHint("Clients must include a code_challenge when performing the authorize code flow, but it is missing.").
|
||||||
WithDebug("The server is configured in a way that enforces PKCE for this client."))
|
WithDebug("The server is configured in a way that enforces PKCE for this client."))
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.EnforcePKCEChallengeMethod {
|
if c.EnforcePKCEChallengeMethod {
|
||||||
if method := form.Get("code_challenge_method"); method != c.PKCEChallengeMethod {
|
if method := form.Get(FormParameterCodeChallengeMethod); method != c.PKCEChallengeMethod {
|
||||||
return errorsx.WithStack(fosite.ErrInvalidRequest.
|
return errorsx.WithStack(fosite.ErrInvalidRequest.
|
||||||
WithHint(fmt.Sprintf("Client must use code_challenge_method=%s, %s is not allowed.", c.PKCEChallengeMethod, method)).
|
WithHintf("Client must use code_challenge_method=%s, %s is not allowed.", c.PKCEChallengeMethod, method).
|
||||||
WithDebug(fmt.Sprintf("The server is configured in a way that enforces PKCE %s as challenge method for this client.", c.PKCEChallengeMethod)))
|
WithDebugf("The server is configured in a way that enforces PKCE %s as challenge method for this client.", c.PKCEChallengeMethod))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +71,23 @@ func (c *Client) ValidateAuthorizationPolicy(r fosite.Requester) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidatePARPolicy is a helper function to validate additional policy constraints on a per-client basis.
|
||||||
|
func (c *Client) ValidatePARPolicy(r fosite.Requester, prefix string) (err error) {
|
||||||
|
form := r.GetRequestForm()
|
||||||
|
|
||||||
|
if c.EnforcePAR {
|
||||||
|
if requestURI := form.Get(FormParameterRequestURI); !strings.HasPrefix(requestURI, prefix) {
|
||||||
|
if requestURI == "" {
|
||||||
|
return errorsx.WithStack(ErrPAREnforcedClientMissingPAR.WithDebug("The request_uri parameter was empty."))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorsx.WithStack(ErrPAREnforcedClientMissingPAR.WithDebugf("The request_uri parameter '%s' is malformed.", requestURI))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient.
|
// IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient.
|
||||||
func (c *Client) IsAuthenticationLevelSufficient(level authentication.Level) bool {
|
func (c *Client) IsAuthenticationLevelSufficient(level authentication.Level) bool {
|
||||||
if level == authentication.NotAuthenticated {
|
if level == authentication.NotAuthenticated {
|
||||||
|
@ -105,7 +124,7 @@ func (c *Client) GetID() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHashedSecret returns the Secret.
|
// GetHashedSecret returns the Secret.
|
||||||
func (c *Client) GetHashedSecret() []byte {
|
func (c *Client) GetHashedSecret() (secret []byte) {
|
||||||
if c.Secret == nil {
|
if c.Secret == nil {
|
||||||
return []byte(nil)
|
return []byte(nil)
|
||||||
}
|
}
|
||||||
|
@ -114,7 +133,7 @@ func (c *Client) GetHashedSecret() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRedirectURIs returns the RedirectURIs.
|
// GetRedirectURIs returns the RedirectURIs.
|
||||||
func (c *Client) GetRedirectURIs() []string {
|
func (c *Client) GetRedirectURIs() (redirectURIs []string) {
|
||||||
return c.RedirectURIs
|
return c.RedirectURIs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,7 @@ func TestNewClientPKCE(t *testing.T) {
|
||||||
expectedEnforcePKCE bool
|
expectedEnforcePKCE bool
|
||||||
expectedEnforcePKCEChallengeMethod bool
|
expectedEnforcePKCEChallengeMethod bool
|
||||||
expected string
|
expected string
|
||||||
req *fosite.Request
|
r *fosite.Request
|
||||||
err string
|
err string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -288,8 +288,8 @@ func TestNewClientPKCE(t *testing.T) {
|
||||||
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.EnforcePKCEChallengeMethod)
|
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.EnforcePKCEChallengeMethod)
|
||||||
assert.Equal(t, tc.expected, client.PKCEChallengeMethod)
|
assert.Equal(t, tc.expected, client.PKCEChallengeMethod)
|
||||||
|
|
||||||
if tc.req != nil {
|
if tc.r != nil {
|
||||||
err := client.ValidateAuthorizationPolicy(tc.req)
|
err := client.ValidatePKCEPolicy(tc.r)
|
||||||
|
|
||||||
if tc.err != "" {
|
if tc.err != "" {
|
||||||
assert.EqualError(t, err, tc.err)
|
assert.EqualError(t, err, tc.err)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"hash"
|
"hash"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"path"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/go-retryablehttp"
|
"github.com/hashicorp/go-retryablehttp"
|
||||||
|
@ -23,8 +24,8 @@ import (
|
||||||
"github.com/authelia/authelia/v4/internal/utils"
|
"github.com/authelia/authelia/v4/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewConfig(config *schema.OpenIDConnectConfiguration, templates *templates.Provider) *Config {
|
func NewConfig(config *schema.OpenIDConnectConfiguration, templates *templates.Provider) (c *Config) {
|
||||||
c := &Config{
|
c = &Config{
|
||||||
GlobalSecret: []byte(utils.HashSHA256FromString(config.HMACSecret)),
|
GlobalSecret: []byte(utils.HashSHA256FromString(config.HMACSecret)),
|
||||||
SendDebugMessagesToClients: config.EnableClientDebugMessages,
|
SendDebugMessagesToClients: config.EnableClientDebugMessages,
|
||||||
MinParameterEntropy: config.MinimumParameterEntropy,
|
MinParameterEntropy: config.MinimumParameterEntropy,
|
||||||
|
@ -39,18 +40,23 @@ func NewConfig(config *schema.OpenIDConnectConfiguration, templates *templates.P
|
||||||
EnforcePublicClients: config.EnforcePKCE != "never",
|
EnforcePublicClients: config.EnforcePKCE != "never",
|
||||||
AllowPlainChallengeMethod: config.EnablePKCEPlainChallenge,
|
AllowPlainChallengeMethod: config.EnablePKCEPlainChallenge,
|
||||||
},
|
},
|
||||||
|
PAR: PARConfig{
|
||||||
|
Enforced: config.PAR.Enforce,
|
||||||
|
ContextLifespan: config.PAR.ContextLifespan,
|
||||||
|
URIPrefix: urnPARPrefix,
|
||||||
|
},
|
||||||
Templates: templates,
|
Templates: templates,
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Strategy.Core = &HMACCoreStrategy{
|
c.Strategy.Core = &HMACCoreStrategy{
|
||||||
Enigma: &hmac.HMACStrategy{Config: c},
|
Enigma: &hmac.HMACStrategy{Config: c},
|
||||||
Config: c,
|
Config: c,
|
||||||
prefix: tokenPrefixFmt,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Config is an implementation of the fosite.Configurator.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// GlobalSecret is the global secret used to sign and verify signatures.
|
// GlobalSecret is the global secret used to sign and verify signatures.
|
||||||
GlobalSecret []byte
|
GlobalSecret []byte
|
||||||
|
@ -67,7 +73,7 @@ type Config struct {
|
||||||
JWTScopeField jwt.JWTScopeFieldEnum
|
JWTScopeField jwt.JWTScopeFieldEnum
|
||||||
JWTMaxDuration time.Duration
|
JWTMaxDuration time.Duration
|
||||||
|
|
||||||
Hasher *AdaptiveHasher
|
Hasher *Hasher
|
||||||
Hash HashConfig
|
Hash HashConfig
|
||||||
Strategy StrategyConfig
|
Strategy StrategyConfig
|
||||||
PAR PARConfig
|
PAR PARConfig
|
||||||
|
@ -91,11 +97,13 @@ type Config struct {
|
||||||
Templates *templates.Provider
|
Templates *templates.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashConfig holds specific fosite.Configurator information for hashing.
|
||||||
type HashConfig struct {
|
type HashConfig struct {
|
||||||
ClientSecrets fosite.Hasher
|
ClientSecrets fosite.Hasher
|
||||||
HMAC func() (h hash.Hash)
|
HMAC func() (h hash.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StrategyConfig holds specific fosite.Configurator information for various strategies.
|
||||||
type StrategyConfig struct {
|
type StrategyConfig struct {
|
||||||
Core oauth2.CoreStrategy
|
Core oauth2.CoreStrategy
|
||||||
OpenID openid.OpenIDConnectTokenStrategy
|
OpenID openid.OpenIDConnectTokenStrategy
|
||||||
|
@ -105,17 +113,20 @@ type StrategyConfig struct {
|
||||||
ClientAuthentication fosite.ClientAuthenticationStrategy
|
ClientAuthentication fosite.ClientAuthenticationStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PARConfig holds specific fosite.Configurator information for Pushed Authorization Requests.
|
||||||
type PARConfig struct {
|
type PARConfig struct {
|
||||||
Enforced bool
|
Enforced bool
|
||||||
URIPrefix string
|
URIPrefix string
|
||||||
ContextLifespan time.Duration
|
ContextLifespan time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IssuersConfig holds specific fosite.Configurator information for the issuer.
|
||||||
type IssuersConfig struct {
|
type IssuersConfig struct {
|
||||||
IDToken string
|
IDToken string
|
||||||
AccessToken string
|
AccessToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandlersConfig holds specific fosite.Configurator handlers configuration information.
|
||||||
type HandlersConfig struct {
|
type HandlersConfig struct {
|
||||||
// ResponseMode provides an extension handler for custom response modes.
|
// ResponseMode provides an extension handler for custom response modes.
|
||||||
ResponseMode fosite.ResponseModeHandler
|
ResponseMode fosite.ResponseModeHandler
|
||||||
|
@ -136,18 +147,21 @@ type HandlersConfig struct {
|
||||||
PushedAuthorizeEndpoint fosite.PushedAuthorizeEndpointHandlers
|
PushedAuthorizeEndpoint fosite.PushedAuthorizeEndpointHandlers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GrantTypeJWTBearerConfig holds specific fosite.Configurator information for the JWT Bearer Grant Type.
|
||||||
type GrantTypeJWTBearerConfig struct {
|
type GrantTypeJWTBearerConfig struct {
|
||||||
OptionalClientAuth bool
|
OptionalClientAuth bool
|
||||||
OptionalJTIClaim bool
|
OptionalJTIClaim bool
|
||||||
OptionalIssuedDate bool
|
OptionalIssuedDate bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProofKeyCodeExchangeConfig holds specific fosite.Configurator information for PKCE.
|
||||||
type ProofKeyCodeExchangeConfig struct {
|
type ProofKeyCodeExchangeConfig struct {
|
||||||
Enforce bool
|
Enforce bool
|
||||||
EnforcePublicClients bool
|
EnforcePublicClients bool
|
||||||
AllowPlainChallengeMethod bool
|
AllowPlainChallengeMethod bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LifespanConfig holds specific fosite.Configurator information for various lifespans.
|
||||||
type LifespanConfig struct {
|
type LifespanConfig struct {
|
||||||
AccessToken time.Duration
|
AccessToken time.Duration
|
||||||
AuthorizeCode time.Duration
|
AuthorizeCode time.Duration
|
||||||
|
@ -161,6 +175,7 @@ const (
|
||||||
PromptConsent = "consent"
|
PromptConsent = "consent"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LoadHandlers reloads the handlers based on the current configuration.
|
||||||
func (c *Config) LoadHandlers(store *Store, strategy jwt.Signer) {
|
func (c *Config) LoadHandlers(store *Store, strategy jwt.Signer) {
|
||||||
validator := openid.NewOpenIDConnectRequestValidator(strategy, c)
|
validator := openid.NewOpenIDConnectRequestValidator(strategy, c)
|
||||||
|
|
||||||
|
@ -277,6 +292,10 @@ func (c *Config) LoadHandlers(store *Store, strategy jwt.Signer) {
|
||||||
if h, ok := handler.(fosite.RevocationHandler); ok {
|
if h, ok := handler.(fosite.RevocationHandler); ok {
|
||||||
x.Revocation.Append(h)
|
x.Revocation.Append(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h, ok := handler.(fosite.PushedAuthorizeEndpointHandler); ok {
|
||||||
|
x.PushedAuthorizeEndpoint.Append(h)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Handlers = x
|
c.Handlers = x
|
||||||
|
@ -515,13 +534,24 @@ func (c *Config) GetFormPostHTMLTemplate(ctx context.Context) (tmpl *template.Te
|
||||||
|
|
||||||
// GetTokenURL returns the token URL.
|
// GetTokenURL returns the token URL.
|
||||||
func (c *Config) GetTokenURL(ctx context.Context) (tokenURL string) {
|
func (c *Config) GetTokenURL(ctx context.Context) (tokenURL string) {
|
||||||
|
if ctx, ok := ctx.(OpenIDConnectContext); ok {
|
||||||
|
tokenURI, err := ctx.IssuerURL()
|
||||||
|
if err != nil {
|
||||||
|
return c.TokenURL
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenURI.Path = path.Join(tokenURI.Path, EndpointPathToken)
|
||||||
|
|
||||||
|
return tokenURI.String()
|
||||||
|
}
|
||||||
|
|
||||||
return c.TokenURL
|
return c.TokenURL
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSecretsHasher returns the client secrets hashing function.
|
// GetSecretsHasher returns the client secrets hashing function.
|
||||||
func (c *Config) GetSecretsHasher(ctx context.Context) (hasher fosite.Hasher) {
|
func (c *Config) GetSecretsHasher(ctx context.Context) (hasher fosite.Hasher) {
|
||||||
if c.Hash.ClientSecrets == nil {
|
if c.Hash.ClientSecrets == nil {
|
||||||
c.Hash.ClientSecrets, _ = NewAdaptiveHasher()
|
c.Hash.ClientSecrets, _ = NewHasher()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Hash.ClientSecrets
|
return c.Hash.ClientSecrets
|
||||||
|
@ -583,7 +613,7 @@ func (c *Config) EnforcePushedAuthorize(ctx context.Context) bool {
|
||||||
|
|
||||||
// GetPushedAuthorizeContextLifespan is the lifespan of the short-lived PAR context.
|
// GetPushedAuthorizeContextLifespan is the lifespan of the short-lived PAR context.
|
||||||
func (c *Config) GetPushedAuthorizeContextLifespan(ctx context.Context) (lifespan time.Duration) {
|
func (c *Config) GetPushedAuthorizeContextLifespan(ctx context.Context) (lifespan time.Duration) {
|
||||||
if c.PAR.ContextLifespan == 0 {
|
if c.PAR.ContextLifespan.Seconds() == 0 {
|
||||||
c.PAR.ContextLifespan = lifespanPARContextDefault
|
c.PAR.ContextLifespan = lifespanPARContextDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,25 @@ const (
|
||||||
GrantTypeClientCredentials = "client_credentials"
|
GrantTypeClientCredentials = "client_credentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Client Auth Method strings.
|
||||||
|
const (
|
||||||
|
ClientAuthMethodClientSecretBasic = "client_secret_basic"
|
||||||
|
ClientAuthMethodClientSecretPost = "client_secret_post"
|
||||||
|
ClientAuthMethodClientSecretJWT = "client_secret_jwt"
|
||||||
|
ClientAuthMethodNone = "none"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Response Type strings.
|
||||||
|
const (
|
||||||
|
ResponseTypeAuthorizationCodeFlow = "code"
|
||||||
|
ResponseTypeImplicitFlowIDToken = "id_token"
|
||||||
|
ResponseTypeImplicitFlowToken = "token"
|
||||||
|
ResponseTypeImplicitFlowBoth = "id_token token"
|
||||||
|
ResponseTypeHybridFlowIDToken = "code id_token"
|
||||||
|
ResponseTypeHybridFlowToken = "code token"
|
||||||
|
ResponseTypeHybridFlowBoth = "code id_token token"
|
||||||
|
)
|
||||||
|
|
||||||
// Signing Algorithm strings.
|
// Signing Algorithm strings.
|
||||||
const (
|
const (
|
||||||
SigningAlgorithmNone = none
|
SigningAlgorithmNone = none
|
||||||
|
@ -91,13 +110,20 @@ const (
|
||||||
PKCEChallengeMethodSHA256 = "S256"
|
PKCEChallengeMethodSHA256 = "S256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FormParameterRequestURI = "request_uri"
|
||||||
|
FormParameterCodeChallenge = "code_challenge"
|
||||||
|
FormParameterCodeChallengeMethod = "code_challenge_method"
|
||||||
|
)
|
||||||
|
|
||||||
// Endpoints.
|
// Endpoints.
|
||||||
const (
|
const (
|
||||||
EndpointAuthorization = "authorization"
|
EndpointAuthorization = "authorization"
|
||||||
EndpointToken = "token"
|
EndpointToken = "token"
|
||||||
EndpointUserinfo = "userinfo"
|
EndpointUserinfo = "userinfo"
|
||||||
EndpointIntrospection = "introspection"
|
EndpointIntrospection = "introspection"
|
||||||
EndpointRevocation = "revocation"
|
EndpointRevocation = "revocation"
|
||||||
|
EndpointPushedAuthorizationRequest = "pushed-authorization-request"
|
||||||
)
|
)
|
||||||
|
|
||||||
// JWT Headers.
|
// JWT Headers.
|
||||||
|
@ -107,7 +133,9 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tokenPrefixFmt = "authelia_%s_" //nolint:gosec
|
tokenPrefixOrgAutheliaFmt = "authelia_%s_" //nolint:gosec
|
||||||
|
tokenPrefixOrgOryFmt = "ory_%s_" //nolint:gosec
|
||||||
|
|
||||||
tokenPrefixPartAccessToken = "at"
|
tokenPrefixPartAccessToken = "at"
|
||||||
tokenPrefixPartRefreshToken = "rt"
|
tokenPrefixPartRefreshToken = "rt"
|
||||||
tokenPrefixPartAuthorizeCode = "ac"
|
tokenPrefixPartAuthorizeCode = "ac"
|
||||||
|
@ -127,6 +155,8 @@ const (
|
||||||
EndpointPathUserinfo = EndpointPathRoot + "/" + EndpointUserinfo
|
EndpointPathUserinfo = EndpointPathRoot + "/" + EndpointUserinfo
|
||||||
EndpointPathIntrospection = EndpointPathRoot + "/" + EndpointIntrospection
|
EndpointPathIntrospection = EndpointPathRoot + "/" + EndpointIntrospection
|
||||||
EndpointPathRevocation = EndpointPathRoot + "/" + EndpointRevocation
|
EndpointPathRevocation = EndpointPathRoot + "/" + EndpointRevocation
|
||||||
|
|
||||||
|
EndpointPathPushedAuthorizationRequest = EndpointPathRoot + "/" + EndpointPushedAuthorizationRequest
|
||||||
)
|
)
|
||||||
|
|
||||||
// Authentication Method Reference Values https://datatracker.ietf.org/doc/html/rfc8176
|
// Authentication Method Reference Values https://datatracker.ietf.org/doc/html/rfc8176
|
||||||
|
|
|
@ -19,7 +19,6 @@ type HMACCoreStrategy struct {
|
||||||
fosite.RefreshTokenLifespanProvider
|
fosite.RefreshTokenLifespanProvider
|
||||||
fosite.AuthorizeCodeLifespanProvider
|
fosite.AuthorizeCodeLifespanProvider
|
||||||
}
|
}
|
||||||
prefix string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccessTokenSignature implements oauth2.AccessTokenStrategy.
|
// AccessTokenSignature implements oauth2.AccessTokenStrategy.
|
||||||
|
@ -112,11 +111,11 @@ func (h *HMACCoreStrategy) ValidateAuthorizeCode(ctx context.Context, r fosite.R
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HMACCoreStrategy) getPrefix(part string) string {
|
func (h *HMACCoreStrategy) getPrefix(part string) string {
|
||||||
if len(h.prefix) == 0 {
|
return h.getCustomPrefix(tokenPrefixOrgAutheliaFmt, part)
|
||||||
return ""
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf(h.prefix, part)
|
func (h *HMACCoreStrategy) getCustomPrefix(tokenPrefixFmt, part string) string {
|
||||||
|
return fmt.Sprintf(tokenPrefixFmt, part)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HMACCoreStrategy) setPrefix(token, part string) string {
|
func (h *HMACCoreStrategy) setPrefix(token, part string) string {
|
||||||
|
@ -124,5 +123,9 @@ func (h *HMACCoreStrategy) setPrefix(token, part string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HMACCoreStrategy) trimPrefix(token, part string) string {
|
func (h *HMACCoreStrategy) trimPrefix(token, part string) string {
|
||||||
|
if strings.HasPrefix(token, h.getCustomPrefix(tokenPrefixOrgOryFmt, part)) {
|
||||||
|
return strings.TrimPrefix(token, h.getCustomPrefix(tokenPrefixOrgOryFmt, part))
|
||||||
|
}
|
||||||
|
|
||||||
return strings.TrimPrefix(token, h.getPrefix(part))
|
return strings.TrimPrefix(token, h.getPrefix(part))
|
||||||
}
|
}
|
|
@ -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
|
package oidc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
)
|
||||||
|
|
||||||
// NewOpenIDConnectWellKnownConfiguration generates a new OpenIDConnectWellKnownConfiguration.
|
// NewOpenIDConnectWellKnownConfiguration generates a new OpenIDConnectWellKnownConfiguration.
|
||||||
func NewOpenIDConnectWellKnownConfiguration(enablePKCEPlainChallenge bool, clients map[string]*Client) (config OpenIDConnectWellKnownConfiguration) {
|
func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration, clients map[string]*Client) (config OpenIDConnectWellKnownConfiguration) {
|
||||||
config = OpenIDConnectWellKnownConfiguration{
|
config = OpenIDConnectWellKnownConfiguration{
|
||||||
CommonDiscoveryOptions: CommonDiscoveryOptions{
|
CommonDiscoveryOptions: CommonDiscoveryOptions{
|
||||||
SubjectTypesSupported: []string{
|
SubjectTypesSupported: []string{
|
||||||
SubjectTypePublic,
|
SubjectTypePublic,
|
||||||
},
|
},
|
||||||
ResponseTypesSupported: []string{
|
ResponseTypesSupported: []string{
|
||||||
"code",
|
ResponseTypeAuthorizationCodeFlow,
|
||||||
"token",
|
ResponseTypeImplicitFlowIDToken,
|
||||||
"id_token",
|
ResponseTypeImplicitFlowToken,
|
||||||
"code token",
|
ResponseTypeImplicitFlowBoth,
|
||||||
"code id_token",
|
ResponseTypeHybridFlowIDToken,
|
||||||
"token id_token",
|
ResponseTypeHybridFlowToken,
|
||||||
"code token id_token",
|
ResponseTypeHybridFlowBoth,
|
||||||
"none",
|
},
|
||||||
|
GrantTypesSupported: []string{
|
||||||
|
GrantTypeAuthorizationCode,
|
||||||
|
GrantTypeImplicit,
|
||||||
|
GrantTypeRefreshToken,
|
||||||
},
|
},
|
||||||
ResponseModesSupported: []string{
|
ResponseModesSupported: []string{
|
||||||
ResponseModeFormPost,
|
ResponseModeFormPost,
|
||||||
|
@ -49,6 +57,12 @@ func NewOpenIDConnectWellKnownConfiguration(enablePKCEPlainChallenge bool, clien
|
||||||
ClaimPreferredUsername,
|
ClaimPreferredUsername,
|
||||||
ClaimFullName,
|
ClaimFullName,
|
||||||
},
|
},
|
||||||
|
TokenEndpointAuthMethodsSupported: []string{
|
||||||
|
ClientAuthMethodClientSecretBasic,
|
||||||
|
ClientAuthMethodClientSecretPost,
|
||||||
|
ClientAuthMethodClientSecretJWT,
|
||||||
|
ClientAuthMethodNone,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
OAuth2DiscoveryOptions: OAuth2DiscoveryOptions{
|
OAuth2DiscoveryOptions: OAuth2DiscoveryOptions{
|
||||||
CodeChallengeMethodsSupported: []string{
|
CodeChallengeMethodsSupported: []string{
|
||||||
|
@ -68,6 +82,9 @@ func NewOpenIDConnectWellKnownConfiguration(enablePKCEPlainChallenge bool, clien
|
||||||
SigningAlgorithmRSAWithSHA256,
|
SigningAlgorithmRSAWithSHA256,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
PushedAuthorizationDiscoveryOptions: PushedAuthorizationDiscoveryOptions{
|
||||||
|
RequirePushedAuthorizationRequests: c.PAR.Enforce,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var pairwise, public bool
|
var pairwise, public bool
|
||||||
|
@ -86,7 +103,7 @@ func NewOpenIDConnectWellKnownConfiguration(enablePKCEPlainChallenge bool, clien
|
||||||
config.SubjectTypesSupported = append(config.SubjectTypesSupported, SubjectTypePairwise)
|
config.SubjectTypesSupported = append(config.SubjectTypesSupported, SubjectTypePairwise)
|
||||||
}
|
}
|
||||||
|
|
||||||
if enablePKCEPlainChallenge {
|
if c.EnablePKCEPlainChallenge {
|
||||||
config.CodeChallengeMethodsSupported = append(config.CodeChallengeMethodsSupported, PKCEChallengeMethodPlain)
|
config.CodeChallengeMethodsSupported = append(config.CodeChallengeMethodsSupported, PKCEChallengeMethodPlain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,15 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
pkcePlainChallenge bool
|
pkcePlainChallenge bool
|
||||||
|
enforcePAR bool
|
||||||
clients map[string]*Client
|
clients map[string]*Client
|
||||||
|
|
||||||
expectCodeChallengeMethodsSupported, expectSubjectTypesSupported []string
|
expectCodeChallengeMethodsSupported, expectSubjectTypesSupported []string
|
||||||
|
@ -63,7 +66,14 @@ func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
actual := NewOpenIDConnectWellKnownConfiguration(tc.pkcePlainChallenge, tc.clients)
|
c := schema.OpenIDConnectConfiguration{
|
||||||
|
EnablePKCEPlainChallenge: tc.pkcePlainChallenge,
|
||||||
|
PAR: schema.OpenIDConnectPARConfiguration{
|
||||||
|
Enforce: tc.enforcePAR,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := NewOpenIDConnectWellKnownConfiguration(&c, tc.clients)
|
||||||
for _, codeChallengeMethod := range tc.expectCodeChallengeMethodsSupported {
|
for _, codeChallengeMethod := range tc.expectCodeChallengeMethodsSupported {
|
||||||
assert.Contains(t, actual.CodeChallengeMethodsSupported, codeChallengeMethod)
|
assert.Contains(t, actual.CodeChallengeMethodsSupported, codeChallengeMethod)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,27 @@ import (
|
||||||
var errPasswordsDoNotMatch = errors.New("the passwords don't match")
|
var errPasswordsDoNotMatch = errors.New("the passwords don't match")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrIssuerCouldNotDerive = fosite.ErrServerError.WithHint("Could not safely derive the issuer.")
|
// ErrIssuerCouldNotDerive is sent when the issuer couldn't be determined from the headers.
|
||||||
ErrSubjectCouldNotLookup = fosite.ErrServerError.WithHint("Could not lookup user subject.")
|
ErrIssuerCouldNotDerive = fosite.ErrServerError.WithHint("Could not safely derive the issuer.")
|
||||||
ErrConsentCouldNotPerform = fosite.ErrServerError.WithHint("Could not perform consent.")
|
|
||||||
ErrConsentCouldNotGenerate = fosite.ErrServerError.WithHint("Could not generate the consent session.")
|
// ErrSubjectCouldNotLookup is sent when the Subject Identifier for a user couldn't be generated or obtained from the database.
|
||||||
ErrConsentCouldNotSave = fosite.ErrServerError.WithHint("Could not save the consent session.")
|
ErrSubjectCouldNotLookup = fosite.ErrServerError.WithHint("Could not lookup user subject.")
|
||||||
ErrConsentCouldNotLookup = fosite.ErrServerError.WithHint("Failed to lookup the consent session.")
|
|
||||||
|
// 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.")
|
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"
|
"github.com/go-crypt/crypt/algorithm/plaintext"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewAdaptiveHasher() (hasher *AdaptiveHasher, err error) {
|
// NewHasher returns a new Hasher.
|
||||||
hasher = &AdaptiveHasher{}
|
func NewHasher() (hasher *Hasher, err error) {
|
||||||
|
hasher = &Hasher{}
|
||||||
|
|
||||||
if hasher.decoder, err = crypt.NewDefaultDecoder(); err != nil {
|
if hasher.decoder, err = crypt.NewDefaultDecoder(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -22,13 +23,13 @@ func NewAdaptiveHasher() (hasher *AdaptiveHasher, err error) {
|
||||||
return hasher, nil
|
return hasher, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AdaptiveHasher implements the fosite.Hasher interface without an actual hashing algo.
|
// Hasher implements the fosite.Hasher interface and adaptively compares hashes.
|
||||||
type AdaptiveHasher struct {
|
type Hasher struct {
|
||||||
decoder algorithm.DecoderRegister
|
decoder algorithm.DecoderRegister
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare compares the hash with the data and returns an error if they don't match.
|
// Compare compares the hash with the data and returns an error if they don't match.
|
||||||
func (h *AdaptiveHasher) Compare(_ context.Context, hash, data []byte) (err error) {
|
func (h Hasher) Compare(_ context.Context, hash, data []byte) (err error) {
|
||||||
var digest algorithm.Digest
|
var digest algorithm.Digest
|
||||||
|
|
||||||
if digest, err = h.decoder.Decode(string(hash)); err != nil {
|
if digest, err = h.decoder.Decode(string(hash)); err != nil {
|
||||||
|
@ -43,6 +44,6 @@ func (h *AdaptiveHasher) Compare(_ context.Context, hash, data []byte) (err erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash creates a new hash from data.
|
// Hash creates a new hash from data.
|
||||||
func (h *AdaptiveHasher) Hash(_ context.Context, data []byte) (hash []byte, err error) {
|
func (h Hasher) Hash(_ context.Context, data []byte) (hash []byte, err error) {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
|
func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
|
||||||
hasher, err := NewAdaptiveHasher()
|
hasher, err := NewHasher()
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotRaiseErrorOnEqualPasswordsPlainTextWithSeparator(t *testing.T) {
|
func TestShouldNotRaiseErrorOnEqualPasswordsPlainTextWithSeparator(t *testing.T) {
|
||||||
hasher, err := NewAdaptiveHasher()
|
hasher, err := NewHasher()
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ func TestShouldNotRaiseErrorOnEqualPasswordsPlainTextWithSeparator(t *testing.T)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
|
func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
|
||||||
hasher, err := NewAdaptiveHasher()
|
hasher, err := NewHasher()
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldHashPassword(t *testing.T) {
|
func TestShouldHashPassword(t *testing.T) {
|
||||||
hasher := AdaptiveHasher{}
|
hasher := Hasher{}
|
||||||
|
|
||||||
data := []byte("abc")
|
data := []byte("abc")
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, store s
|
||||||
|
|
||||||
provider.Config.LoadHandlers(provider.Store, provider.KeyManager.Strategy())
|
provider.Config.LoadHandlers(provider.Store, provider.KeyManager.Strategy())
|
||||||
|
|
||||||
provider.discovery = NewOpenIDConnectWellKnownConfiguration(config.EnablePKCEPlainChallenge, provider.Store.clients)
|
provider.discovery = NewOpenIDConnectWellKnownConfiguration(config, provider.Store.clients)
|
||||||
|
|
||||||
return provider, nil
|
return provider, nil
|
||||||
}
|
}
|
||||||
|
@ -50,12 +50,12 @@ func (p *OpenIDConnectProvider) GetOAuth2WellKnownConfiguration(issuer string) O
|
||||||
}
|
}
|
||||||
|
|
||||||
options.Issuer = issuer
|
options.Issuer = issuer
|
||||||
|
|
||||||
options.JWKSURI = fmt.Sprintf("%s%s", issuer, EndpointPathJWKs)
|
options.JWKSURI = fmt.Sprintf("%s%s", issuer, EndpointPathJWKs)
|
||||||
|
|
||||||
options.IntrospectionEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathIntrospection)
|
|
||||||
options.TokenEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathToken)
|
|
||||||
|
|
||||||
options.AuthorizationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathAuthorization)
|
options.AuthorizationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathAuthorization)
|
||||||
|
options.PushedAuthorizationRequestEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathPushedAuthorizationRequest)
|
||||||
|
options.TokenEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathToken)
|
||||||
|
options.IntrospectionEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathIntrospection)
|
||||||
options.RevocationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathRevocation)
|
options.RevocationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathRevocation)
|
||||||
|
|
||||||
return options
|
return options
|
||||||
|
@ -72,14 +72,14 @@ func (p *OpenIDConnectProvider) GetOpenIDConnectWellKnownConfiguration(issuer st
|
||||||
}
|
}
|
||||||
|
|
||||||
options.Issuer = issuer
|
options.Issuer = issuer
|
||||||
|
|
||||||
options.JWKSURI = fmt.Sprintf("%s%s", issuer, EndpointPathJWKs)
|
options.JWKSURI = fmt.Sprintf("%s%s", issuer, EndpointPathJWKs)
|
||||||
|
|
||||||
options.IntrospectionEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathIntrospection)
|
|
||||||
options.TokenEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathToken)
|
|
||||||
|
|
||||||
options.AuthorizationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathAuthorization)
|
options.AuthorizationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathAuthorization)
|
||||||
options.RevocationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathRevocation)
|
options.PushedAuthorizationRequestEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathPushedAuthorizationRequest)
|
||||||
|
options.TokenEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathToken)
|
||||||
options.UserinfoEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathUserinfo)
|
options.UserinfoEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathUserinfo)
|
||||||
|
options.IntrospectionEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathIntrospection)
|
||||||
|
options.RevocationEndpoint = fmt.Sprintf("%s%s", issuer, EndpointPathRevocation)
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,15 +142,25 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
||||||
assert.Len(t, disco.SubjectTypesSupported, 1)
|
assert.Len(t, disco.SubjectTypesSupported, 1)
|
||||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
||||||
|
|
||||||
assert.Len(t, disco.ResponseTypesSupported, 8)
|
assert.Len(t, disco.ResponseTypesSupported, 7)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "code")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowIDToken)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "id_token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowToken)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "code token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowBoth)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "code id_token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowIDToken)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "token id_token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "code token id_token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "none")
|
|
||||||
|
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 4)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretJWT)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone)
|
||||||
|
|
||||||
|
assert.Len(t, disco.GrantTypesSupported, 3)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, GrantTypeAuthorizationCode)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, GrantTypeRefreshToken)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, GrantTypeImplicit)
|
||||||
|
|
||||||
assert.Len(t, disco.IDTokenSigningAlgValuesSupported, 1)
|
assert.Len(t, disco.IDTokenSigningAlgValuesSupported, 1)
|
||||||
assert.Contains(t, disco.IDTokenSigningAlgValuesSupported, SigningAlgorithmRSAWithSHA256)
|
assert.Contains(t, disco.IDTokenSigningAlgValuesSupported, SigningAlgorithmRSAWithSHA256)
|
||||||
|
@ -231,15 +241,25 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
|
||||||
assert.Len(t, disco.SubjectTypesSupported, 1)
|
assert.Len(t, disco.SubjectTypesSupported, 1)
|
||||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
||||||
|
|
||||||
assert.Len(t, disco.ResponseTypesSupported, 8)
|
assert.Len(t, disco.ResponseTypesSupported, 7)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "code")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowIDToken)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "id_token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowToken)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "code token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowBoth)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "code id_token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowIDToken)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "token id_token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "code token id_token")
|
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth)
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, "none")
|
|
||||||
|
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 4)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretJWT)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone)
|
||||||
|
|
||||||
|
assert.Len(t, disco.GrantTypesSupported, 3)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, GrantTypeAuthorizationCode)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, GrantTypeRefreshToken)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, GrantTypeImplicit)
|
||||||
|
|
||||||
assert.Len(t, disco.ClaimsSupported, 18)
|
assert.Len(t, disco.ClaimsSupported, 18)
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimAuthenticationMethodsReference)
|
assert.Contains(t, disco.ClaimsSupported, ClaimAuthenticationMethodsReference)
|
||||||
|
|
|
@ -165,7 +165,7 @@ func (s *Store) InvalidateAuthorizeCodeSession(ctx context.Context, code string)
|
||||||
// This implements a portion of oauth2.AuthorizeCodeStorage.
|
// This implements a portion of oauth2.AuthorizeCodeStorage.
|
||||||
func (s *Store) GetAuthorizeCodeSession(ctx context.Context, code string, session fosite.Session) (request fosite.Requester, err error) {
|
func (s *Store) GetAuthorizeCodeSession(ctx context.Context, code string, session fosite.Session) (request fosite.Requester, err error) {
|
||||||
// TODO: Implement the fosite.ErrInvalidatedAuthorizeCode error above. This requires splitting the invalidated sessions and deleted sessions.
|
// TODO: Implement the fosite.ErrInvalidatedAuthorizeCode error above. This requires splitting the invalidated sessions and deleted sessions.
|
||||||
return s.loadSessionBySignature(ctx, storage.OAuth2SessionTypeAuthorizeCode, code, session)
|
return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeAuthorizeCode, code, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAccessTokenSession stores the authorization request for a given access token.
|
// CreateAccessTokenSession stores the authorization request for a given access token.
|
||||||
|
@ -190,7 +190,7 @@ func (s *Store) RevokeAccessToken(ctx context.Context, requestID string) (err er
|
||||||
// GetAccessTokenSession gets the authorization request for a given access token.
|
// GetAccessTokenSession gets the authorization request for a given access token.
|
||||||
// This implements a portion of oauth2.AccessTokenStorage.
|
// This implements a portion of oauth2.AccessTokenStorage.
|
||||||
func (s *Store) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) {
|
func (s *Store) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) {
|
||||||
return s.loadSessionBySignature(ctx, storage.OAuth2SessionTypeAccessToken, signature, session)
|
return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeAccessToken, signature, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateRefreshTokenSession stores the authorization request for a given refresh token.
|
// CreateRefreshTokenSession stores the authorization request for a given refresh token.
|
||||||
|
@ -223,7 +223,7 @@ func (s *Store) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requestI
|
||||||
// GetRefreshTokenSession gets the authorization request for a given refresh token.
|
// GetRefreshTokenSession gets the authorization request for a given refresh token.
|
||||||
// This implements a portion of oauth2.RefreshTokenStorage.
|
// This implements a portion of oauth2.RefreshTokenStorage.
|
||||||
func (s *Store) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) {
|
func (s *Store) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) {
|
||||||
return s.loadSessionBySignature(ctx, storage.OAuth2SessionTypeRefreshToken, signature, session)
|
return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeRefreshToken, signature, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreatePKCERequestSession stores the authorization request for a given PKCE request.
|
// CreatePKCERequestSession stores the authorization request for a given PKCE request.
|
||||||
|
@ -241,7 +241,7 @@ func (s *Store) DeletePKCERequestSession(ctx context.Context, signature string)
|
||||||
// GetPKCERequestSession gets the authorization request for a given PKCE request.
|
// GetPKCERequestSession gets the authorization request for a given PKCE request.
|
||||||
// This implements a portion of pkce.PKCERequestStorage.
|
// This implements a portion of pkce.PKCERequestStorage.
|
||||||
func (s *Store) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (requester fosite.Requester, err error) {
|
func (s *Store) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (requester fosite.Requester, err error) {
|
||||||
return s.loadSessionBySignature(ctx, storage.OAuth2SessionTypePKCEChallenge, signature, session)
|
return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypePKCEChallenge, signature, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateOpenIDConnectSession creates an open id connect session for a given authorize code.
|
// CreateOpenIDConnectSession creates an open id connect session for a given authorize code.
|
||||||
|
@ -263,7 +263,37 @@ func (s *Store) DeleteOpenIDConnectSession(ctx context.Context, authorizeCode st
|
||||||
// - or an arbitrary error if an error occurred.
|
// - or an arbitrary error if an error occurred.
|
||||||
// This implements a portion of openid.OpenIDConnectRequestStorage.
|
// This implements a portion of openid.OpenIDConnectRequestStorage.
|
||||||
func (s *Store) GetOpenIDConnectSession(ctx context.Context, authorizeCode string, request fosite.Requester) (r fosite.Requester, err error) {
|
func (s *Store) GetOpenIDConnectSession(ctx context.Context, authorizeCode string, request fosite.Requester) (r fosite.Requester, err error) {
|
||||||
return s.loadSessionBySignature(ctx, storage.OAuth2SessionTypeOpenIDConnect, authorizeCode, request.GetSession())
|
return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeOpenIDConnect, authorizeCode, request.GetSession())
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePARSession stores the pushed authorization request context. The requestURI is used to derive the key.
|
||||||
|
// This implements a portion of fosite.PARStorage.
|
||||||
|
func (s *Store) CreatePARSession(ctx context.Context, requestURI string, request fosite.AuthorizeRequester) (err error) {
|
||||||
|
var par *model.OAuth2PARContext
|
||||||
|
|
||||||
|
if par, err = model.NewOAuth2PARContext(requestURI, request); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.provider.SaveOAuth2PARContext(ctx, *par)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPARSession gets the push authorization request context. The caller is expected to merge the AuthorizeRequest.
|
||||||
|
// This implements a portion of fosite.PARStorage.
|
||||||
|
func (s *Store) GetPARSession(ctx context.Context, requestURI string) (request fosite.AuthorizeRequester, err error) {
|
||||||
|
var par *model.OAuth2PARContext
|
||||||
|
|
||||||
|
if par, err = s.provider.LoadOAuth2PARContext(ctx, requestURI); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return par.ToAuthorizeRequest(ctx, NewSession(), s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePARSession deletes the context.
|
||||||
|
// This implements a portion of fosite.PARStorage.
|
||||||
|
func (s *Store) DeletePARSession(ctx context.Context, requestURI string) (err error) {
|
||||||
|
return s.provider.RevokeOAuth2PARContext(ctx, requestURI)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsJWTUsed implements an interface required for RFC7523.
|
// IsJWTUsed implements an interface required for RFC7523.
|
||||||
|
@ -280,7 +310,7 @@ func (s *Store) MarkJWTUsedForTime(ctx context.Context, jti string, exp time.Tim
|
||||||
return s.SetClientAssertionJWT(ctx, jti, exp)
|
return s.SetClientAssertionJWT(ctx, jti, exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) loadSessionBySignature(ctx context.Context, sessionType storage.OAuth2SessionType, signature string, session fosite.Session) (r fosite.Requester, err error) {
|
func (s *Store) loadRequesterBySignature(ctx context.Context, sessionType storage.OAuth2SessionType, signature string, session fosite.Session) (r fosite.Requester, err error) {
|
||||||
var (
|
var (
|
||||||
sessionModel *model.OAuth2Session
|
sessionModel *model.OAuth2Session
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package oidc
|
package oidc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -118,6 +119,8 @@ type Client struct {
|
||||||
ResponseTypes []string
|
ResponseTypes []string
|
||||||
ResponseModes []fosite.ResponseModeType
|
ResponseModes []fosite.ResponseModeType
|
||||||
|
|
||||||
|
EnforcePAR bool
|
||||||
|
|
||||||
UserinfoSigningAlgorithm string
|
UserinfoSigningAlgorithm string
|
||||||
|
|
||||||
Policy authorization.Level
|
Policy authorization.Level
|
||||||
|
@ -643,3 +646,10 @@ type OpenIDConnectWellKnownConfiguration struct {
|
||||||
OpenIDConnectFrontChannelLogoutDiscoveryOptions
|
OpenIDConnectFrontChannelLogoutDiscoveryOptions
|
||||||
OpenIDConnectBackChannelLogoutDiscoveryOptions
|
OpenIDConnectBackChannelLogoutDiscoveryOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenIDConnectContext represents the context implementation that is used by some OpenID Connect 1.0 implementations.
|
||||||
|
type OpenIDConnectContext interface {
|
||||||
|
context.Context
|
||||||
|
|
||||||
|
IssuerURL() (issuerURL *url.URL, err error)
|
||||||
|
}
|
||||||
|
|
|
@ -331,6 +331,15 @@ func handleRouter(config *schema.Configuration, providers middlewares.Providers)
|
||||||
r.GET("/api/oidc/authorize", policyCORSAuthorization.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
|
r.GET("/api/oidc/authorize", policyCORSAuthorization.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
|
||||||
r.POST("/api/oidc/authorize", policyCORSAuthorization.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
|
r.POST("/api/oidc/authorize", policyCORSAuthorization.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectAuthorization))))
|
||||||
|
|
||||||
|
policyCORSPAR := middlewares.NewCORSPolicyBuilder().
|
||||||
|
WithAllowedMethods(fasthttp.MethodOptions, fasthttp.MethodPost).
|
||||||
|
WithAllowedOrigins(allowedOrigins...).
|
||||||
|
WithEnabled(utils.IsStringInSliceFold(oidc.EndpointPushedAuthorizationRequest, config.IdentityProviders.OIDC.CORS.Endpoints)).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
r.OPTIONS(oidc.EndpointPathPushedAuthorizationRequest, policyCORSPAR.HandleOnlyOPTIONS)
|
||||||
|
r.POST(oidc.EndpointPathPushedAuthorizationRequest, policyCORSPAR.Middleware(bridgeOIDC(middlewares.NewHTTPToAutheliaHandlerAdaptor(handlers.OpenIDConnectPushedAuthorizationRequest))))
|
||||||
|
|
||||||
policyCORSToken := middlewares.NewCORSPolicyBuilder().
|
policyCORSToken := middlewares.NewCORSPolicyBuilder().
|
||||||
WithAllowCredentials(true).
|
WithAllowCredentials(true).
|
||||||
WithAllowedMethods(fasthttp.MethodOptions, fasthttp.MethodPost).
|
WithAllowedMethods(fasthttp.MethodOptions, fasthttp.MethodPost).
|
||||||
|
|
|
@ -13,15 +13,16 @@ const (
|
||||||
tableUserPreferences = "user_preferences"
|
tableUserPreferences = "user_preferences"
|
||||||
tableWebauthnDevices = "webauthn_devices"
|
tableWebauthnDevices = "webauthn_devices"
|
||||||
|
|
||||||
|
tableOAuth2BlacklistedJTI = "oauth2_blacklisted_jti"
|
||||||
tableOAuth2ConsentSession = "oauth2_consent_session"
|
tableOAuth2ConsentSession = "oauth2_consent_session"
|
||||||
tableOAuth2ConsentPreConfiguration = "oauth2_consent_preconfiguration"
|
tableOAuth2ConsentPreConfiguration = "oauth2_consent_preconfiguration"
|
||||||
|
|
||||||
|
tableOAuth2AccessTokenSession = "oauth2_access_token_session" //nolint:gosec // This is not a hardcoded credential.
|
||||||
tableOAuth2AuthorizeCodeSession = "oauth2_authorization_code_session"
|
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"
|
|
||||||
tableOAuth2OpenIDConnectSession = "oauth2_openid_connect_session"
|
tableOAuth2OpenIDConnectSession = "oauth2_openid_connect_session"
|
||||||
tableOAuth2BlacklistedJTI = "oauth2_blacklisted_jti"
|
tableOAuth2PARContext = "oauth2_par_context"
|
||||||
|
tableOAuth2PKCERequestSession = "oauth2_pkce_request_session"
|
||||||
|
tableOAuth2RefreshTokenSession = "oauth2_refresh_token_session" //nolint:gosec // This is not a hardcoded credential.
|
||||||
|
|
||||||
tableMigrations = "migrations"
|
tableMigrations = "migrations"
|
||||||
tableEncryption = "encryption"
|
tableEncryption = "encryption"
|
||||||
|
@ -32,26 +33,29 @@ type OAuth2SessionType int
|
||||||
|
|
||||||
// Representation of specific OAuth 2.0 session types.
|
// Representation of specific OAuth 2.0 session types.
|
||||||
const (
|
const (
|
||||||
OAuth2SessionTypeAuthorizeCode OAuth2SessionType = iota
|
OAuth2SessionTypeAccessToken OAuth2SessionType = iota
|
||||||
OAuth2SessionTypeAccessToken
|
OAuth2SessionTypeAuthorizeCode
|
||||||
OAuth2SessionTypeRefreshToken
|
|
||||||
OAuth2SessionTypePKCEChallenge
|
|
||||||
OAuth2SessionTypeOpenIDConnect
|
OAuth2SessionTypeOpenIDConnect
|
||||||
|
OAuth2SessionTypePAR
|
||||||
|
OAuth2SessionTypePKCEChallenge
|
||||||
|
OAuth2SessionTypeRefreshToken
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns a string representation of this OAuth2SessionType.
|
// String returns a string representation of this OAuth2SessionType.
|
||||||
func (s OAuth2SessionType) String() string {
|
func (s OAuth2SessionType) String() string {
|
||||||
switch s {
|
switch s {
|
||||||
case OAuth2SessionTypeAuthorizeCode:
|
|
||||||
return "authorization code"
|
|
||||||
case OAuth2SessionTypeAccessToken:
|
case OAuth2SessionTypeAccessToken:
|
||||||
return "access token"
|
return "access token"
|
||||||
case OAuth2SessionTypeRefreshToken:
|
case OAuth2SessionTypeAuthorizeCode:
|
||||||
return "refresh token"
|
return "authorization code"
|
||||||
case OAuth2SessionTypePKCEChallenge:
|
|
||||||
return "pkce challenge"
|
|
||||||
case OAuth2SessionTypeOpenIDConnect:
|
case OAuth2SessionTypeOpenIDConnect:
|
||||||
return "openid connect"
|
return "openid connect"
|
||||||
|
case OAuth2SessionTypePAR:
|
||||||
|
return "pushed authorization request context"
|
||||||
|
case OAuth2SessionTypePKCEChallenge:
|
||||||
|
return "pkce challenge"
|
||||||
|
case OAuth2SessionTypeRefreshToken:
|
||||||
|
return "refresh token"
|
||||||
default:
|
default:
|
||||||
return "invalid"
|
return "invalid"
|
||||||
}
|
}
|
||||||
|
@ -60,16 +64,18 @@ func (s OAuth2SessionType) String() string {
|
||||||
// Table returns the table name for this session type.
|
// Table returns the table name for this session type.
|
||||||
func (s OAuth2SessionType) Table() string {
|
func (s OAuth2SessionType) Table() string {
|
||||||
switch s {
|
switch s {
|
||||||
case OAuth2SessionTypeAuthorizeCode:
|
|
||||||
return tableOAuth2AuthorizeCodeSession
|
|
||||||
case OAuth2SessionTypeAccessToken:
|
case OAuth2SessionTypeAccessToken:
|
||||||
return tableOAuth2AccessTokenSession
|
return tableOAuth2AccessTokenSession
|
||||||
case OAuth2SessionTypeRefreshToken:
|
case OAuth2SessionTypeAuthorizeCode:
|
||||||
return tableOAuth2RefreshTokenSession
|
return tableOAuth2AuthorizeCodeSession
|
||||||
case OAuth2SessionTypePKCEChallenge:
|
|
||||||
return tableOAuth2PKCERequestSession
|
|
||||||
case OAuth2SessionTypeOpenIDConnect:
|
case OAuth2SessionTypeOpenIDConnect:
|
||||||
return tableOAuth2OpenIDConnectSession
|
return tableOAuth2OpenIDConnectSession
|
||||||
|
case OAuth2SessionTypePAR:
|
||||||
|
return tableOAuth2PARContext
|
||||||
|
case OAuth2SessionTypePKCEChallenge:
|
||||||
|
return tableOAuth2PKCERequestSession
|
||||||
|
case OAuth2SessionTypeRefreshToken:
|
||||||
|
return tableOAuth2RefreshTokenSession
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -119,7 +125,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
reMigration = regexp.MustCompile(`^V(\d{4})\.([^.]+)\.(all|sqlite|postgres|mysql)\.(up|down)\.sql$`)
|
reMigration = regexp.MustCompile(`^V(?P<Version>\d{4})\.(?P<Name>[^.]+)\.(?P<Provider>(all|sqlite|postgres|mysql))\.(?P<Direction>(up|down))\.sql$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -130,15 +130,15 @@ func skipMigration(providerName string, up bool, target, prior int, migration *m
|
||||||
}
|
}
|
||||||
|
|
||||||
func scanMigration(m string) (migration model.SchemaMigration, err error) {
|
func scanMigration(m string) (migration model.SchemaMigration, err error) {
|
||||||
result := reMigration.FindStringSubmatch(m)
|
if !reMigration.MatchString(m) {
|
||||||
|
|
||||||
if result == nil || len(result) != 5 {
|
|
||||||
return model.SchemaMigration{}, errors.New("invalid migration: could not parse the format")
|
return model.SchemaMigration{}, errors.New("invalid migration: could not parse the format")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result := reMigration.FindStringSubmatch(m)
|
||||||
|
|
||||||
migration = model.SchemaMigration{
|
migration = model.SchemaMigration{
|
||||||
Name: strings.ReplaceAll(result[2], "_", " "),
|
Name: strings.ReplaceAll(result[reMigration.SubexpIndex("Name")], "_", " "),
|
||||||
Provider: result[3],
|
Provider: result[reMigration.SubexpIndex("Provider")],
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := migrationsFS.ReadFile(fmt.Sprintf("migrations/%s", m))
|
data, err := migrationsFS.ReadFile(fmt.Sprintf("migrations/%s", m))
|
||||||
|
@ -148,22 +148,22 @@ func scanMigration(m string) (migration model.SchemaMigration, err error) {
|
||||||
|
|
||||||
migration.Query = string(data)
|
migration.Query = string(data)
|
||||||
|
|
||||||
switch result[4] {
|
switch direction := result[reMigration.SubexpIndex("Direction")]; direction {
|
||||||
case "up":
|
case "up":
|
||||||
migration.Up = true
|
migration.Up = true
|
||||||
case "down":
|
case "down":
|
||||||
migration.Up = false
|
migration.Up = false
|
||||||
default:
|
default:
|
||||||
return model.SchemaMigration{}, fmt.Errorf("invalid migration: value in position 4 '%s' must be up or down", result[4])
|
return model.SchemaMigration{}, fmt.Errorf("invalid migration: value in Direction group '%s' must be up or down", direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
migration.Version, _ = strconv.Atoi(result[1])
|
migration.Version, _ = strconv.Atoi(result[reMigration.SubexpIndex("Version")])
|
||||||
|
|
||||||
switch migration.Provider {
|
switch migration.Provider {
|
||||||
case providerAll, providerSQLite, providerMySQL, providerPostgres:
|
case providerAll, providerSQLite, providerMySQL, providerPostgres:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return model.SchemaMigration{}, fmt.Errorf("invalid migration: value in position 3 '%s' must be all, sqlite, postgres, or mysql", result[3])
|
return model.SchemaMigration{}, fmt.Errorf("invalid migration: value in Provider group '%s' must be all, sqlite, postgres, or mysql", migration.Provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
return migration, nil
|
return migration, nil
|
||||||
|
|
|
@ -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);
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// This is the latest schema version for the purpose of tests.
|
// This is the latest schema version for the purpose of tests.
|
||||||
LatestVersion = 7
|
LatestVersion = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestShouldObtainCorrectUpMigrations(t *testing.T) {
|
func TestShouldObtainCorrectUpMigrations(t *testing.T) {
|
||||||
|
|
|
@ -24,8 +24,8 @@ type Provider interface {
|
||||||
LoadUserInfo(ctx context.Context, username string) (info model.UserInfo, err error)
|
LoadUserInfo(ctx context.Context, username string) (info model.UserInfo, err error)
|
||||||
|
|
||||||
SaveUserOpaqueIdentifier(ctx context.Context, subject model.UserOpaqueIdentifier) (err error)
|
SaveUserOpaqueIdentifier(ctx context.Context, subject model.UserOpaqueIdentifier) (err error)
|
||||||
LoadUserOpaqueIdentifier(ctx context.Context, opaqueUUID uuid.UUID) (subject *model.UserOpaqueIdentifier, err error)
|
LoadUserOpaqueIdentifier(ctx context.Context, identifier uuid.UUID) (subject *model.UserOpaqueIdentifier, err error)
|
||||||
LoadUserOpaqueIdentifiers(ctx context.Context) (opaqueIDs []model.UserOpaqueIdentifier, err error)
|
LoadUserOpaqueIdentifiers(ctx context.Context) (identifiers []model.UserOpaqueIdentifier, err error)
|
||||||
LoadUserOpaqueIdentifierBySignature(ctx context.Context, service, sectorID, username string) (subject *model.UserOpaqueIdentifier, err error)
|
LoadUserOpaqueIdentifierBySignature(ctx context.Context, service, sectorID, username string) (subject *model.UserOpaqueIdentifier, err error)
|
||||||
|
|
||||||
SaveIdentityVerification(ctx context.Context, verification model.IdentityVerification) (err error)
|
SaveIdentityVerification(ctx context.Context, verification model.IdentityVerification) (err error)
|
||||||
|
@ -65,6 +65,10 @@ type Provider interface {
|
||||||
DeactivateOAuth2SessionByRequestID(ctx context.Context, sessionType OAuth2SessionType, requestID string) (err error)
|
DeactivateOAuth2SessionByRequestID(ctx context.Context, sessionType OAuth2SessionType, requestID string) (err error)
|
||||||
LoadOAuth2Session(ctx context.Context, sessionType OAuth2SessionType, signature string) (session *model.OAuth2Session, err error)
|
LoadOAuth2Session(ctx context.Context, sessionType OAuth2SessionType, signature string) (session *model.OAuth2Session, err error)
|
||||||
|
|
||||||
|
SaveOAuth2PARContext(ctx context.Context, par model.OAuth2PARContext) (err error)
|
||||||
|
LoadOAuth2PARContext(ctx context.Context, signature string) (par *model.OAuth2PARContext, err error)
|
||||||
|
RevokeOAuth2PARContext(ctx context.Context, signature string) (err error)
|
||||||
|
|
||||||
SaveOAuth2BlacklistedJTI(ctx context.Context, blacklistedJTI model.OAuth2BlacklistedJTI) (err error)
|
SaveOAuth2BlacklistedJTI(ctx context.Context, blacklistedJTI model.OAuth2BlacklistedJTI) (err error)
|
||||||
LoadOAuth2BlacklistedJTI(ctx context.Context, signature string) (blacklistedJTI *model.OAuth2BlacklistedJTI, err error)
|
LoadOAuth2BlacklistedJTI(ctx context.Context, signature string) (blacklistedJTI *model.OAuth2BlacklistedJTI, err error)
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,13 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
|
||||||
sqlSelectUserOpaqueIdentifiers: fmt.Sprintf(queryFmtSelectUserOpaqueIdentifiers, tableUserOpaqueIdentifier),
|
sqlSelectUserOpaqueIdentifiers: fmt.Sprintf(queryFmtSelectUserOpaqueIdentifiers, tableUserOpaqueIdentifier),
|
||||||
sqlSelectUserOpaqueIdentifierBySignature: fmt.Sprintf(queryFmtSelectUserOpaqueIdentifierBySignature, tableUserOpaqueIdentifier),
|
sqlSelectUserOpaqueIdentifierBySignature: fmt.Sprintf(queryFmtSelectUserOpaqueIdentifierBySignature, tableUserOpaqueIdentifier),
|
||||||
|
|
||||||
|
sqlUpsertOAuth2BlacklistedJTI: fmt.Sprintf(queryFmtUpsertOAuth2BlacklistedJTI, tableOAuth2BlacklistedJTI),
|
||||||
|
sqlSelectOAuth2BlacklistedJTI: fmt.Sprintf(queryFmtSelectOAuth2BlacklistedJTI, tableOAuth2BlacklistedJTI),
|
||||||
|
|
||||||
|
sqlInsertOAuth2PARContext: fmt.Sprintf(queryFmtInsertOAuth2PARContext, tableOAuth2PARContext),
|
||||||
|
sqlSelectOAuth2PARContext: fmt.Sprintf(queryFmtSelectOAuth2PARContext, tableOAuth2PARContext),
|
||||||
|
sqlRevokeOAuth2PARContext: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2PARContext),
|
||||||
|
|
||||||
sqlInsertOAuth2ConsentPreConfiguration: fmt.Sprintf(queryFmtInsertOAuth2ConsentPreConfiguration, tableOAuth2ConsentPreConfiguration),
|
sqlInsertOAuth2ConsentPreConfiguration: fmt.Sprintf(queryFmtInsertOAuth2ConsentPreConfiguration, tableOAuth2ConsentPreConfiguration),
|
||||||
sqlSelectOAuth2ConsentPreConfigurations: fmt.Sprintf(queryFmtSelectOAuth2ConsentPreConfigurations, tableOAuth2ConsentPreConfiguration),
|
sqlSelectOAuth2ConsentPreConfigurations: fmt.Sprintf(queryFmtSelectOAuth2ConsentPreConfigurations, tableOAuth2ConsentPreConfiguration),
|
||||||
|
|
||||||
|
@ -79,13 +86,6 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
|
||||||
sqlUpdateOAuth2ConsentSessionGranted: fmt.Sprintf(queryFmtUpdateOAuth2ConsentSessionGranted, tableOAuth2ConsentSession),
|
sqlUpdateOAuth2ConsentSessionGranted: fmt.Sprintf(queryFmtUpdateOAuth2ConsentSessionGranted, tableOAuth2ConsentSession),
|
||||||
sqlSelectOAuth2ConsentSessionByChallengeID: fmt.Sprintf(queryFmtSelectOAuth2ConsentSessionByChallengeID, tableOAuth2ConsentSession),
|
sqlSelectOAuth2ConsentSessionByChallengeID: fmt.Sprintf(queryFmtSelectOAuth2ConsentSessionByChallengeID, tableOAuth2ConsentSession),
|
||||||
|
|
||||||
sqlInsertOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2AuthorizeCodeSession),
|
|
||||||
sqlSelectOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2AuthorizeCodeSession),
|
|
||||||
sqlRevokeOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2AuthorizeCodeSession),
|
|
||||||
sqlRevokeOAuth2AuthorizeCodeSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2AuthorizeCodeSession),
|
|
||||||
sqlDeactivateOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2AuthorizeCodeSession),
|
|
||||||
sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2AuthorizeCodeSession),
|
|
||||||
|
|
||||||
sqlInsertOAuth2AccessTokenSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2AccessTokenSession),
|
sqlInsertOAuth2AccessTokenSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2AccessTokenSession),
|
||||||
sqlSelectOAuth2AccessTokenSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2AccessTokenSession),
|
sqlSelectOAuth2AccessTokenSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2AccessTokenSession),
|
||||||
sqlRevokeOAuth2AccessTokenSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2AccessTokenSession),
|
sqlRevokeOAuth2AccessTokenSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2AccessTokenSession),
|
||||||
|
@ -93,19 +93,12 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
|
||||||
sqlDeactivateOAuth2AccessTokenSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2AccessTokenSession),
|
sqlDeactivateOAuth2AccessTokenSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2AccessTokenSession),
|
||||||
sqlDeactivateOAuth2AccessTokenSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2AccessTokenSession),
|
sqlDeactivateOAuth2AccessTokenSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2AccessTokenSession),
|
||||||
|
|
||||||
sqlInsertOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2RefreshTokenSession),
|
sqlInsertOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2AuthorizeCodeSession),
|
||||||
sqlSelectOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2RefreshTokenSession),
|
sqlSelectOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2AuthorizeCodeSession),
|
||||||
sqlRevokeOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2RefreshTokenSession),
|
sqlRevokeOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2AuthorizeCodeSession),
|
||||||
sqlRevokeOAuth2RefreshTokenSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2RefreshTokenSession),
|
sqlRevokeOAuth2AuthorizeCodeSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2AuthorizeCodeSession),
|
||||||
sqlDeactivateOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2RefreshTokenSession),
|
sqlDeactivateOAuth2AuthorizeCodeSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2AuthorizeCodeSession),
|
||||||
sqlDeactivateOAuth2RefreshTokenSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2RefreshTokenSession),
|
sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2AuthorizeCodeSession),
|
||||||
|
|
||||||
sqlInsertOAuth2PKCERequestSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2PKCERequestSession),
|
|
||||||
sqlSelectOAuth2PKCERequestSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2PKCERequestSession),
|
|
||||||
sqlRevokeOAuth2PKCERequestSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2PKCERequestSession),
|
|
||||||
sqlRevokeOAuth2PKCERequestSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2PKCERequestSession),
|
|
||||||
sqlDeactivateOAuth2PKCERequestSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2PKCERequestSession),
|
|
||||||
sqlDeactivateOAuth2PKCERequestSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2PKCERequestSession),
|
|
||||||
|
|
||||||
sqlInsertOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2OpenIDConnectSession),
|
sqlInsertOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2OpenIDConnectSession),
|
||||||
sqlSelectOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2OpenIDConnectSession),
|
sqlSelectOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2OpenIDConnectSession),
|
||||||
|
@ -114,8 +107,19 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
|
||||||
sqlDeactivateOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2OpenIDConnectSession),
|
sqlDeactivateOAuth2OpenIDConnectSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2OpenIDConnectSession),
|
||||||
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2OpenIDConnectSession),
|
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2OpenIDConnectSession),
|
||||||
|
|
||||||
sqlUpsertOAuth2BlacklistedJTI: fmt.Sprintf(queryFmtUpsertOAuth2BlacklistedJTI, tableOAuth2BlacklistedJTI),
|
sqlInsertOAuth2PKCERequestSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2PKCERequestSession),
|
||||||
sqlSelectOAuth2BlacklistedJTI: fmt.Sprintf(queryFmtSelectOAuth2BlacklistedJTI, tableOAuth2BlacklistedJTI),
|
sqlSelectOAuth2PKCERequestSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2PKCERequestSession),
|
||||||
|
sqlRevokeOAuth2PKCERequestSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2PKCERequestSession),
|
||||||
|
sqlRevokeOAuth2PKCERequestSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2PKCERequestSession),
|
||||||
|
sqlDeactivateOAuth2PKCERequestSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2PKCERequestSession),
|
||||||
|
sqlDeactivateOAuth2PKCERequestSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2PKCERequestSession),
|
||||||
|
|
||||||
|
sqlInsertOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtInsertOAuth2Session, tableOAuth2RefreshTokenSession),
|
||||||
|
sqlSelectOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtSelectOAuth2Session, tableOAuth2RefreshTokenSession),
|
||||||
|
sqlRevokeOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtRevokeOAuth2Session, tableOAuth2RefreshTokenSession),
|
||||||
|
sqlRevokeOAuth2RefreshTokenSessionByRequestID: fmt.Sprintf(queryFmtRevokeOAuth2SessionByRequestID, tableOAuth2RefreshTokenSession),
|
||||||
|
sqlDeactivateOAuth2RefreshTokenSession: fmt.Sprintf(queryFmtDeactivateOAuth2Session, tableOAuth2RefreshTokenSession),
|
||||||
|
sqlDeactivateOAuth2RefreshTokenSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2RefreshTokenSession),
|
||||||
|
|
||||||
sqlInsertMigration: fmt.Sprintf(queryFmtInsertMigration, tableMigrations),
|
sqlInsertMigration: fmt.Sprintf(queryFmtInsertMigration, tableMigrations),
|
||||||
sqlSelectMigrations: fmt.Sprintf(queryFmtSelectMigrations, tableMigrations),
|
sqlSelectMigrations: fmt.Sprintf(queryFmtSelectMigrations, tableMigrations),
|
||||||
|
@ -224,13 +228,18 @@ type SQLProvider struct {
|
||||||
sqlDeactivateOAuth2AccessTokenSession string
|
sqlDeactivateOAuth2AccessTokenSession string
|
||||||
sqlDeactivateOAuth2AccessTokenSessionByRequestID string
|
sqlDeactivateOAuth2AccessTokenSessionByRequestID string
|
||||||
|
|
||||||
// Table: oauth2_refresh_token_session.
|
// Table: oauth2_openid_connect_session.
|
||||||
sqlInsertOAuth2RefreshTokenSession string
|
sqlInsertOAuth2OpenIDConnectSession string
|
||||||
sqlSelectOAuth2RefreshTokenSession string
|
sqlSelectOAuth2OpenIDConnectSession string
|
||||||
sqlRevokeOAuth2RefreshTokenSession string
|
sqlRevokeOAuth2OpenIDConnectSession string
|
||||||
sqlRevokeOAuth2RefreshTokenSessionByRequestID string
|
sqlRevokeOAuth2OpenIDConnectSessionByRequestID string
|
||||||
sqlDeactivateOAuth2RefreshTokenSession string
|
sqlDeactivateOAuth2OpenIDConnectSession string
|
||||||
sqlDeactivateOAuth2RefreshTokenSessionByRequestID string
|
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID string
|
||||||
|
|
||||||
|
// Table: oauth2_par_context.
|
||||||
|
sqlInsertOAuth2PARContext string
|
||||||
|
sqlSelectOAuth2PARContext string
|
||||||
|
sqlRevokeOAuth2PARContext string
|
||||||
|
|
||||||
// Table: oauth2_pkce_request_session.
|
// Table: oauth2_pkce_request_session.
|
||||||
sqlInsertOAuth2PKCERequestSession string
|
sqlInsertOAuth2PKCERequestSession string
|
||||||
|
@ -240,13 +249,13 @@ type SQLProvider struct {
|
||||||
sqlDeactivateOAuth2PKCERequestSession string
|
sqlDeactivateOAuth2PKCERequestSession string
|
||||||
sqlDeactivateOAuth2PKCERequestSessionByRequestID string
|
sqlDeactivateOAuth2PKCERequestSessionByRequestID string
|
||||||
|
|
||||||
// Table: oauth2_openid_connect_session.
|
// Table: oauth2_refresh_token_session.
|
||||||
sqlInsertOAuth2OpenIDConnectSession string
|
sqlInsertOAuth2RefreshTokenSession string
|
||||||
sqlSelectOAuth2OpenIDConnectSession string
|
sqlSelectOAuth2RefreshTokenSession string
|
||||||
sqlRevokeOAuth2OpenIDConnectSession string
|
sqlRevokeOAuth2RefreshTokenSession string
|
||||||
sqlRevokeOAuth2OpenIDConnectSessionByRequestID string
|
sqlRevokeOAuth2RefreshTokenSessionByRequestID string
|
||||||
sqlDeactivateOAuth2OpenIDConnectSession string
|
sqlDeactivateOAuth2RefreshTokenSession string
|
||||||
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID string
|
sqlDeactivateOAuth2RefreshTokenSessionByRequestID string
|
||||||
|
|
||||||
sqlUpsertOAuth2BlacklistedJTI string
|
sqlUpsertOAuth2BlacklistedJTI string
|
||||||
sqlSelectOAuth2BlacklistedJTI string
|
sqlSelectOAuth2BlacklistedJTI string
|
||||||
|
@ -339,19 +348,19 @@ func (p *SQLProvider) Rollback(ctx context.Context) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveUserOpaqueIdentifier saves a new opaque user identifier to the database.
|
// SaveUserOpaqueIdentifier saves a new opaque user identifier to the database.
|
||||||
func (p *SQLProvider) SaveUserOpaqueIdentifier(ctx context.Context, opaqueID model.UserOpaqueIdentifier) (err error) {
|
func (p *SQLProvider) SaveUserOpaqueIdentifier(ctx context.Context, subject model.UserOpaqueIdentifier) (err error) {
|
||||||
if _, err = p.db.ExecContext(ctx, p.sqlInsertUserOpaqueIdentifier, opaqueID.Service, opaqueID.SectorID, opaqueID.Username, opaqueID.Identifier); err != nil {
|
if _, err = p.db.ExecContext(ctx, p.sqlInsertUserOpaqueIdentifier, subject.Service, subject.SectorID, subject.Username, subject.Identifier); err != nil {
|
||||||
return fmt.Errorf("error inserting user opaque id for user '%s' with opaque id '%s': %w", opaqueID.Username, opaqueID.Identifier.String(), err)
|
return fmt.Errorf("error inserting user opaque id for user '%s' with opaque id '%s': %w", subject.Username, subject.Identifier.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadUserOpaqueIdentifier selects an opaque user identifier from the database.
|
// LoadUserOpaqueIdentifier selects an opaque user identifier from the database.
|
||||||
func (p *SQLProvider) LoadUserOpaqueIdentifier(ctx context.Context, opaqueUUID uuid.UUID) (opaqueID *model.UserOpaqueIdentifier, err error) {
|
func (p *SQLProvider) LoadUserOpaqueIdentifier(ctx context.Context, identifier uuid.UUID) (subject *model.UserOpaqueIdentifier, err error) {
|
||||||
opaqueID = &model.UserOpaqueIdentifier{}
|
subject = &model.UserOpaqueIdentifier{}
|
||||||
|
|
||||||
if err = p.db.GetContext(ctx, opaqueID, p.sqlSelectUserOpaqueIdentifier, opaqueUUID); err != nil {
|
if err = p.db.GetContext(ctx, subject, p.sqlSelectUserOpaqueIdentifier, identifier); err != nil {
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
case errors.Is(err, sql.ErrNoRows):
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -360,11 +369,11 @@ func (p *SQLProvider) LoadUserOpaqueIdentifier(ctx context.Context, opaqueUUID u
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return opaqueID, nil
|
return subject, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadUserOpaqueIdentifiers selects an opaque user identifiers from the database.
|
// LoadUserOpaqueIdentifiers selects an opaque user identifiers from the database.
|
||||||
func (p *SQLProvider) LoadUserOpaqueIdentifiers(ctx context.Context) (opaqueIDs []model.UserOpaqueIdentifier, err error) {
|
func (p *SQLProvider) LoadUserOpaqueIdentifiers(ctx context.Context) (identifiers []model.UserOpaqueIdentifier, err error) {
|
||||||
var rows *sqlx.Rows
|
var rows *sqlx.Rows
|
||||||
|
|
||||||
if rows, err = p.db.QueryxContext(ctx, p.sqlSelectUserOpaqueIdentifiers); err != nil {
|
if rows, err = p.db.QueryxContext(ctx, p.sqlSelectUserOpaqueIdentifiers); err != nil {
|
||||||
|
@ -380,17 +389,17 @@ func (p *SQLProvider) LoadUserOpaqueIdentifiers(ctx context.Context) (opaqueIDs
|
||||||
return nil, fmt.Errorf("error selecting user opaque identifiers: error scanning row: %w", err)
|
return nil, fmt.Errorf("error selecting user opaque identifiers: error scanning row: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opaqueIDs = append(opaqueIDs, *opaqueID)
|
identifiers = append(identifiers, *opaqueID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return opaqueIDs, nil
|
return identifiers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadUserOpaqueIdentifierBySignature selects an opaque user identifier from the database given a service name, sector id, and username.
|
// LoadUserOpaqueIdentifierBySignature selects an opaque user identifier from the database given a service name, sector id, and username.
|
||||||
func (p *SQLProvider) LoadUserOpaqueIdentifierBySignature(ctx context.Context, service, sectorID, username string) (opaqueID *model.UserOpaqueIdentifier, err error) {
|
func (p *SQLProvider) LoadUserOpaqueIdentifierBySignature(ctx context.Context, service, sectorID, username string) (subject *model.UserOpaqueIdentifier, err error) {
|
||||||
opaqueID = &model.UserOpaqueIdentifier{}
|
subject = &model.UserOpaqueIdentifier{}
|
||||||
|
|
||||||
if err = p.db.GetContext(ctx, opaqueID, p.sqlSelectUserOpaqueIdentifierBySignature, service, sectorID, username); err != nil {
|
if err = p.db.GetContext(ctx, subject, p.sqlSelectUserOpaqueIdentifierBySignature, service, sectorID, username); err != nil {
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
case errors.Is(err, sql.ErrNoRows):
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -399,7 +408,7 @@ func (p *SQLProvider) LoadUserOpaqueIdentifierBySignature(ctx context.Context, s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return opaqueID, nil
|
return subject, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveOAuth2ConsentSession inserts an OAuth2.0 consent session.
|
// SaveOAuth2ConsentSession inserts an OAuth2.0 consent session.
|
||||||
|
@ -496,22 +505,22 @@ func (p *SQLProvider) SaveOAuth2Session(ctx context.Context, sessionType OAuth2S
|
||||||
var query string
|
var query string
|
||||||
|
|
||||||
switch sessionType {
|
switch sessionType {
|
||||||
case OAuth2SessionTypeAuthorizeCode:
|
|
||||||
query = p.sqlInsertOAuth2AuthorizeCodeSession
|
|
||||||
case OAuth2SessionTypeAccessToken:
|
case OAuth2SessionTypeAccessToken:
|
||||||
query = p.sqlInsertOAuth2AccessTokenSession
|
query = p.sqlInsertOAuth2AccessTokenSession
|
||||||
case OAuth2SessionTypeRefreshToken:
|
case OAuth2SessionTypeAuthorizeCode:
|
||||||
query = p.sqlInsertOAuth2RefreshTokenSession
|
query = p.sqlInsertOAuth2AuthorizeCodeSession
|
||||||
case OAuth2SessionTypePKCEChallenge:
|
|
||||||
query = p.sqlInsertOAuth2PKCERequestSession
|
|
||||||
case OAuth2SessionTypeOpenIDConnect:
|
case OAuth2SessionTypeOpenIDConnect:
|
||||||
query = p.sqlInsertOAuth2OpenIDConnectSession
|
query = p.sqlInsertOAuth2OpenIDConnectSession
|
||||||
|
case OAuth2SessionTypePKCEChallenge:
|
||||||
|
query = p.sqlInsertOAuth2PKCERequestSession
|
||||||
|
case OAuth2SessionTypeRefreshToken:
|
||||||
|
query = p.sqlInsertOAuth2RefreshTokenSession
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("error inserting oauth2 session for subject '%s' and request id '%s': unknown oauth2 session type '%s'", session.Subject, session.RequestID, sessionType)
|
return fmt.Errorf("error inserting oauth2 session for subject '%s' and request id '%s': unknown oauth2 session type '%s'", session.Subject, session.RequestID, sessionType)
|
||||||
}
|
}
|
||||||
|
|
||||||
if session.Session, err = p.encrypt(session.Session); err != nil {
|
if session.Session, err = p.encrypt(session.Session); err != nil {
|
||||||
return fmt.Errorf("error encrypting the oauth2 %s session data for subject '%s' and request id '%s' and challenge id '%s': %w", sessionType, session.Subject, session.RequestID, session.ChallengeID.String(), err)
|
return fmt.Errorf("error encrypting oauth2 %s session data for subject '%s' and request id '%s' and challenge id '%s': %w", sessionType, session.Subject, session.RequestID, session.ChallengeID.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = p.db.ExecContext(ctx, query,
|
_, err = p.db.ExecContext(ctx, query,
|
||||||
|
@ -532,16 +541,16 @@ func (p *SQLProvider) RevokeOAuth2Session(ctx context.Context, sessionType OAuth
|
||||||
var query string
|
var query string
|
||||||
|
|
||||||
switch sessionType {
|
switch sessionType {
|
||||||
case OAuth2SessionTypeAuthorizeCode:
|
|
||||||
query = p.sqlRevokeOAuth2AuthorizeCodeSession
|
|
||||||
case OAuth2SessionTypeAccessToken:
|
case OAuth2SessionTypeAccessToken:
|
||||||
query = p.sqlRevokeOAuth2AccessTokenSession
|
query = p.sqlRevokeOAuth2AccessTokenSession
|
||||||
case OAuth2SessionTypeRefreshToken:
|
case OAuth2SessionTypeAuthorizeCode:
|
||||||
query = p.sqlRevokeOAuth2RefreshTokenSession
|
query = p.sqlRevokeOAuth2AuthorizeCodeSession
|
||||||
case OAuth2SessionTypePKCEChallenge:
|
|
||||||
query = p.sqlRevokeOAuth2PKCERequestSession
|
|
||||||
case OAuth2SessionTypeOpenIDConnect:
|
case OAuth2SessionTypeOpenIDConnect:
|
||||||
query = p.sqlRevokeOAuth2OpenIDConnectSession
|
query = p.sqlRevokeOAuth2OpenIDConnectSession
|
||||||
|
case OAuth2SessionTypePKCEChallenge:
|
||||||
|
query = p.sqlRevokeOAuth2PKCERequestSession
|
||||||
|
case OAuth2SessionTypeRefreshToken:
|
||||||
|
query = p.sqlRevokeOAuth2RefreshTokenSession
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("error revoking oauth2 session with signature '%s': unknown oauth2 session type '%s'", signature, sessionType.String())
|
return fmt.Errorf("error revoking oauth2 session with signature '%s': unknown oauth2 session type '%s'", signature, sessionType.String())
|
||||||
}
|
}
|
||||||
|
@ -558,16 +567,16 @@ func (p *SQLProvider) RevokeOAuth2SessionByRequestID(ctx context.Context, sessio
|
||||||
var query string
|
var query string
|
||||||
|
|
||||||
switch sessionType {
|
switch sessionType {
|
||||||
case OAuth2SessionTypeAuthorizeCode:
|
|
||||||
query = p.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID
|
|
||||||
case OAuth2SessionTypeAccessToken:
|
case OAuth2SessionTypeAccessToken:
|
||||||
query = p.sqlRevokeOAuth2AccessTokenSessionByRequestID
|
query = p.sqlRevokeOAuth2AccessTokenSessionByRequestID
|
||||||
case OAuth2SessionTypeRefreshToken:
|
case OAuth2SessionTypeAuthorizeCode:
|
||||||
query = p.sqlRevokeOAuth2RefreshTokenSessionByRequestID
|
query = p.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID
|
||||||
case OAuth2SessionTypePKCEChallenge:
|
|
||||||
query = p.sqlRevokeOAuth2PKCERequestSessionByRequestID
|
|
||||||
case OAuth2SessionTypeOpenIDConnect:
|
case OAuth2SessionTypeOpenIDConnect:
|
||||||
query = p.sqlRevokeOAuth2OpenIDConnectSessionByRequestID
|
query = p.sqlRevokeOAuth2OpenIDConnectSessionByRequestID
|
||||||
|
case OAuth2SessionTypePKCEChallenge:
|
||||||
|
query = p.sqlRevokeOAuth2PKCERequestSessionByRequestID
|
||||||
|
case OAuth2SessionTypeRefreshToken:
|
||||||
|
query = p.sqlRevokeOAuth2RefreshTokenSessionByRequestID
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("error revoking oauth2 session with request id '%s': unknown oauth2 session type '%s'", requestID, sessionType.String())
|
return fmt.Errorf("error revoking oauth2 session with request id '%s': unknown oauth2 session type '%s'", requestID, sessionType.String())
|
||||||
}
|
}
|
||||||
|
@ -584,16 +593,16 @@ func (p *SQLProvider) DeactivateOAuth2Session(ctx context.Context, sessionType O
|
||||||
var query string
|
var query string
|
||||||
|
|
||||||
switch sessionType {
|
switch sessionType {
|
||||||
case OAuth2SessionTypeAuthorizeCode:
|
|
||||||
query = p.sqlDeactivateOAuth2AuthorizeCodeSession
|
|
||||||
case OAuth2SessionTypeAccessToken:
|
case OAuth2SessionTypeAccessToken:
|
||||||
query = p.sqlDeactivateOAuth2AccessTokenSession
|
query = p.sqlDeactivateOAuth2AccessTokenSession
|
||||||
case OAuth2SessionTypeRefreshToken:
|
case OAuth2SessionTypeAuthorizeCode:
|
||||||
query = p.sqlDeactivateOAuth2RefreshTokenSession
|
query = p.sqlDeactivateOAuth2AuthorizeCodeSession
|
||||||
case OAuth2SessionTypePKCEChallenge:
|
|
||||||
query = p.sqlDeactivateOAuth2PKCERequestSession
|
|
||||||
case OAuth2SessionTypeOpenIDConnect:
|
case OAuth2SessionTypeOpenIDConnect:
|
||||||
query = p.sqlDeactivateOAuth2OpenIDConnectSession
|
query = p.sqlDeactivateOAuth2OpenIDConnectSession
|
||||||
|
case OAuth2SessionTypePKCEChallenge:
|
||||||
|
query = p.sqlDeactivateOAuth2PKCERequestSession
|
||||||
|
case OAuth2SessionTypeRefreshToken:
|
||||||
|
query = p.sqlDeactivateOAuth2RefreshTokenSession
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("error deactivating oauth2 session with signature '%s': unknown oauth2 session type '%s'", signature, sessionType.String())
|
return fmt.Errorf("error deactivating oauth2 session with signature '%s': unknown oauth2 session type '%s'", signature, sessionType.String())
|
||||||
}
|
}
|
||||||
|
@ -610,16 +619,16 @@ func (p *SQLProvider) DeactivateOAuth2SessionByRequestID(ctx context.Context, se
|
||||||
var query string
|
var query string
|
||||||
|
|
||||||
switch sessionType {
|
switch sessionType {
|
||||||
case OAuth2SessionTypeAuthorizeCode:
|
|
||||||
query = p.sqlDeactivateOAuth2AuthorizeCodeSession
|
|
||||||
case OAuth2SessionTypeAccessToken:
|
case OAuth2SessionTypeAccessToken:
|
||||||
query = p.sqlDeactivateOAuth2AccessTokenSessionByRequestID
|
query = p.sqlDeactivateOAuth2AccessTokenSessionByRequestID
|
||||||
case OAuth2SessionTypeRefreshToken:
|
case OAuth2SessionTypeAuthorizeCode:
|
||||||
query = p.sqlDeactivateOAuth2RefreshTokenSessionByRequestID
|
query = p.sqlDeactivateOAuth2AuthorizeCodeSession
|
||||||
case OAuth2SessionTypePKCEChallenge:
|
|
||||||
query = p.sqlDeactivateOAuth2PKCERequestSessionByRequestID
|
|
||||||
case OAuth2SessionTypeOpenIDConnect:
|
case OAuth2SessionTypeOpenIDConnect:
|
||||||
query = p.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID
|
query = p.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID
|
||||||
|
case OAuth2SessionTypePKCEChallenge:
|
||||||
|
query = p.sqlDeactivateOAuth2PKCERequestSessionByRequestID
|
||||||
|
case OAuth2SessionTypeRefreshToken:
|
||||||
|
query = p.sqlDeactivateOAuth2RefreshTokenSessionByRequestID
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("error deactivating oauth2 session with request id '%s': unknown oauth2 session type '%s'", requestID, sessionType.String())
|
return fmt.Errorf("error deactivating oauth2 session with request id '%s': unknown oauth2 session type '%s'", requestID, sessionType.String())
|
||||||
}
|
}
|
||||||
|
@ -636,16 +645,16 @@ func (p *SQLProvider) LoadOAuth2Session(ctx context.Context, sessionType OAuth2S
|
||||||
var query string
|
var query string
|
||||||
|
|
||||||
switch sessionType {
|
switch sessionType {
|
||||||
case OAuth2SessionTypeAuthorizeCode:
|
|
||||||
query = p.sqlSelectOAuth2AuthorizeCodeSession
|
|
||||||
case OAuth2SessionTypeAccessToken:
|
case OAuth2SessionTypeAccessToken:
|
||||||
query = p.sqlSelectOAuth2AccessTokenSession
|
query = p.sqlSelectOAuth2AccessTokenSession
|
||||||
case OAuth2SessionTypeRefreshToken:
|
case OAuth2SessionTypeAuthorizeCode:
|
||||||
query = p.sqlSelectOAuth2RefreshTokenSession
|
query = p.sqlSelectOAuth2AuthorizeCodeSession
|
||||||
case OAuth2SessionTypePKCEChallenge:
|
|
||||||
query = p.sqlSelectOAuth2PKCERequestSession
|
|
||||||
case OAuth2SessionTypeOpenIDConnect:
|
case OAuth2SessionTypeOpenIDConnect:
|
||||||
query = p.sqlSelectOAuth2OpenIDConnectSession
|
query = p.sqlSelectOAuth2OpenIDConnectSession
|
||||||
|
case OAuth2SessionTypePKCEChallenge:
|
||||||
|
query = p.sqlSelectOAuth2PKCERequestSession
|
||||||
|
case OAuth2SessionTypeRefreshToken:
|
||||||
|
query = p.sqlSelectOAuth2RefreshTokenSession
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("error selecting oauth2 session: unknown oauth2 session type '%s'", sessionType.String())
|
return nil, fmt.Errorf("error selecting oauth2 session: unknown oauth2 session type '%s'", sessionType.String())
|
||||||
}
|
}
|
||||||
|
@ -663,6 +672,45 @@ func (p *SQLProvider) LoadOAuth2Session(ctx context.Context, sessionType OAuth2S
|
||||||
return session, nil
|
return session, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveOAuth2PARContext save a OAuth2PARContext to the database.
|
||||||
|
func (p *SQLProvider) SaveOAuth2PARContext(ctx context.Context, par model.OAuth2PARContext) (err error) {
|
||||||
|
if par.Session, err = p.encrypt(par.Session); err != nil {
|
||||||
|
return fmt.Errorf("error encrypting oauth2 pushed authorization request context data for with signature '%s' and request id '%s': %w", par.Signature, par.RequestID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = p.db.ExecContext(ctx, p.sqlInsertOAuth2PARContext,
|
||||||
|
par.Signature, par.RequestID, par.ClientID, par.RequestedAt, par.Scopes, par.Audience, par.HandledResponseTypes,
|
||||||
|
par.ResponseMode, par.DefaultResponseMode, par.Revoked, par.Form, par.Session); err != nil {
|
||||||
|
return fmt.Errorf("error inserting oauth2 pushed authorization request context data for with signature '%s' and request id '%s': %w", par.Signature, par.RequestID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadOAuth2PARContext loads a OAuth2PARContext from the database.
|
||||||
|
func (p *SQLProvider) LoadOAuth2PARContext(ctx context.Context, signature string) (par *model.OAuth2PARContext, err error) {
|
||||||
|
par = &model.OAuth2PARContext{}
|
||||||
|
|
||||||
|
if err = p.db.GetContext(ctx, par, p.sqlSelectOAuth2PARContext, signature); err != nil {
|
||||||
|
return nil, fmt.Errorf("error selecting oauth2 pushed authorization request context with signature '%s': %w", signature, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if par.Session, err = p.decrypt(par.Session); err != nil {
|
||||||
|
return nil, fmt.Errorf("error decrypting oauth2 oauth2 pushed authorization request context data with signature '%s' and request id '%s': %w", signature, par.RequestID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return par, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevokeOAuth2PARContext marks a OAuth2PARContext as revoked in the database.
|
||||||
|
func (p *SQLProvider) RevokeOAuth2PARContext(ctx context.Context, signature string) (err error) {
|
||||||
|
if _, err = p.db.ExecContext(ctx, p.sqlRevokeOAuth2PARContext, signature); err != nil {
|
||||||
|
return fmt.Errorf("error revoking oauth2 pushed authorization request context with signature '%s': %w", signature, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SaveOAuth2BlacklistedJTI saves a OAuth2BlacklistedJTI to the database.
|
// SaveOAuth2BlacklistedJTI saves a OAuth2BlacklistedJTI to the database.
|
||||||
func (p *SQLProvider) SaveOAuth2BlacklistedJTI(ctx context.Context, blacklistedJTI model.OAuth2BlacklistedJTI) (err error) {
|
func (p *SQLProvider) SaveOAuth2BlacklistedJTI(ctx context.Context, blacklistedJTI model.OAuth2BlacklistedJTI) (err error) {
|
||||||
if _, err = p.db.ExecContext(ctx, p.sqlUpsertOAuth2BlacklistedJTI, blacklistedJTI.Signature, blacklistedJTI.ExpiresAt); err != nil {
|
if _, err = p.db.ExecContext(ctx, p.sqlUpsertOAuth2BlacklistedJTI, blacklistedJTI.Signature, blacklistedJTI.ExpiresAt); err != nil {
|
||||||
|
@ -762,7 +810,7 @@ func (p *SQLProvider) FindIdentityVerification(ctx context.Context, jti string)
|
||||||
// SaveTOTPConfiguration save a TOTP configuration of a given user in the database.
|
// SaveTOTPConfiguration save a TOTP configuration of a given user in the database.
|
||||||
func (p *SQLProvider) SaveTOTPConfiguration(ctx context.Context, config model.TOTPConfiguration) (err error) {
|
func (p *SQLProvider) SaveTOTPConfiguration(ctx context.Context, config model.TOTPConfiguration) (err error) {
|
||||||
if config.Secret, err = p.encrypt(config.Secret); err != nil {
|
if config.Secret, err = p.encrypt(config.Secret); err != nil {
|
||||||
return fmt.Errorf("error encrypting the TOTP configuration secret for user '%s': %w", config.Username, err)
|
return fmt.Errorf("error encrypting TOTP configuration secret for user '%s': %w", config.Username, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = p.db.ExecContext(ctx, p.sqlUpsertTOTPConfig,
|
if _, err = p.db.ExecContext(ctx, p.sqlUpsertTOTPConfig,
|
||||||
|
@ -806,7 +854,7 @@ func (p *SQLProvider) LoadTOTPConfiguration(ctx context.Context, username string
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Secret, err = p.decrypt(config.Secret); err != nil {
|
if config.Secret, err = p.decrypt(config.Secret); err != nil {
|
||||||
return nil, fmt.Errorf("error decrypting the TOTP secret for user '%s': %w", username, err)
|
return nil, fmt.Errorf("error decrypting TOTP secret for user '%s': %w", username, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
|
@ -836,7 +884,7 @@ func (p *SQLProvider) LoadTOTPConfigurations(ctx context.Context, limit, page in
|
||||||
// SaveWebauthnDevice saves a registered Webauthn device.
|
// SaveWebauthnDevice saves a registered Webauthn device.
|
||||||
func (p *SQLProvider) SaveWebauthnDevice(ctx context.Context, device model.WebauthnDevice) (err error) {
|
func (p *SQLProvider) SaveWebauthnDevice(ctx context.Context, device model.WebauthnDevice) (err error) {
|
||||||
if device.PublicKey, err = p.encrypt(device.PublicKey); err != nil {
|
if device.PublicKey, err = p.encrypt(device.PublicKey); err != nil {
|
||||||
return fmt.Errorf("error encrypting the Webauthn device public key for user '%s' kid '%x': %w", device.Username, device.KID, err)
|
return fmt.Errorf("error encrypting Webauthn device public key for user '%s' kid '%x': %w", device.Username, device.KID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = p.db.ExecContext(ctx, p.sqlUpsertWebauthnDevice,
|
if _, err = p.db.ExecContext(ctx, p.sqlUpsertWebauthnDevice,
|
||||||
|
|
|
@ -86,13 +86,6 @@ func NewPostgreSQLProvider(config *schema.Configuration, tconfig *tls.Config, gl
|
||||||
provider.sqlUpdateOAuth2ConsentSessionGranted = provider.db.Rebind(provider.sqlUpdateOAuth2ConsentSessionGranted)
|
provider.sqlUpdateOAuth2ConsentSessionGranted = provider.db.Rebind(provider.sqlUpdateOAuth2ConsentSessionGranted)
|
||||||
provider.sqlSelectOAuth2ConsentSessionByChallengeID = provider.db.Rebind(provider.sqlSelectOAuth2ConsentSessionByChallengeID)
|
provider.sqlSelectOAuth2ConsentSessionByChallengeID = provider.db.Rebind(provider.sqlSelectOAuth2ConsentSessionByChallengeID)
|
||||||
|
|
||||||
provider.sqlInsertOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlInsertOAuth2AuthorizeCodeSession)
|
|
||||||
provider.sqlRevokeOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlRevokeOAuth2AuthorizeCodeSession)
|
|
||||||
provider.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID)
|
|
||||||
provider.sqlDeactivateOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlDeactivateOAuth2AuthorizeCodeSession)
|
|
||||||
provider.sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID)
|
|
||||||
provider.sqlSelectOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlSelectOAuth2AuthorizeCodeSession)
|
|
||||||
|
|
||||||
provider.sqlInsertOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlInsertOAuth2AccessTokenSession)
|
provider.sqlInsertOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlInsertOAuth2AccessTokenSession)
|
||||||
provider.sqlRevokeOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlRevokeOAuth2AccessTokenSession)
|
provider.sqlRevokeOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlRevokeOAuth2AccessTokenSession)
|
||||||
provider.sqlRevokeOAuth2AccessTokenSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2AccessTokenSessionByRequestID)
|
provider.sqlRevokeOAuth2AccessTokenSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2AccessTokenSessionByRequestID)
|
||||||
|
@ -100,12 +93,23 @@ func NewPostgreSQLProvider(config *schema.Configuration, tconfig *tls.Config, gl
|
||||||
provider.sqlDeactivateOAuth2AccessTokenSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2AccessTokenSessionByRequestID)
|
provider.sqlDeactivateOAuth2AccessTokenSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2AccessTokenSessionByRequestID)
|
||||||
provider.sqlSelectOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlSelectOAuth2AccessTokenSession)
|
provider.sqlSelectOAuth2AccessTokenSession = provider.db.Rebind(provider.sqlSelectOAuth2AccessTokenSession)
|
||||||
|
|
||||||
provider.sqlInsertOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlInsertOAuth2RefreshTokenSession)
|
provider.sqlInsertOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlInsertOAuth2AuthorizeCodeSession)
|
||||||
provider.sqlRevokeOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSession)
|
provider.sqlRevokeOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlRevokeOAuth2AuthorizeCodeSession)
|
||||||
provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID)
|
provider.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2AuthorizeCodeSessionByRequestID)
|
||||||
provider.sqlDeactivateOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSession)
|
provider.sqlDeactivateOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlDeactivateOAuth2AuthorizeCodeSession)
|
||||||
provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID)
|
provider.sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID)
|
||||||
provider.sqlSelectOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlSelectOAuth2RefreshTokenSession)
|
provider.sqlSelectOAuth2AuthorizeCodeSession = provider.db.Rebind(provider.sqlSelectOAuth2AuthorizeCodeSession)
|
||||||
|
|
||||||
|
provider.sqlInsertOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlInsertOAuth2OpenIDConnectSession)
|
||||||
|
provider.sqlRevokeOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlRevokeOAuth2OpenIDConnectSession)
|
||||||
|
provider.sqlRevokeOAuth2OpenIDConnectSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2OpenIDConnectSessionByRequestID)
|
||||||
|
provider.sqlDeactivateOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlDeactivateOAuth2OpenIDConnectSession)
|
||||||
|
provider.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID)
|
||||||
|
provider.sqlSelectOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlSelectOAuth2OpenIDConnectSession)
|
||||||
|
|
||||||
|
provider.sqlInsertOAuth2PARContext = provider.db.Rebind(provider.sqlInsertOAuth2PARContext)
|
||||||
|
provider.sqlRevokeOAuth2PARContext = provider.db.Rebind(provider.sqlRevokeOAuth2PARContext)
|
||||||
|
provider.sqlSelectOAuth2PARContext = provider.db.Rebind(provider.sqlSelectOAuth2PARContext)
|
||||||
|
|
||||||
provider.sqlInsertOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlInsertOAuth2PKCERequestSession)
|
provider.sqlInsertOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlInsertOAuth2PKCERequestSession)
|
||||||
provider.sqlRevokeOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlRevokeOAuth2PKCERequestSession)
|
provider.sqlRevokeOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlRevokeOAuth2PKCERequestSession)
|
||||||
|
@ -114,12 +118,12 @@ func NewPostgreSQLProvider(config *schema.Configuration, tconfig *tls.Config, gl
|
||||||
provider.sqlDeactivateOAuth2PKCERequestSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2PKCERequestSessionByRequestID)
|
provider.sqlDeactivateOAuth2PKCERequestSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2PKCERequestSessionByRequestID)
|
||||||
provider.sqlSelectOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlSelectOAuth2PKCERequestSession)
|
provider.sqlSelectOAuth2PKCERequestSession = provider.db.Rebind(provider.sqlSelectOAuth2PKCERequestSession)
|
||||||
|
|
||||||
provider.sqlInsertOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlInsertOAuth2OpenIDConnectSession)
|
provider.sqlInsertOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlInsertOAuth2RefreshTokenSession)
|
||||||
provider.sqlRevokeOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlRevokeOAuth2OpenIDConnectSession)
|
provider.sqlRevokeOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSession)
|
||||||
provider.sqlRevokeOAuth2OpenIDConnectSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2OpenIDConnectSessionByRequestID)
|
provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlRevokeOAuth2RefreshTokenSessionByRequestID)
|
||||||
provider.sqlDeactivateOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlDeactivateOAuth2OpenIDConnectSession)
|
provider.sqlDeactivateOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSession)
|
||||||
provider.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2OpenIDConnectSessionByRequestID)
|
provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID = provider.db.Rebind(provider.sqlDeactivateOAuth2RefreshTokenSessionByRequestID)
|
||||||
provider.sqlSelectOAuth2OpenIDConnectSession = provider.db.Rebind(provider.sqlSelectOAuth2OpenIDConnectSession)
|
provider.sqlSelectOAuth2RefreshTokenSession = provider.db.Rebind(provider.sqlSelectOAuth2RefreshTokenSession)
|
||||||
|
|
||||||
provider.sqlSelectOAuth2BlacklistedJTI = provider.db.Rebind(provider.sqlSelectOAuth2BlacklistedJTI)
|
provider.sqlSelectOAuth2BlacklistedJTI = provider.db.Rebind(provider.sqlSelectOAuth2BlacklistedJTI)
|
||||||
|
|
||||||
|
|
|
@ -314,6 +314,19 @@ const (
|
||||||
SET active = FALSE
|
SET active = FALSE
|
||||||
WHERE request_id = ?;`
|
WHERE request_id = ?;`
|
||||||
|
|
||||||
|
queryFmtSelectOAuth2PARContext = `
|
||||||
|
SELECT id, signature, request_id, client_id, requested_at, scopes, audience,
|
||||||
|
handled_response_types, response_mode, response_mode_default, revoked,
|
||||||
|
form_data, session_data
|
||||||
|
FROM %s
|
||||||
|
WHERE signature = ? AND revoked = FALSE;`
|
||||||
|
|
||||||
|
queryFmtInsertOAuth2PARContext = `
|
||||||
|
INSERT INTO %s (signature, request_id, client_id, requested_at, scopes, audience,
|
||||||
|
handled_response_types, response_mode, response_mode_default, revoked,
|
||||||
|
form_data, session_data)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`
|
||||||
|
|
||||||
queryFmtSelectOAuth2BlacklistedJTI = `
|
queryFmtSelectOAuth2BlacklistedJTI = `
|
||||||
SELECT id, signature, expires_at
|
SELECT id, signature, expires_at
|
||||||
FROM %s
|
FROM %s
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.20.1-alpine
|
FROM golang:1.20.2-alpine
|
||||||
|
|
||||||
ARG USER_ID
|
ARG USER_ID
|
||||||
ARG GROUP_ID
|
ARG GROUP_ID
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
version: '3'
|
version: '3'
|
||||||
services:
|
services:
|
||||||
k3d:
|
k3d:
|
||||||
image: ghcr.io/k3d-io/k3d:5.4.7-dind
|
image: ghcr.io/k3d-io/k3d:5.4.8-dind
|
||||||
volumes:
|
volumes:
|
||||||
- './example/kube:/authelia'
|
- './example/kube:/authelia'
|
||||||
- './example/kube/authelia/configs/configuration.yml:/configmaps/authelia/configuration.yml'
|
- './example/kube/authelia/configs/configuration.yml:/configmaps/authelia/configuration.yml'
|
||||||
|
|
|
@ -164,7 +164,7 @@ http {
|
||||||
# to the virtual endpoint introduced by nginx and declared in the next block.
|
# to the virtual endpoint introduced by nginx and declared in the next block.
|
||||||
location / {
|
location / {
|
||||||
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
||||||
auth_request /authelia;
|
auth_request /internal/authelia/authz;
|
||||||
|
|
||||||
## Set the $target_url variable based on the original request.
|
## Set the $target_url variable based on the original request.
|
||||||
set $target_url $scheme://$http_host$request_uri;
|
set $target_url $scheme://$http_host$request_uri;
|
||||||
|
@ -209,7 +209,7 @@ http {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Virtual endpoint forwarding requests to Authelia server.
|
# Virtual endpoint forwarding requests to Authelia server.
|
||||||
location /authelia {
|
location /internal/authelia/authz {
|
||||||
## Essential Proxy Configuration
|
## Essential Proxy Configuration
|
||||||
internal;
|
internal;
|
||||||
proxy_pass $upstream_authelia;
|
proxy_pass $upstream_authelia;
|
||||||
|
@ -250,7 +250,7 @@ http {
|
||||||
# Used by suites to test the forwarded users and groups headers produced by Authelia.
|
# Used by suites to test the forwarded users and groups headers produced by Authelia.
|
||||||
location /headers {
|
location /headers {
|
||||||
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
||||||
auth_request /authelia;
|
auth_request /internal/authelia/authz;
|
||||||
|
|
||||||
## Set the $target_url variable based on the original request.
|
## Set the $target_url variable based on the original request.
|
||||||
set $target_url $scheme://$http_host$request_uri;
|
set $target_url $scheme://$http_host$request_uri;
|
||||||
|
@ -307,7 +307,7 @@ http {
|
||||||
# to the virtual endpoint introduced by nginx and declared in the next block.
|
# to the virtual endpoint introduced by nginx and declared in the next block.
|
||||||
location / {
|
location / {
|
||||||
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
|
||||||
auth_request /authelia;
|
auth_request /internal/authelia/authz;
|
||||||
|
|
||||||
## Set the $target_url variable based on the original request.
|
## Set the $target_url variable based on the original request.
|
||||||
set $target_url $scheme://$http_host$request_uri;
|
set $target_url $scheme://$http_host$request_uri;
|
||||||
|
@ -346,7 +346,7 @@ http {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Virtual endpoint forwarding requests to Authelia server.
|
# Virtual endpoint forwarding requests to Authelia server.
|
||||||
location /authelia {
|
location /internal/authelia/authz {
|
||||||
## Essential Proxy Configuration
|
## Essential Proxy Configuration
|
||||||
internal;
|
internal;
|
||||||
proxy_pass $upstream_authelia;
|
proxy_pass $upstream_authelia;
|
||||||
|
@ -356,7 +356,6 @@ http {
|
||||||
# Those headers will be used by Authelia to deduce the target url of the user.
|
# Those headers will be used by Authelia to deduce the target url of the user.
|
||||||
#
|
#
|
||||||
# X-Forwarded-Proto is mandatory since Authelia uses the "trust proxy" option.
|
# X-Forwarded-Proto is mandatory since Authelia uses the "trust proxy" option.
|
||||||
# See https://expressjs.com/en/guide/behind-proxies.html
|
|
||||||
proxy_set_header X-Original-Method $request_method;
|
proxy_set_header X-Original-Method $request_method;
|
||||||
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
|
@ -1132,6 +1132,7 @@ func (s *CLISuite) TestStorage05ShouldChangeEncryptionKey() {
|
||||||
s.Assert().Contains(output, "\n\n\tTable (oauth2_openid_connect_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
s.Assert().Contains(output, "\n\n\tTable (oauth2_openid_connect_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
||||||
s.Assert().Contains(output, "\n\n\tTable (oauth2_pkce_request_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
s.Assert().Contains(output, "\n\n\tTable (oauth2_pkce_request_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
||||||
s.Assert().Contains(output, "\n\n\tTable (oauth2_refresh_token_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
s.Assert().Contains(output, "\n\n\tTable (oauth2_refresh_token_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
||||||
|
s.Assert().Contains(output, "\n\n\tTable (oauth2_par_context): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
||||||
s.Assert().Contains(output, "\n\n\tTable (totp_configurations): FAILURE\n\t\tInvalid Rows: 4\n\t\tTotal Rows: 4\n")
|
s.Assert().Contains(output, "\n\n\tTable (totp_configurations): FAILURE\n\t\tInvalid Rows: 4\n\t\tTotal Rows: 4\n")
|
||||||
s.Assert().Contains(output, "\n\n\tTable (webauthn_devices): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
s.Assert().Contains(output, "\n\n\tTable (webauthn_devices): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
||||||
|
|
||||||
|
@ -1149,6 +1150,7 @@ func (s *CLISuite) TestStorage05ShouldChangeEncryptionKey() {
|
||||||
s.Assert().Contains(output, "\n\n\tTable (oauth2_openid_connect_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
s.Assert().Contains(output, "\n\n\tTable (oauth2_openid_connect_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
||||||
s.Assert().Contains(output, "\n\n\tTable (oauth2_pkce_request_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
s.Assert().Contains(output, "\n\n\tTable (oauth2_pkce_request_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
||||||
s.Assert().Contains(output, "\n\n\tTable (oauth2_refresh_token_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
s.Assert().Contains(output, "\n\n\tTable (oauth2_refresh_token_session): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
||||||
|
s.Assert().Contains(output, "\n\n\tTable (oauth2_par_context): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
||||||
s.Assert().Contains(output, "\n\n\tTable (totp_configurations): SUCCESS\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 4\n")
|
s.Assert().Contains(output, "\n\n\tTable (totp_configurations): SUCCESS\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 4\n")
|
||||||
s.Assert().Contains(output, "\n\n\tTable (webauthn_devices): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
s.Assert().Contains(output, "\n\n\tTable (webauthn_devices): N/A\n\t\tInvalid Rows: 0\n\t\tTotal Rows: 0\n")
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
This email has been sent to you in order to validate your identity.
|
This email has been sent to you in order to validate your identity. Purpose: {{ .Title }}.
|
||||||
|
|
||||||
If you did not initiate the process your credentials might have been compromised and you should reset your password and contact an administrator.
|
If you did not initiate the process your credentials might have been compromised and you should reset your password and contact an administrator.
|
||||||
|
|
||||||
To setup your 2FA please visit the following URL: {{ .LinkURL }}
|
To confirm your identity please visit the following URL: {{ .LinkURL }}
|
||||||
|
|
||||||
This email was generated by a user with the IP {{ .RemoteIP }}.
|
This email was generated by a user with the IP {{ .RemoteIP }}.
|
||||||
|
|
||||||
|
|
|
@ -104,8 +104,13 @@ func IsStringSliceContainsAll(needles []string, haystack []string) (inSlice bool
|
||||||
|
|
||||||
// IsStringSliceContainsAny checks if the haystack contains any of the strings in the needles.
|
// IsStringSliceContainsAny checks if the haystack contains any of the strings in the needles.
|
||||||
func IsStringSliceContainsAny(needles []string, haystack []string) (inSlice bool) {
|
func IsStringSliceContainsAny(needles []string, haystack []string) (inSlice bool) {
|
||||||
|
return IsStringSliceContainsAnyF(needles, haystack, IsStringInSlice)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsStringSliceContainsAnyF checks if the haystack contains any of the strings in the needles using the isInSlice func.
|
||||||
|
func IsStringSliceContainsAnyF(needles []string, haystack []string, isInSlice func(needle string, haystack []string) bool) (inSlice bool) {
|
||||||
for _, n := range needles {
|
for _, n := range needles {
|
||||||
if IsStringInSlice(n, haystack) {
|
if isInSlice(n, haystack) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,12 @@
|
||||||
"@fortawesome/free-solid-svg-icons": "6.3.0",
|
"@fortawesome/free-solid-svg-icons": "6.3.0",
|
||||||
"@fortawesome/react-fontawesome": "0.2.0",
|
"@fortawesome/react-fontawesome": "0.2.0",
|
||||||
"@mui/icons-material": "5.11.11",
|
"@mui/icons-material": "5.11.11",
|
||||||
"@mui/material": "5.11.11",
|
"@mui/material": "5.11.12",
|
||||||
"@mui/styles": "5.11.11",
|
"@mui/styles": "5.11.12",
|
||||||
"axios": "1.3.4",
|
"axios": "1.3.4",
|
||||||
"broadcast-channel": "4.20.2",
|
"broadcast-channel": "4.20.2",
|
||||||
"classnames": "2.3.2",
|
"classnames": "2.3.2",
|
||||||
"i18next": "22.4.10",
|
"i18next": "22.4.11",
|
||||||
"i18next-browser-languagedetector": "7.0.1",
|
"i18next-browser-languagedetector": "7.0.1",
|
||||||
"i18next-http-backend": "2.1.1",
|
"i18next-http-backend": "2.1.1",
|
||||||
"qrcode.react": "3.1.0",
|
"qrcode.react": "3.1.0",
|
||||||
|
@ -148,18 +148,18 @@
|
||||||
"@testing-library/jest-dom": "5.16.5",
|
"@testing-library/jest-dom": "5.16.5",
|
||||||
"@testing-library/react": "14.0.0",
|
"@testing-library/react": "14.0.0",
|
||||||
"@types/jest": "29.4.0",
|
"@types/jest": "29.4.0",
|
||||||
"@types/node": "18.14.4",
|
"@types/node": "18.15.0",
|
||||||
"@types/qrcode.react": "1.0.2",
|
"@types/qrcode.react": "1.0.2",
|
||||||
"@types/react": "18.0.28",
|
"@types/react": "18.0.28",
|
||||||
"@types/react-dom": "18.0.11",
|
"@types/react-dom": "18.0.11",
|
||||||
"@types/zxcvbn": "4.4.1",
|
"@types/zxcvbn": "4.4.1",
|
||||||
"@typescript-eslint/eslint-plugin": "5.54.0",
|
"@typescript-eslint/eslint-plugin": "5.54.1",
|
||||||
"@typescript-eslint/parser": "5.54.0",
|
"@typescript-eslint/parser": "5.54.1",
|
||||||
"@vitejs/plugin-react": "3.1.0",
|
"@vitejs/plugin-react": "3.1.0",
|
||||||
"esbuild": "0.17.10",
|
"esbuild": "0.17.11",
|
||||||
"esbuild-jest": "0.5.0",
|
"esbuild-jest": "0.5.0",
|
||||||
"eslint": "8.35.0",
|
"eslint": "8.35.0",
|
||||||
"eslint-config-prettier": "8.6.0",
|
"eslint-config-prettier": "8.7.0",
|
||||||
"eslint-config-react-app": "7.0.1",
|
"eslint-config-react-app": "7.0.1",
|
||||||
"eslint-formatter-rdjson": "1.0.5",
|
"eslint-formatter-rdjson": "1.0.5",
|
||||||
"eslint-import-resolver-typescript": "3.5.3",
|
"eslint-import-resolver-typescript": "3.5.3",
|
||||||
|
@ -169,8 +169,8 @@
|
||||||
"eslint-plugin-react": "7.32.2",
|
"eslint-plugin-react": "7.32.2",
|
||||||
"eslint-plugin-react-hooks": "4.6.0",
|
"eslint-plugin-react-hooks": "4.6.0",
|
||||||
"husky": "8.0.3",
|
"husky": "8.0.3",
|
||||||
"jest": "29.4.3",
|
"jest": "29.5.0",
|
||||||
"jest-environment-jsdom": "29.4.3",
|
"jest-environment-jsdom": "29.5.0",
|
||||||
"jest-transform-stub": "2.0.0",
|
"jest-transform-stub": "2.0.0",
|
||||||
"jest-watch-typeahead": "2.2.2",
|
"jest-watch-typeahead": "2.2.2",
|
||||||
"prettier": "2.8.4",
|
"prettier": "2.8.4",
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue