feat(oidc): private_key_jwt client auth (#5280)
This adds support for the private_key_jwt client authentication method. Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com> Co-authored-by: Amir Zarrinkafsh <nightah@me.com>pull/5437/head
parent
cef374cdc1
commit
65ecfe4b9a
|
@ -4,7 +4,16 @@
|
||||||
# Authelia Configuration #
|
# Authelia Configuration #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
## Note: the container by default expects to find this file at /config/configuration.yml.
|
##
|
||||||
|
## Notes:
|
||||||
|
##
|
||||||
|
## - the default location of this file is assumed to be configuration.yml unless otherwise noted
|
||||||
|
## - when using docker the container expects this by default to be at /config/configuration.yml
|
||||||
|
## - the default location where this file is loaded from can be overridden with the X_AUTHELIA_CONFIG environment var
|
||||||
|
## - the comments in this configuration file are helpful but users should consult the official documentation on the
|
||||||
|
## website at https://www.authelia.com/ or https://www.authelia.com/configuration/prologue/introduction/
|
||||||
|
## - this configuration file template is not automatically updated
|
||||||
|
##
|
||||||
|
|
||||||
## Certificates directory specifies where Authelia will load trusted certificates (public portion) from in addition to
|
## Certificates directory specifies where Authelia will load trusted certificates (public portion) from in addition to
|
||||||
## the system certificates store.
|
## the system certificates store.
|
||||||
|
@ -357,73 +366,37 @@ authentication_backend:
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# certificate_chain: |
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# private_key: |
|
# private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
## The distinguished name of the container searched for objects in the directory information tree.
|
## The distinguished name of the container searched for objects in the directory information tree.
|
||||||
|
@ -485,7 +458,7 @@ authentication_backend:
|
||||||
# permit_referrals: false
|
# permit_referrals: false
|
||||||
|
|
||||||
## The username and password of the admin user.
|
## The username and password of the admin user.
|
||||||
# user: cn=admin,dc=example,dc=com
|
# user: 'cn=admin,dc=example,dc=com'
|
||||||
## Password can also be set using a secret: https://www.authelia.com/c/secrets
|
## Password can also be set using a secret: https://www.authelia.com/c/secrets
|
||||||
# password: 'password'
|
# password: 'password'
|
||||||
|
|
||||||
|
@ -622,7 +595,7 @@ access_control:
|
||||||
# networks:
|
# networks:
|
||||||
# - '10.10.0.0/16'
|
# - '10.10.0.0/16'
|
||||||
# - '192.168.2.0/24'
|
# - '192.168.2.0/24'
|
||||||
# - name: VPN
|
# - name: 'VPN'
|
||||||
# networks: '10.9.0.0/16'
|
# networks: '10.9.0.0/16'
|
||||||
|
|
||||||
# rules:
|
# rules:
|
||||||
|
@ -748,7 +721,8 @@ session:
|
||||||
# expiration: '1h'
|
# expiration: '1h'
|
||||||
|
|
||||||
## The time before the cookie expires and the session is destroyed if remember me IS selected by the user. Setting
|
## The time before the cookie expires and the session is destroyed if remember me IS selected by the user. Setting
|
||||||
## this value to -1 disables remember me for this session cookie domain.
|
## this value to -1 disables remember me for this session cookie domain. If allowed and the user uses the remember
|
||||||
|
## me checkbox this overrides the expiration option and disables the inactivity option.
|
||||||
# remember_me: '1M'
|
# remember_me: '1M'
|
||||||
|
|
||||||
## Cookie Session Domain default 'name' value.
|
## Cookie Session Domain default 'name' value.
|
||||||
|
@ -816,73 +790,37 @@ session:
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# certificate_chain: |
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# private_key: |
|
# private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
## The Redis HA configuration options.
|
## The Redis HA configuration options.
|
||||||
|
@ -997,73 +935,37 @@ regulation:
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# certificate_chain: |
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# private_key: |
|
# private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -1116,73 +1018,37 @@ regulation:
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# certificate_chain: |
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# private_key: |
|
# private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -1270,73 +1136,37 @@ notifier:
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# certificate_chain: |
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# private_key: |
|
# private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -1354,80 +1184,88 @@ notifier:
|
||||||
## HMAC Secret can also be set using a secret: https://www.authelia.com/c/secrets
|
## HMAC Secret can also be set using a secret: https://www.authelia.com/c/secrets
|
||||||
# hmac_secret: 'this_is_a_secret_abc123abc123abc'
|
# hmac_secret: 'this_is_a_secret_abc123abc123abc'
|
||||||
|
|
||||||
## The issuer_certificate_chain is an optional PEM encoded certificate chain. It's used in conjunction with the
|
## Issuer JWKS configures multiple JSON Web Keys. It's required that at least one of these is RS256 or the
|
||||||
## issuer_private_key to sign JWT's. All certificates in the chain must be within the validity period, and every
|
## option issuer_private_key is configured. There must only be one key per algorithm at this time.
|
||||||
## certificate included must be signed by the certificate immediately after it if provided.
|
## For RSA keys the minimum is a 2048 bit key.
|
||||||
# issuer_certificate_chain: |
|
# issuer_private_keys:
|
||||||
|
# -
|
||||||
|
## Key ID embedded into the JWT header for key matching. Must be an alphanumeric string with 7 or less characters.
|
||||||
|
## This value is automatically generated if not provided. It's recommended to not configure this.
|
||||||
|
# key_id: 'example'
|
||||||
|
|
||||||
|
## The key algorithm used with this key.
|
||||||
|
# algorithm: 'RS256'
|
||||||
|
|
||||||
|
## The key use expected with this key. Currently only 'sig' is supported.
|
||||||
|
# use: 'sig'
|
||||||
|
|
||||||
|
## Required Private Key in PEM DER form.
|
||||||
|
# key: |
|
||||||
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
|
|
||||||
|
## Optional matching certificate chain in PEM DER form that matches the key. All certificates within the chain
|
||||||
|
## must be valid and current, and from top to bottom each certificate must be signed by the subsequent one.
|
||||||
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
|
||||||
# -----BEGIN CERTIFICATE-----
|
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The issuer_private_key is used to sign the JWT forged by OpenID Connect.
|
## The issuer_private_key is used to sign the JWT forged by OpenID Connect. This is in addition to the
|
||||||
|
## issuer_private_keys option. Assumed to use the RS256 algorithm, and must not be specified if any of the
|
||||||
|
## keys in issuer_private_keys also has the algorithm RS256 or are an RSA key without an algorithm.
|
||||||
## Issuer Private Key can also be set using a secret: https://www.authelia.com/c/secrets
|
## Issuer Private Key can also be set using a secret: https://www.authelia.com/c/secrets
|
||||||
# issuer_private_key: |
|
# issuer_private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
|
## Optional matching certificate chain in PEM DER form that matches the issuer_private_key. All certificates within
|
||||||
|
## the chain must be valid and current, and from top to bottom each certificate must be signed by the next
|
||||||
|
## certificate in the chain if provided.
|
||||||
|
# issuer_certificate_chain: |
|
||||||
|
# -----BEGIN CERTIFICATE-----
|
||||||
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
# -----END CERTIFICATE-----
|
||||||
|
# -----BEGIN CERTIFICATE-----
|
||||||
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The lifespans configure the expiration for these token types in the duration common syntax.
|
## The lifespans configure the expiration for these token types in the duration common syntax.
|
||||||
# access_token_lifespan: '1h'
|
# access_token_lifespan: '1h'
|
||||||
# authorize_code_lifespan: '1m'
|
# authorize_code_lifespan: '1m'
|
||||||
|
@ -1499,6 +1337,11 @@ notifier:
|
||||||
# - 'email'
|
# - 'email'
|
||||||
# - 'profile'
|
# - 'profile'
|
||||||
|
|
||||||
|
## Grant Types configures which grants this client can obtain.
|
||||||
|
## It's not recommended to define this unless you know what you're doing.
|
||||||
|
# grant_types:
|
||||||
|
# - 'authorization_code'
|
||||||
|
|
||||||
## Response Types configures which responses this client can be sent.
|
## Response Types configures which responses this client can be sent.
|
||||||
## It's not recommended to define this unless you know what you're doing.
|
## It's not recommended to define this unless you know what you're doing.
|
||||||
# response_types:
|
# response_types:
|
||||||
|
@ -1509,25 +1352,19 @@ notifier:
|
||||||
# - 'form_post'
|
# - 'form_post'
|
||||||
# - 'query'
|
# - 'query'
|
||||||
|
|
||||||
## Grant Types configures which grants this client can obtain.
|
|
||||||
## It's not recommended to define this unless you know what you're doing.
|
|
||||||
# grant_types:
|
|
||||||
# - 'authorization_code'
|
|
||||||
|
|
||||||
## The permitted client authentication method for the Token Endpoint for this client.
|
|
||||||
# token_endpoint_auth_method: 'client_secret_basic'
|
|
||||||
|
|
||||||
## The permitted client authentication signing algorithm for the Token Endpoint for this client when using
|
|
||||||
## the 'client_secret_jwt' token_endpoint_auth_method.
|
|
||||||
# token_endpoint_auth_signing_alg: HS256
|
|
||||||
|
|
||||||
## The permitted client authentication signing algorithm for the Token Endpoint for this client when using
|
|
||||||
## the 'client_secret_jwt' token_endpoint_auth_method.
|
|
||||||
# token_endpoint_auth_signing_alg: HS256
|
|
||||||
|
|
||||||
## The policy to require for this client; one_factor or two_factor.
|
## The policy to require for this client; one_factor or two_factor.
|
||||||
# authorization_policy: 'two_factor'
|
# authorization_policy: 'two_factor'
|
||||||
|
|
||||||
|
## The consent mode controls how consent is obtained.
|
||||||
|
# consent_mode: 'auto'
|
||||||
|
|
||||||
|
## This value controls the duration a consent on this client remains remembered when the consent mode is
|
||||||
|
## configured as 'auto' or 'pre-configured' in the duration common syntax.
|
||||||
|
# pre_configured_consent_duration: '1w'
|
||||||
|
|
||||||
|
## Enforces the use of Pushed Authorization Requests for this client when set to true.
|
||||||
|
# enforce_par: false
|
||||||
|
|
||||||
## Enforces the use of PKCE for this client when set to true.
|
## Enforces the use of PKCE for this client when set to true.
|
||||||
# enforce_pkce: false
|
# enforce_pkce: false
|
||||||
|
|
||||||
|
@ -1535,13 +1372,69 @@ notifier:
|
||||||
## Options are 'plain' and 'S256'.
|
## Options are 'plain' and 'S256'.
|
||||||
# pkce_challenge_method: 'S256'
|
# pkce_challenge_method: 'S256'
|
||||||
|
|
||||||
|
## The permitted client authentication method for the Token Endpoint for this client.
|
||||||
|
# token_endpoint_auth_method: 'client_secret_basic'
|
||||||
|
|
||||||
|
## The permitted client authentication signing algorithm for the Token Endpoint for this client when using
|
||||||
|
## the 'client_secret_jwt' or 'private_key_jwt' token_endpoint_auth_method.
|
||||||
|
# token_endpoint_auth_signing_alg: 'RS256'
|
||||||
|
|
||||||
|
## The signing algorithm which must be used for request objects. A client JWK with a matching algorithm must be
|
||||||
|
## included if configured.
|
||||||
|
# request_object_signing_alg: 'RS256'
|
||||||
|
|
||||||
|
## The signing algorithm used for ID Tokens. Am issuer JWK with a matching algorithm must be included.
|
||||||
|
# id_token_signing_alg: 'RS256'
|
||||||
|
|
||||||
## The algorithm used to sign userinfo endpoint responses for this client, either none or RS256.
|
## The algorithm used to sign userinfo endpoint responses for this client, either none or RS256.
|
||||||
# userinfo_signing_algorithm: 'none'
|
# userinfo_signing_alg: 'none'
|
||||||
|
|
||||||
## The consent mode controls how consent is obtained.
|
## Trusted public keys configuration for request object signing for things such as private_key_jwt
|
||||||
# consent_mode: 'auto'
|
# public_keys:
|
||||||
|
|
||||||
## This value controls the duration a consent on this client remains remembered when the consent mode is
|
## URL of the HTTPS endpoint which serves the keys. It's recommended to manually configure them in the
|
||||||
## configured as 'auto' or 'pre-configured' in the duration common syntax.
|
## values option below. Please note the URL and the individual values are mutually exclusive.
|
||||||
# pre_configured_consent_duration: '1w'
|
# uri: 'https://app.example.com/jwks.json'
|
||||||
|
|
||||||
|
## Values from the individual keys.
|
||||||
|
# values:
|
||||||
|
# -
|
||||||
|
## Key ID used to match the JWT's to an individual identifier. This option is required if configured.
|
||||||
|
# key_id: 'example'
|
||||||
|
|
||||||
|
## The key algorithm expected with this key.
|
||||||
|
# algorithm: 'RS256'
|
||||||
|
|
||||||
|
## The key use expected with this key. Currently only 'sig' is supported.
|
||||||
|
# use: 'sig'
|
||||||
|
|
||||||
|
## Required Public Key in PEM DER form.
|
||||||
|
# key: |
|
||||||
|
# -----BEGIN RSA PUBLIC KEY-----
|
||||||
|
# MEgCQQDAwV26ZA1lodtOQxNrJ491gWT+VzFum9IeZ+WTmMypYWyW1CzXKwsvTHDz
|
||||||
|
# 9ec+jserR3EMQ0Rr24lj13FL1ib5AgMBAAE=
|
||||||
|
# -----END RSA PUBLIC KEY----
|
||||||
|
|
||||||
|
## The matching certificate chain in PEM DER form that matches the key if available.
|
||||||
|
# certificate_chain: |
|
||||||
|
# -----BEGIN CERTIFICATE-----
|
||||||
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
# -----END CERTIFICATE-----
|
||||||
|
# -----BEGIN CERTIFICATE-----
|
||||||
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
# -----END CERTIFICATE-----
|
||||||
...
|
...
|
||||||
|
|
|
@ -16,4 +16,4 @@ aliases:
|
||||||
|
|
||||||
## OpenID Connect
|
## OpenID Connect
|
||||||
|
|
||||||
The only identity provider implementation supported at this time is [OpenID Connect 1.0](open-id-connect.md).
|
The only identity provider implementation supported at this time is [OpenID Connect 1.0](openid-connect/provider.md).
|
||||||
|
|
|
@ -1,671 +0,0 @@
|
||||||
---
|
|
||||||
title: "OpenID Connect"
|
|
||||||
description: "OpenID Connect Configuration"
|
|
||||||
lead: "Authelia can operate as an OpenID Connect 1.0 Provider. This section describes how to configure this."
|
|
||||||
date: 2022-06-15T17:51:47+10:00
|
|
||||||
draft: false
|
|
||||||
images: []
|
|
||||||
menu:
|
|
||||||
configuration:
|
|
||||||
parent: "identity-providers"
|
|
||||||
weight: 190200
|
|
||||||
toc: true
|
|
||||||
aliases:
|
|
||||||
- /c/oidc
|
|
||||||
- /docs/configuration/identity-providers/oidc.html
|
|
||||||
---
|
|
||||||
|
|
||||||
__Authelia__ currently supports the [OpenID Connect 1.0] Provider role as an open
|
|
||||||
[__beta__](../../roadmap/active/openid-connect.md) feature. We currently do not support the [OpenID Connect 1.0] Relying
|
|
||||||
Party role. This means other applications that implement the [OpenID Connect 1.0] Relying Party role can use Authelia as
|
|
||||||
an [OpenID Connect 1.0] Provider similar to how you may use social media or development platforms for login.
|
|
||||||
|
|
||||||
The [OpenID Connect 1.0] Relying Party role is the role which allows an application to use GitHub, Google, or other
|
|
||||||
[OpenID Connect 1.0] Providers for authentication and authorization. We do not intend to support this functionality at
|
|
||||||
this moment in time.
|
|
||||||
|
|
||||||
More information about the beta can be found in the [roadmap](../../roadmap/active/openid-connect.md).
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
The following snippet provides a sample-configuration for the OIDC identity provider explaining each field in detail.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
identity_providers:
|
|
||||||
oidc:
|
|
||||||
hmac_secret: this_is_a_secret_abc123abc123abc
|
|
||||||
issuer_certificate_chain: |
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
|
||||||
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
|
||||||
MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
|
||||||
ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
|
||||||
/Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
|
||||||
LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
|
||||||
91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
|
||||||
kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
|
||||||
Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
/ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
|
||||||
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
|
||||||
MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
|
||||||
ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
|
||||||
zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
|
||||||
5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
|
||||||
kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
|
||||||
ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
|
||||||
Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
issuer_private_key: |
|
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
|
||||||
T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
|
||||||
KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
|
||||||
+5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
|
||||||
LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
|
||||||
txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
|
||||||
aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
|
||||||
Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
DO NOT USE==
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
issuer_jwks:
|
|
||||||
- key_id: ''
|
|
||||||
algorithm: 'RS256'
|
|
||||||
key: |
|
|
||||||
<private key data>
|
|
||||||
certificate_chain: |
|
|
||||||
<certificate chain data>
|
|
||||||
access_token_lifespan: 1h
|
|
||||||
authorize_code_lifespan: 1m
|
|
||||||
id_token_lifespan: 1h
|
|
||||||
refresh_token_lifespan: 90m
|
|
||||||
enable_client_debug_messages: false
|
|
||||||
enforce_pkce: public_clients_only
|
|
||||||
cors:
|
|
||||||
endpoints:
|
|
||||||
- authorization
|
|
||||||
- token
|
|
||||||
- revocation
|
|
||||||
- introspection
|
|
||||||
allowed_origins:
|
|
||||||
- https://example.com
|
|
||||||
allowed_origins_from_client_redirect_uris: false
|
|
||||||
clients:
|
|
||||||
- id: myapp
|
|
||||||
description: My Application
|
|
||||||
secret: '$pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng' # The digest of 'insecure_secret'.
|
|
||||||
sector_identifier: ''
|
|
||||||
public: false
|
|
||||||
authorization_policy: two_factor
|
|
||||||
consent_mode: explicit
|
|
||||||
pre_configured_consent_duration: 1w
|
|
||||||
audience: []
|
|
||||||
scopes:
|
|
||||||
- openid
|
|
||||||
- groups
|
|
||||||
- email
|
|
||||||
- profile
|
|
||||||
redirect_uris:
|
|
||||||
- https://oidc.example.com:8080/oauth2/callback
|
|
||||||
grant_types:
|
|
||||||
- refresh_token
|
|
||||||
- authorization_code
|
|
||||||
response_types:
|
|
||||||
- code
|
|
||||||
response_modes:
|
|
||||||
- form_post
|
|
||||||
- query
|
|
||||||
- fragment
|
|
||||||
userinfo_signing_algorithm: none
|
|
||||||
```
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
### hmac_secret
|
|
||||||
|
|
||||||
{{< confkey type="string" required="yes" >}}
|
|
||||||
|
|
||||||
*__Important Note:__ This can also be defined using a [secret](../methods/secrets.md) which is __strongly recommended__
|
|
||||||
especially for containerized deployments.*
|
|
||||||
|
|
||||||
The HMAC secret used to sign the [JWT]'s. The provided string is hashed to a SHA256 ([RFC6234]) byte string for the
|
|
||||||
purpose of meeting the required format.
|
|
||||||
|
|
||||||
It's __strongly recommended__ this is a
|
|
||||||
[Random Alphanumeric String](../../reference/guides/generating-secure-values.md#generating-a-random-alphanumeric-string)
|
|
||||||
with 64 or more characters.
|
|
||||||
|
|
||||||
### issuer_private_key
|
|
||||||
|
|
||||||
{{< confkey type="string" required="yes" >}}
|
|
||||||
|
|
||||||
*__Important Note:__ This can also be defined using a [secret](../methods/secrets.md) which is __strongly recommended__
|
|
||||||
especially for containerized deployments.*
|
|
||||||
|
|
||||||
The private key used to sign/encrypt the [OpenID Connect 1.0] issued [JWT]'s. The key must be generated by the administrator
|
|
||||||
and can be done by following the
|
|
||||||
[Generating an RSA Keypair](../../reference/guides/generating-secure-values.md#generating-an-rsa-keypair) guide.
|
|
||||||
|
|
||||||
The private key *__MUST__*:
|
|
||||||
* Be a PEM block encoded in the DER base64 format ([RFC4648]).
|
|
||||||
* Be an RSA Key.
|
|
||||||
* Have a key size of at least 2048 bits.
|
|
||||||
|
|
||||||
If the [issuer_certificate_chain](#issuercertificatechain) is provided the private key must include matching public
|
|
||||||
key data for the first certificate in the chain.
|
|
||||||
|
|
||||||
### issuer_certificate_chain
|
|
||||||
|
|
||||||
{{< confkey type="string" required="no" >}}
|
|
||||||
|
|
||||||
The certificate chain/bundle to be used with the [issuer_private_key](#issuer_private_key) DER base64 ([RFC4648])
|
|
||||||
encoded PEM format used to sign/encrypt the [OpenID Connect 1.0] [JWT]'s. When configured it enables the [x5c] and [x5t]
|
|
||||||
JSON key's in the JWKs [Discoverable Endpoint](../../integration/openid-connect/introduction.md#discoverable-endpoints)
|
|
||||||
as per [RFC7517].
|
|
||||||
|
|
||||||
[RFC7517]: https://datatracker.ietf.org/doc/html/rfc7517
|
|
||||||
[x5c]: https://datatracker.ietf.org/doc/html/rfc7517#section-4.7
|
|
||||||
[x5t]: https://datatracker.ietf.org/doc/html/rfc7517#section-4.8
|
|
||||||
|
|
||||||
The first certificate in the chain must have the public key for the [issuer_private_key](#issuerprivatekey), each
|
|
||||||
certificate in the chain must be valid for the current date, and each certificate in the chain should be signed by the
|
|
||||||
certificate immediately following it if present.
|
|
||||||
|
|
||||||
### issuer_jwks
|
|
||||||
|
|
||||||
{{< confkey type="list(object" required="no" >}}
|
|
||||||
|
|
||||||
The list of JWKS instead of or in addition to the [issuer_private_key](#issuerprivatekey) and
|
|
||||||
[issuer_certificate_chain](#issuercertificatechain). Can also accept ECDSA Private Key's and Certificates.
|
|
||||||
|
|
||||||
#### key_id
|
|
||||||
|
|
||||||
{{< confkey type="string" default="<thumbprint of public key>" required="no" >}}
|
|
||||||
|
|
||||||
Completely optional, and generally discouraged unless there is a collision between the automatically generated key id's.
|
|
||||||
If provided must be a unique string with 7 or less alphanumeric characters.
|
|
||||||
|
|
||||||
This value is the first 7 characters of the public key thumbprint (SHA1) encoded into hexadecimal.
|
|
||||||
|
|
||||||
#### algorithm
|
|
||||||
|
|
||||||
{{< confkey type="string" required="no" >}}
|
|
||||||
|
|
||||||
The algorithm for this key. This value must be unique. It's automatically detected based on the type of key.
|
|
||||||
|
|
||||||
#### key
|
|
||||||
|
|
||||||
{{< confkey type="string" required="yes" >}}
|
|
||||||
|
|
||||||
The private key associated with this key entry.
|
|
||||||
|
|
||||||
The private key used to sign/encrypt the [OpenID Connect 1.0] issued [JWT]'s. The key must be generated by the administrator
|
|
||||||
and can be done by following the
|
|
||||||
[Generating an RSA Keypair](../../reference/guides/generating-secure-values.md#generating-an-rsa-keypair) guide.
|
|
||||||
|
|
||||||
The private key *__MUST__*:
|
|
||||||
* Be a PEM block encoded in the DER base64 format ([RFC4648]).
|
|
||||||
* Be one of:
|
|
||||||
* An RSA key with a key size of at least 2048 bits.
|
|
||||||
* An ECDSA private key with one of the P-256, P-384, or P-521 elliptical curves.
|
|
||||||
|
|
||||||
If the [certificate_chain](#certificatechain) is provided the private key must include matching public
|
|
||||||
key data for the first certificate in the chain.
|
|
||||||
|
|
||||||
#### certificate_chain
|
|
||||||
|
|
||||||
{{< confkey type="string" required="no" >}}
|
|
||||||
|
|
||||||
The certificate chain/bundle to be used with the [key](#key) DER base64 ([RFC4648])
|
|
||||||
encoded PEM format used to sign/encrypt the [OpenID Connect 1.0] [JWT]'s. When configured it enables the [x5c] and [x5t]
|
|
||||||
JSON key's in the JWKs [Discoverable Endpoint](../../integration/openid-connect/introduction.md#discoverable-endpoints)
|
|
||||||
as per [RFC7517].
|
|
||||||
|
|
||||||
[RFC7517]: https://datatracker.ietf.org/doc/html/rfc7517
|
|
||||||
[x5c]: https://datatracker.ietf.org/doc/html/rfc7517#section-4.7
|
|
||||||
[x5t]: https://datatracker.ietf.org/doc/html/rfc7517#section-4.8
|
|
||||||
|
|
||||||
The first certificate in the chain must have the public key for the [key](#key), each certificate in the chain must be
|
|
||||||
valid for the current date, and each certificate in the chain should be signed by the certificate immediately following
|
|
||||||
it if present.
|
|
||||||
|
|
||||||
### access_token_lifespan
|
|
||||||
|
|
||||||
{{< confkey type="duration" default="1h" required="no" >}}
|
|
||||||
|
|
||||||
The maximum lifetime of an access token. It's generally recommended keeping this short similar to the default.
|
|
||||||
For more information read these docs about [token lifespan].
|
|
||||||
|
|
||||||
### authorize_code_lifespan
|
|
||||||
|
|
||||||
{{< confkey type="duration" default="1m" required="no" >}}
|
|
||||||
|
|
||||||
The maximum lifetime of an authorize code. This can be rather short, as the authorize code should only be needed to
|
|
||||||
obtain the other token types. For more information read these docs about [token lifespan].
|
|
||||||
|
|
||||||
### id_token_lifespan
|
|
||||||
|
|
||||||
{{< confkey type="duration" default="1h" required="no" >}}
|
|
||||||
|
|
||||||
The maximum lifetime of an ID token. For more information read these docs about [token lifespan].
|
|
||||||
|
|
||||||
### refresh_token_lifespan
|
|
||||||
|
|
||||||
{{< confkey type="string" default="90m" required="no" >}}
|
|
||||||
|
|
||||||
The maximum lifetime of a refresh token. The
|
|
||||||
refresh token can be used to obtain new refresh tokens as well as access tokens or id tokens with an
|
|
||||||
up-to-date expiration. For more information read these docs about [token lifespan].
|
|
||||||
|
|
||||||
A good starting point is 50% more or 30 minutes more (which ever is less) time than the highest lifespan out of the
|
|
||||||
[access token lifespan](#access_token_lifespan), the [authorize code lifespan](#authorize_code_lifespan), and the
|
|
||||||
[id token lifespan](#id_token_lifespan). For instance the default for all of these is 60 minutes, so the default refresh
|
|
||||||
token lifespan is 90 minutes.
|
|
||||||
|
|
||||||
### enable_client_debug_messages
|
|
||||||
|
|
||||||
{{< confkey type="boolean" default="false" required="no" >}}
|
|
||||||
|
|
||||||
Allows additional debug messages to be sent to the clients.
|
|
||||||
|
|
||||||
### minimum_parameter_entropy
|
|
||||||
|
|
||||||
{{< confkey type="integer" default="8" required="no" >}}
|
|
||||||
|
|
||||||
This controls the minimum length of the `nonce` and `state` parameters.
|
|
||||||
|
|
||||||
*__Security Notice:__* Changing this value is generally discouraged, reducing it from the default can theoretically
|
|
||||||
make certain scenarios less secure. It is highly encouraged that if your OpenID Connect RP does not send these
|
|
||||||
parameters or sends parameters with a lower length than the default that they implement a change rather than changing
|
|
||||||
this value.
|
|
||||||
|
|
||||||
### enforce_pkce
|
|
||||||
|
|
||||||
{{< confkey type="string" default="public_clients_only" required="no" >}}
|
|
||||||
|
|
||||||
[Proof Key for Code Exchange](https://datatracker.ietf.org/doc/html/rfc7636) enforcement policy: if specified, must be
|
|
||||||
either `never`, `public_clients_only` or `always`.
|
|
||||||
|
|
||||||
If set to `public_clients_only` (default), [PKCE] will be required for public clients using the
|
|
||||||
[Authorization Code Flow].
|
|
||||||
|
|
||||||
When set to `always`, [PKCE] will be required for all clients using the Authorization Code flow.
|
|
||||||
|
|
||||||
*__Security Notice:__* Changing this value to `never` is generally discouraged, reducing it from the default can
|
|
||||||
theoretically make certain client-side applications (mobile applications, SPA) vulnerable to CSRF and authorization code
|
|
||||||
interception attacks.
|
|
||||||
|
|
||||||
### enable_pkce_plain_challenge
|
|
||||||
|
|
||||||
{{< confkey type="boolean" default="false" required="no" >}}
|
|
||||||
|
|
||||||
Allows [PKCE] `plain` challenges when set to `true`.
|
|
||||||
|
|
||||||
*__Security Notice:__* Changing this value is generally discouraged. Applications should use the `S256` [PKCE] challenge
|
|
||||||
method instead.
|
|
||||||
|
|
||||||
### pushed_authorizations
|
|
||||||
|
|
||||||
Controls the behaviour of [Pushed Authorization Requests].
|
|
||||||
|
|
||||||
#### enforce
|
|
||||||
|
|
||||||
{{< confkey type="boolean" default="false" required="no" >}}
|
|
||||||
|
|
||||||
When enabled all authorization requests must use the [Pushed Authorization Requests] flow.
|
|
||||||
|
|
||||||
#### context_lifespan
|
|
||||||
|
|
||||||
{{< confkey type="duration" default="5m" required="no" >}}
|
|
||||||
|
|
||||||
The maximum amount of time between the [Pushed Authorization Requests] flow being initiated and the generated
|
|
||||||
`request_uri` being utilized by a client.
|
|
||||||
|
|
||||||
### cors
|
|
||||||
|
|
||||||
Some [OpenID Connect 1.0] Endpoints need to allow cross-origin resource sharing, however some are optional. This section allows
|
|
||||||
you to configure the optional parts. We reply with CORS headers when the request includes the Origin header.
|
|
||||||
|
|
||||||
#### endpoints
|
|
||||||
|
|
||||||
{{< confkey type="list(string)" required="no" >}}
|
|
||||||
|
|
||||||
A list of endpoints to configure with cross-origin resource sharing headers. It is recommended that the `userinfo`
|
|
||||||
option is at least in this list. The potential endpoints which this can be enabled on are as follows:
|
|
||||||
|
|
||||||
* authorization
|
|
||||||
* pushed-authorization-request
|
|
||||||
* token
|
|
||||||
* revocation
|
|
||||||
* introspection
|
|
||||||
* userinfo
|
|
||||||
|
|
||||||
#### allowed_origins
|
|
||||||
|
|
||||||
{{< confkey type="list(string)" required="no" >}}
|
|
||||||
|
|
||||||
A list of permitted origins.
|
|
||||||
|
|
||||||
Any origin with https is permitted unless this option is configured or the
|
|
||||||
[allowed_origins_from_client_redirect_uris](#allowed_origins_from_client_redirect_uris) option is enabled. This means
|
|
||||||
you must configure this option manually if you want http endpoints to be permitted to make cross-origin requests to the
|
|
||||||
[OpenID Connect 1.0] endpoints, however this is not recommended.
|
|
||||||
|
|
||||||
Origins must only have the scheme, hostname and port, they may not have a trailing slash or path.
|
|
||||||
|
|
||||||
In addition to an Origin URI, you may specify the wildcard origin in the allowed_origins. It MUST be specified by itself
|
|
||||||
and the [allowed_origins_from_client_redirect_uris](#allowedoriginsfromclientredirecturis) MUST NOT be enabled. The
|
|
||||||
wildcard origin is denoted as `*`. Examples:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
identity_providers:
|
|
||||||
oidc:
|
|
||||||
cors:
|
|
||||||
allowed_origins: "*"
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
identity_providers:
|
|
||||||
oidc:
|
|
||||||
cors:
|
|
||||||
allowed_origins:
|
|
||||||
- "*"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### allowed_origins_from_client_redirect_uris
|
|
||||||
|
|
||||||
{{< confkey type="boolean" default="false" required="no" >}}
|
|
||||||
|
|
||||||
Automatically adds the origin portion of all redirect URI's on all clients to the list of
|
|
||||||
[allowed_origins](#allowed_origins), provided they have the scheme http or https and do not have the hostname of
|
|
||||||
localhost.
|
|
||||||
|
|
||||||
### clients
|
|
||||||
|
|
||||||
{{< confkey type="list" required="yes" >}}
|
|
||||||
|
|
||||||
A list of clients to configure. The options for each client are described below.
|
|
||||||
|
|
||||||
#### id
|
|
||||||
|
|
||||||
{{< confkey type="string" required="yes" >}}
|
|
||||||
|
|
||||||
The Client ID for this client. It must exactly match the Client ID configured in the application
|
|
||||||
consuming this client.
|
|
||||||
|
|
||||||
#### description
|
|
||||||
|
|
||||||
{{< confkey type="string" default="*same as id*" required="no" >}}
|
|
||||||
|
|
||||||
A friendly description for this client shown in the UI. This defaults to the same as the ID.
|
|
||||||
|
|
||||||
#### secret
|
|
||||||
|
|
||||||
{{< confkey type="string" required="situational" >}}
|
|
||||||
|
|
||||||
The shared secret between Authelia and the application consuming this client. This secret must match the secret
|
|
||||||
configured in the application.
|
|
||||||
|
|
||||||
This secret must be generated by the administrator and can be done by following the
|
|
||||||
[How Do I Generate Client Secrets](../../integration/openid-connect/frequently-asked-questions.md#how-do-i-generate-client-secrets) FAQ.
|
|
||||||
|
|
||||||
This must be provided when the client is a confidential client type, and must be blank when using the public client
|
|
||||||
type. To set the client type to public see the [public](#public) configuration option.
|
|
||||||
|
|
||||||
#### sector_identifier
|
|
||||||
|
|
||||||
{{< confkey type="string" required="no" >}}
|
|
||||||
|
|
||||||
*__Important Note:__ because adjusting this option will inevitably change the `sub` claim of all tokens generated for
|
|
||||||
the specified client, changing this should cause the relying party to detect all future authorizations as completely new
|
|
||||||
users.*
|
|
||||||
|
|
||||||
Must be an empty string or the host component of a URL. This is commonly just the domain name, but may also include a
|
|
||||||
port.
|
|
||||||
|
|
||||||
Authelia utilizes UUID version 4 subject identifiers. By default the public [Subject Identifier Type] is utilized for
|
|
||||||
all clients. This means the subject identifiers will be the same for all clients. This configuration option enables
|
|
||||||
[Pairwise Identifier Algorithm] for this client, and configures the sector identifier utilized for both the storage and
|
|
||||||
the lookup of the subject identifier.
|
|
||||||
|
|
||||||
1. All clients who do not have this configured will generate the same subject identifier for a particular user
|
|
||||||
regardless of which client obtains the ID token.
|
|
||||||
2. All clients which have the same sector identifier will:
|
|
||||||
1. have the same subject identifier for a particular user when compared to clients with the same sector identifier.
|
|
||||||
2. have a completely different subject identifier for a particular user whe compared to:
|
|
||||||
1. any client with the public subject identifier type.
|
|
||||||
2. any client with a differing sector identifier.
|
|
||||||
|
|
||||||
In specific but limited scenarios this option is beneficial for privacy reasons. In particular this is useful when the
|
|
||||||
party utilizing the *Authelia* [OpenID Connect 1.0] Authorization Server is foreign and not controlled by the user. It would
|
|
||||||
prevent the third party utilizing the subject identifier with another third party in order to track the user.
|
|
||||||
|
|
||||||
Keep in mind depending on the other claims they may still be able to perform this tracking and it is not a silver
|
|
||||||
bullet. There are very few benefits when utilizing this in a homelab or business where no third party is utilizing
|
|
||||||
the server.
|
|
||||||
|
|
||||||
#### public
|
|
||||||
|
|
||||||
{{< confkey type="bool" default="false" required="no" >}}
|
|
||||||
|
|
||||||
This enables the public client type for this client. This is for clients that are not capable of maintaining
|
|
||||||
confidentiality of credentials, you can read more about client types in [RFC6749 Section 2.1]. This is particularly
|
|
||||||
useful for SPA's and CLI tools. This option requires setting the [client secret](#secret) to a blank string.
|
|
||||||
|
|
||||||
#### redirect_uris
|
|
||||||
|
|
||||||
{{< confkey type="list(string)" required="yes" >}}
|
|
||||||
|
|
||||||
A list of valid callback URIs this client will redirect to. All other callbacks will be considered unsafe. The URIs are
|
|
||||||
case-sensitive and they differ from application to application - the community has provided
|
|
||||||
[a list of URL´s for common applications](../../integration/openid-connect/introduction.md).
|
|
||||||
|
|
||||||
Some restrictions that have been placed on clients and
|
|
||||||
their redirect URIs are as follows:
|
|
||||||
|
|
||||||
1. If a client attempts to authorize with Authelia and its redirect URI is not listed in the client configuration the
|
|
||||||
attempt to authorize will fail and an error will be generated.
|
|
||||||
2. The redirect URIs are case-sensitive.
|
|
||||||
3. The URI must include a scheme and that scheme must be one of `http` or `https`.
|
|
||||||
|
|
||||||
#### audience
|
|
||||||
|
|
||||||
{{< confkey type="list(string)" required="no" >}}
|
|
||||||
|
|
||||||
A list of audiences this client is allowed to request.
|
|
||||||
|
|
||||||
#### scopes
|
|
||||||
|
|
||||||
{{< confkey type="list(string)" default="openid, groups, profile, email" required="no" >}}
|
|
||||||
|
|
||||||
A list of scopes to allow this client to consume. See
|
|
||||||
[scope definitions](../../integration/openid-connect/introduction.md#scope-definitions) for more information. The
|
|
||||||
documentation for the application you are trying to configure [OpenID Connect 1.0] for will likely have a list of scopes
|
|
||||||
or claims required which can be matched with the above guide.
|
|
||||||
|
|
||||||
#### response_types
|
|
||||||
|
|
||||||
{{< confkey type="list(string)" default="code" required="no" >}}
|
|
||||||
|
|
||||||
*__Security Note:__ It is recommended that only the `code` response type (i.e. the default) is used. The other response
|
|
||||||
types are not as secure as this response type.*
|
|
||||||
|
|
||||||
A list of response types this client supports. If a response type not in this list is requested by a client then an
|
|
||||||
error will be returned to the client. The response type indicates the types of values that are returned to the client.
|
|
||||||
|
|
||||||
See the [Response Types](../../integration/openid-connect/introduction.md#response-types) section of the
|
|
||||||
[OpenID Connect 1.0 Integration Guide](../../integration/openid-connect/introduction.md#response-types) for more information.
|
|
||||||
|
|
||||||
#### response_modes
|
|
||||||
|
|
||||||
{{< confkey type="list(string)" default="form_post, query" required="no" >}}
|
|
||||||
|
|
||||||
*__Important Note:__ It is recommended that this isn't configured at this time unless you know what you're doing.*
|
|
||||||
|
|
||||||
A list of response modes this client supports. If a response mode not in this list is requested by a client then an
|
|
||||||
error will be returned to the client. The response mode controls how the response type is returned to the client.
|
|
||||||
|
|
||||||
See the [Response Modes](../../integration/openid-connect/introduction.md#response-modes) section of the
|
|
||||||
[OpenID Connect 1.0 Integration Guide](../../integration/openid-connect/introduction.md#response-modes) for more
|
|
||||||
information.
|
|
||||||
|
|
||||||
The default values are based on the [response_types](#responsetypes) values. When the [response_types](#responsetypes)
|
|
||||||
values include the `code` type then the `query` response mode will be included. When any other type is included the
|
|
||||||
`fragment` response mode will be included. It's important to note at this time we do not support the `none` response
|
|
||||||
type, but when it is supported it will include the `query` response mode.
|
|
||||||
|
|
||||||
#### grant_types
|
|
||||||
|
|
||||||
{{< confkey type="list(string)" default="authorization_code" required="no" >}}
|
|
||||||
|
|
||||||
*__Important Note:__ It is recommended that this isn't configured at this time unless you know what you're doing.*
|
|
||||||
|
|
||||||
The list of grant types this client is permitted to use in order to obtain access to the relevant tokens.
|
|
||||||
|
|
||||||
See the [Grant Types](../../integration/openid-connect/introduction.md#grant-types) section of the
|
|
||||||
[OpenID Connect 1.0 Integration Guide](../../integration/openid-connect/introduction.md#grant-types) for more information.
|
|
||||||
|
|
||||||
#### authorization_policy
|
|
||||||
|
|
||||||
{{< confkey type="string" default="two_factor" required="no" >}}
|
|
||||||
|
|
||||||
The authorization policy for this client: either `one_factor` or `two_factor`.
|
|
||||||
|
|
||||||
#### enforce_par
|
|
||||||
|
|
||||||
{{< confkey type="boolean" default="false" required="no" >}}
|
|
||||||
|
|
||||||
Enforces the use of a [Pushed Authorization Requests] flow for this client.
|
|
||||||
|
|
||||||
#### enforce_pkce
|
|
||||||
|
|
||||||
{{< confkey type="bool" default="false" required="no" >}}
|
|
||||||
|
|
||||||
This setting enforces the use of [PKCE] for this individual client. To enforce it for all clients see the global
|
|
||||||
[enforce_pkce](#enforcepkce) setting.
|
|
||||||
|
|
||||||
#### pkce_challenge_method
|
|
||||||
|
|
||||||
{{< confkey type="string" default="" required="no" >}}
|
|
||||||
|
|
||||||
This setting enforces the use of the specified [PKCE] challenge method for this individual client. This setting also
|
|
||||||
effectively enables the [enforce_pkce](#enforcepkce-1) option for this client.
|
|
||||||
|
|
||||||
Valid values are an empty string, `plain`, or `S256`. It should be noted that `S256` is strongly recommended if the
|
|
||||||
relying party supports it.
|
|
||||||
|
|
||||||
#### userinfo_signing_algorithm
|
|
||||||
|
|
||||||
{{< confkey type="string" default="none" required="no" >}}
|
|
||||||
|
|
||||||
The algorithm used to sign the userinfo endpoint responses. This can either be `none` or `RS256`.
|
|
||||||
|
|
||||||
See the [integration guide](../../integration/openid-connect/introduction.md#user-information-signing-algorithm) for
|
|
||||||
more information.
|
|
||||||
|
|
||||||
#### token_endpoint_auth_method
|
|
||||||
|
|
||||||
{{< confkey type="string" default="" required="no" >}}
|
|
||||||
|
|
||||||
The registered client authentication mechanism used by this client for the [Token Endpoint]. If no method is defined
|
|
||||||
the confidential client type will accept any supported method. The public client type defaults to `none` as this
|
|
||||||
is required by the specification. This may be required as a breaking change in future versions.
|
|
||||||
Supported values are `client_secret_basic`, `client_secret_post`, `client_secret_jwt`, and `none`.
|
|
||||||
|
|
||||||
See the [integration guide](../../integration/openid-connect/introduction.md#client-authentication-method) for
|
|
||||||
more information.
|
|
||||||
|
|
||||||
#### token_endpoint_auth_signing_alg
|
|
||||||
|
|
||||||
{{< confkey type="string" default="HS256" required="no" >}}
|
|
||||||
|
|
||||||
The JWT signing algorithm accepted when the [token_endpoint_auth_method](#tokenendpointauthmethod) is configured as
|
|
||||||
`client_secret_jwt`. Supported values are `HS256`, `HS385`, and `HS512`.
|
|
||||||
|
|
||||||
#### consent_mode
|
|
||||||
|
|
||||||
{{< confkey type="string" default="auto" required="no" >}}
|
|
||||||
|
|
||||||
*__Important Note:__ the `implicit` consent mode is not technically part of the specification. It theoretically could be
|
|
||||||
misused in certain conditions specifically with the public client type or when the client credentials (i.e. client
|
|
||||||
secret) has been exposed to an attacker. For these reasons this mode is discouraged.*
|
|
||||||
|
|
||||||
Configures the consent mode. The following table describes the different modes:
|
|
||||||
|
|
||||||
| Value | Description |
|
|
||||||
|:--------------:|:----------------------------------------------------------------------------------------------------------------------------------------------:|
|
|
||||||
| auto | Automatically determined (default). Uses `explicit` unless [pre_configured_consent_duration] is specified in which case uses `pre-configured`. |
|
|
||||||
| explicit | Requires the user provide unique explicit consent for every authorization. |
|
|
||||||
| implicit | Automatically assumes consent for every authorization, never asking the user if they wish to give consent. |
|
|
||||||
| pre-configured | Allows the end-user to remember their consent for the [pre_configured_consent_duration]. |
|
|
||||||
|
|
||||||
[pre_configured_consent_duration]: #preconfiguredconsentduration
|
|
||||||
|
|
||||||
#### pre_configured_consent_duration
|
|
||||||
|
|
||||||
{{< confkey type="duration" default="1w" required="no" >}}
|
|
||||||
|
|
||||||
*__Note:__ This setting uses the [duration notation format](../prologue/common.md#duration-notation-format). Please see
|
|
||||||
the [common options](../prologue/common.md#duration-notation-format) documentation for information on this format.*
|
|
||||||
|
|
||||||
Specifying this in the configuration without a consent [consent_mode] enables the `pre-configured` mode. If this is
|
|
||||||
specified as well as the [consent_mode] then it only has an effect if the [consent_mode] is `pre-configured` or `auto`.
|
|
||||||
|
|
||||||
The period of time dictates how long a users choice to remember the pre-configured consent lasts.
|
|
||||||
|
|
||||||
Pre-configured consents are only valid if the subject, client id are exactly the same and the requested scopes/audience
|
|
||||||
match exactly with the granted scopes/audience.
|
|
||||||
|
|
||||||
[consent_mode]: #consentmode
|
|
||||||
|
|
||||||
## Integration
|
|
||||||
|
|
||||||
To integrate Authelia's [OpenID Connect 1.0] implementation with a relying party please see the
|
|
||||||
[integration docs](../../integration/openid-connect/introduction.md).
|
|
||||||
|
|
||||||
[token lifespan]: https://docs.apigee.com/api-platform/antipatterns/oauth-long-expiration
|
|
||||||
[OpenID Connect 1.0]: https://openid.net/connect/
|
|
||||||
[Token Endpoint]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
||||||
[JWT]: https://datatracker.ietf.org/doc/html/rfc7519
|
|
||||||
[RFC6234]: https://datatracker.ietf.org/doc/html/rfc6234
|
|
||||||
[RFC4648]: https://datatracker.ietf.org/doc/html/rfc4648
|
|
||||||
[RFC7468]: https://datatracker.ietf.org/doc/html/rfc7468
|
|
||||||
[RFC6749 Section 2.1]: https://datatracker.ietf.org/doc/html/rfc6749#section-2.1
|
|
||||||
[PKCE]: https://datatracker.ietf.org/doc/html/rfc7636
|
|
||||||
[Authorization Code Flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
|
|
||||||
[Subject Identifier Type]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
|
|
||||||
[Pairwise Identifier Algorithm]: https://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg
|
|
||||||
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
title: "OpenID Connect 1.0"
|
||||||
|
description: ""
|
||||||
|
lead: ""
|
||||||
|
date: 2023-05-08T13:38:08+10:00
|
||||||
|
lastmod: 2022-01-18T20:07:56+01:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: "identity-providers"
|
||||||
|
identifier: "openid-connect"
|
||||||
|
weight: 190120
|
||||||
|
toc: true
|
||||||
|
---
|
|
@ -0,0 +1,413 @@
|
||||||
|
---
|
||||||
|
title: "OpenID Connect 1.0 Clients"
|
||||||
|
description: "OpenID Connect 1.0 Registered Clients Configuration"
|
||||||
|
lead: "Authelia can operate as an OpenID Connect 1.0 Provider. This section describes how to configure the registered clients."
|
||||||
|
date: 2023-05-08T13:38:08+10:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
menu:
|
||||||
|
configuration:
|
||||||
|
parent: "openid-connect"
|
||||||
|
weight: 190220
|
||||||
|
toc: true
|
||||||
|
---
|
||||||
|
|
||||||
|
This section covers specifics regarding configuring the providers registered clients for [OpenID Connect 1.0]. For the
|
||||||
|
provider specific configuration and information not related to clients see the [OpenID Connect 1.0 Provider](provider.md)
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
More information about OpenID Connect can be found in the [roadmap](../../../roadmap/active/openid-connect.md) and in the
|
||||||
|
[integration](../../../integration/openid-connect/introduction.md) documentation.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The following snippet provides a configuration example for the [OpenID Connect 1.0] Registered Clients. This is not
|
||||||
|
intended for production use it's used to provide context and an indentation example.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
identity_providers:
|
||||||
|
oidc:
|
||||||
|
clients:
|
||||||
|
- id: myapp
|
||||||
|
description: My Application
|
||||||
|
secret: '$pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng' # The digest of 'insecure_secret'.
|
||||||
|
sector_identifier: ''
|
||||||
|
public: false
|
||||||
|
redirect_uris:
|
||||||
|
- https://oidc.example.com:8080/oauth2/callback
|
||||||
|
audience: []
|
||||||
|
scopes:
|
||||||
|
- openid
|
||||||
|
- groups
|
||||||
|
- email
|
||||||
|
- profile
|
||||||
|
grant_types:
|
||||||
|
- refresh_token
|
||||||
|
- authorization_code
|
||||||
|
response_types:
|
||||||
|
- code
|
||||||
|
response_modes:
|
||||||
|
- form_post
|
||||||
|
- query
|
||||||
|
- fragment
|
||||||
|
authorization_policy: two_factor
|
||||||
|
consent_mode: explicit
|
||||||
|
pre_configured_consent_duration: 1w
|
||||||
|
enforce_par: false
|
||||||
|
enforce_pkce: false
|
||||||
|
pkce_challenge_method: S256
|
||||||
|
token_endpoint_auth_method: ''
|
||||||
|
token_endpoint_auth_signing_alg: RS256
|
||||||
|
id_token_signing_alg: RS256
|
||||||
|
request_object_signing_alg: RS256
|
||||||
|
userinfo_signing_alg: none
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### id
|
||||||
|
|
||||||
|
{{< confkey type="string" required="yes" >}}
|
||||||
|
|
||||||
|
The Client ID for this client. It must exactly match the Client ID configured in the application consuming this client.
|
||||||
|
|
||||||
|
### description
|
||||||
|
|
||||||
|
{{< confkey type="string" default="*same as id*" required="no" >}}
|
||||||
|
|
||||||
|
A friendly description for this client shown in the UI. This defaults to the same as the ID.
|
||||||
|
|
||||||
|
### secret
|
||||||
|
|
||||||
|
{{< confkey type="string" required="situational" >}}
|
||||||
|
|
||||||
|
The shared secret between Authelia and the application consuming this client. This secret must match the secret
|
||||||
|
configured in the application.
|
||||||
|
|
||||||
|
This secret must be generated by the administrator and can be done by following the
|
||||||
|
[How Do I Generate Client Secrets](../../../integration/openid-connect/frequently-asked-questions.md#how-do-i-generate-client-secrets) FAQ.
|
||||||
|
|
||||||
|
This must be provided when the client is a confidential client type, and must be blank when using the public client
|
||||||
|
type. To set the client type to public see the [public](#public) configuration option.
|
||||||
|
|
||||||
|
### sector_identifier
|
||||||
|
|
||||||
|
{{< confkey type="string" required="no" >}}
|
||||||
|
|
||||||
|
*__Important Note:__ because adjusting this option will inevitably change the `sub` claim of all tokens generated for
|
||||||
|
the specified client, changing this should cause the relying party to detect all future authorizations as completely new
|
||||||
|
users.*
|
||||||
|
|
||||||
|
Must be an empty string or the host component of a URL. This is commonly just the domain name, but may also include a
|
||||||
|
port.
|
||||||
|
|
||||||
|
Authelia utilizes UUID version 4 subject identifiers. By default the public [Subject Identifier Type] is utilized for
|
||||||
|
all clients. This means the subject identifiers will be the same for all clients. This configuration option enables
|
||||||
|
[Pairwise Identifier Algorithm] for this client, and configures the sector identifier utilized for both the storage and
|
||||||
|
the lookup of the subject identifier.
|
||||||
|
|
||||||
|
1. All clients who do not have this configured will generate the same subject identifier for a particular user
|
||||||
|
regardless of which client obtains the ID token.
|
||||||
|
2. All clients which have the same sector identifier will:
|
||||||
|
1. have the same subject identifier for a particular user when compared to clients with the same sector identifier.
|
||||||
|
2. have a completely different subject identifier for a particular user whe compared to:
|
||||||
|
1. any client with the public subject identifier type.
|
||||||
|
2. any client with a differing sector identifier.
|
||||||
|
|
||||||
|
In specific but limited scenarios this option is beneficial for privacy reasons. In particular this is useful when the
|
||||||
|
party utilizing the *Authelia* [OpenID Connect 1.0] Authorization Server is foreign and not controlled by the user. It would
|
||||||
|
prevent the third party utilizing the subject identifier with another third party in order to track the user.
|
||||||
|
|
||||||
|
Keep in mind depending on the other claims they may still be able to perform this tracking and it is not a silver
|
||||||
|
bullet. There are very few benefits when utilizing this in a homelab or business where no third party is utilizing
|
||||||
|
the server.
|
||||||
|
|
||||||
|
### public
|
||||||
|
|
||||||
|
{{< confkey type="bool" default="false" required="no" >}}
|
||||||
|
|
||||||
|
This enables the public client type for this client. This is for clients that are not capable of maintaining
|
||||||
|
confidentiality of credentials, you can read more about client types in [RFC6749 Section 2.1]. This is particularly
|
||||||
|
useful for SPA's and CLI tools. This option requires setting the [client secret](#secret) to a blank string.
|
||||||
|
|
||||||
|
### redirect_uris
|
||||||
|
|
||||||
|
{{< confkey type="list(string)" required="yes" >}}
|
||||||
|
|
||||||
|
A list of valid callback URIs this client will redirect to. All other callbacks will be considered unsafe. The URIs are
|
||||||
|
case-sensitive and they differ from application to application - the community has provided
|
||||||
|
[a list of URL´s for common applications](../../../integration/openid-connect/introduction.md).
|
||||||
|
|
||||||
|
Some restrictions that have been placed on clients and
|
||||||
|
their redirect URIs are as follows:
|
||||||
|
|
||||||
|
1. If a client attempts to authorize with Authelia and its redirect URI is not listed in the client configuration the
|
||||||
|
attempt to authorize will fail and an error will be generated.
|
||||||
|
2. The redirect URIs are case-sensitive.
|
||||||
|
3. The URI must include a scheme and that scheme must be one of `http` or `https`.
|
||||||
|
|
||||||
|
### audience
|
||||||
|
|
||||||
|
{{< confkey type="list(string)" required="no" >}}
|
||||||
|
|
||||||
|
A list of audiences this client is allowed to request.
|
||||||
|
|
||||||
|
### scopes
|
||||||
|
|
||||||
|
{{< confkey type="list(string)" default="openid, groups, profile, email" required="no" >}}
|
||||||
|
|
||||||
|
A list of scopes to allow this client to consume. See
|
||||||
|
[scope definitions](../../../integration/openid-connect/introduction.md#scope-definitions) for more information. The
|
||||||
|
documentation for the application you are trying to configure [OpenID Connect 1.0] for will likely have a list of scopes
|
||||||
|
or claims required which can be matched with the above guide.
|
||||||
|
|
||||||
|
### grant_types
|
||||||
|
|
||||||
|
{{< confkey type="list(string)" default="authorization_code" required="no" >}}
|
||||||
|
|
||||||
|
*__Important Note:__ It is recommended that this isn't configured at this time unless you know what you're doing.*
|
||||||
|
|
||||||
|
The list of grant types this client is permitted to use in order to obtain access to the relevant tokens.
|
||||||
|
|
||||||
|
See the [Grant Types](../../../integration/openid-connect/introduction.md#grant-types) section of the
|
||||||
|
[OpenID Connect 1.0 Integration Guide](../../../integration/openid-connect/introduction.md#grant-types) for more information.
|
||||||
|
|
||||||
|
### response_types
|
||||||
|
|
||||||
|
{{< confkey type="list(string)" default="code" required="no" >}}
|
||||||
|
|
||||||
|
*__Security Note:__ It is recommended that only the `code` response type (i.e. the default) is used. The other response
|
||||||
|
types are not as secure as this response type.*
|
||||||
|
|
||||||
|
A list of response types this client supports. If a response type not in this list is requested by a client then an
|
||||||
|
error will be returned to the client. The response type indicates the types of values that are returned to the client.
|
||||||
|
|
||||||
|
See the [Response Types](../../../integration/openid-connect/introduction.md#response-types) section of the
|
||||||
|
[OpenID Connect 1.0 Integration Guide](../../../integration/openid-connect/introduction.md#response-types) for more information.
|
||||||
|
|
||||||
|
### response_modes
|
||||||
|
|
||||||
|
{{< confkey type="list(string)" default="form_post, query" required="no" >}}
|
||||||
|
|
||||||
|
*__Important Note:__ It is recommended that this isn't configured at this time unless you know what you're doing.*
|
||||||
|
|
||||||
|
A list of response modes this client supports. If a response mode not in this list is requested by a client then an
|
||||||
|
error will be returned to the client. The response mode controls how the response type is returned to the client.
|
||||||
|
|
||||||
|
See the [Response Modes](../../../integration/openid-connect/introduction.md#response-modes) section of the
|
||||||
|
[OpenID Connect 1.0 Integration Guide](../../../integration/openid-connect/introduction.md#response-modes) for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
The default values are based on the [response_types](#responsetypes) values. When the [response_types](#responsetypes)
|
||||||
|
values include the `code` type then the `query` response mode will be included. When any other type is included the
|
||||||
|
`fragment` response mode will be included. It's important to note at this time we do not support the `none` response
|
||||||
|
type, but when it is supported it will include the `query` response mode.
|
||||||
|
|
||||||
|
### authorization_policy
|
||||||
|
|
||||||
|
{{< confkey type="string" default="two_factor" required="no" >}}
|
||||||
|
|
||||||
|
The authorization policy for this client: either `one_factor` or `two_factor`.
|
||||||
|
|
||||||
|
### consent_mode
|
||||||
|
|
||||||
|
{{< confkey type="string" default="auto" required="no" >}}
|
||||||
|
|
||||||
|
*__Important Note:__ the `implicit` consent mode is not technically part of the specification. It theoretically could be
|
||||||
|
misused in certain conditions specifically with the public client type or when the client credentials (i.e. client
|
||||||
|
secret) has been exposed to an attacker. For these reasons this mode is discouraged.*
|
||||||
|
|
||||||
|
Configures the consent mode. The following table describes the different modes:
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|:--------------:|:----------------------------------------------------------------------------------------------------------------------------------------------:|
|
||||||
|
| auto | Automatically determined (default). Uses `explicit` unless [pre_configured_consent_duration] is specified in which case uses `pre-configured`. |
|
||||||
|
| explicit | Requires the user provide unique explicit consent for every authorization. |
|
||||||
|
| implicit | Automatically assumes consent for every authorization, never asking the user if they wish to give consent. |
|
||||||
|
| pre-configured | Allows the end-user to remember their consent for the [pre_configured_consent_duration]. |
|
||||||
|
|
||||||
|
[pre_configured_consent_duration]: #preconfiguredconsentduration
|
||||||
|
|
||||||
|
### pre_configured_consent_duration
|
||||||
|
|
||||||
|
{{< confkey type="duration" default="1w" required="no" >}}
|
||||||
|
|
||||||
|
*__Note:__ This setting uses the [duration notation format](../../prologue/common.md#duration-notation-format). Please see
|
||||||
|
the [common options](../../prologue/common.md#duration-notation-format) documentation for information on this format.*
|
||||||
|
|
||||||
|
Specifying this in the configuration without a consent [consent_mode] enables the `pre-configured` mode. If this is
|
||||||
|
specified as well as the [consent_mode] then it only has an effect if the [consent_mode] is `pre-configured` or `auto`.
|
||||||
|
|
||||||
|
The period of time dictates how long a users choice to remember the pre-configured consent lasts.
|
||||||
|
|
||||||
|
Pre-configured consents are only valid if the subject, client id are exactly the same and the requested scopes/audience
|
||||||
|
match exactly with the granted scopes/audience.
|
||||||
|
|
||||||
|
[consent_mode]: #consentmode
|
||||||
|
|
||||||
|
### enforce_par
|
||||||
|
|
||||||
|
{{< confkey type="boolean" default="false" required="no" >}}
|
||||||
|
|
||||||
|
This configuration option enforces the use of a [Pushed Authorization Requests] flow for this registered client.
|
||||||
|
To enforce it for all clients see the global [pushed_authorizations enforce](provider.md#enforce) provider configuration
|
||||||
|
option.
|
||||||
|
|
||||||
|
### enforce_pkce
|
||||||
|
|
||||||
|
{{< confkey type="bool" default="false" required="no" >}}
|
||||||
|
|
||||||
|
This configuration option enforces the use of [PKCE] for this registered client. To enforce it for all clients see the
|
||||||
|
global [enforce_pkce](provider.md#enforcepkce) provider configuration option.
|
||||||
|
|
||||||
|
### pkce_challenge_method
|
||||||
|
|
||||||
|
{{< confkey type="string" default="" required="no" >}}
|
||||||
|
|
||||||
|
This setting enforces the use of the specified [PKCE] challenge method for this individual client. This setting also
|
||||||
|
effectively enables the [enforce_pkce](#enforcepkce) option for this client.
|
||||||
|
|
||||||
|
Valid values are an empty string, `plain`, or `S256`. It should be noted that `S256` is strongly recommended if the
|
||||||
|
relying party supports it.
|
||||||
|
|
||||||
|
### token_endpoint_auth_method
|
||||||
|
|
||||||
|
{{< confkey type="string" default="" required="no" >}}
|
||||||
|
|
||||||
|
The registered client authentication mechanism used by this client for the [Token Endpoint]. If no method is defined
|
||||||
|
the confidential client type will accept any supported method. The public client type defaults to `none` as this
|
||||||
|
is required by the specification. This may be required as a breaking change in future versions.
|
||||||
|
Supported values are `client_secret_basic`, `client_secret_post`, `client_secret_jwt`, `private_key_jwt`, and `none`.
|
||||||
|
|
||||||
|
See the [integration guide](../../../integration/openid-connect/introduction.md#client-authentication-method) for
|
||||||
|
more information.
|
||||||
|
|
||||||
|
### token_endpoint_auth_signing_alg
|
||||||
|
|
||||||
|
{{< confkey type="string" default="RS256" required="no" >}}
|
||||||
|
|
||||||
|
The JWT signing algorithm accepted when the [token_endpoint_auth_method](#tokenendpointauthmethod) is configured as
|
||||||
|
`client_secret_jwt` or `private_key_jwt`.
|
||||||
|
|
||||||
|
See the request object section of the [integration guide](../../../integration/openid-connect/introduction.md#request-object)
|
||||||
|
for more information including the algorithm column for supported values.
|
||||||
|
|
||||||
|
It's recommended that you specifically configure this when the following options are configured to specific values
|
||||||
|
otherwise we assume the default value:
|
||||||
|
|
||||||
|
| Configuration Option | Value | Default |
|
||||||
|
|:----------------------------------------------------------:|:-------------------:|:-------:|
|
||||||
|
| [token_endpoint_auth_method](#tokenendpointauthsigningalg) | `private_key_jwt` | `RS256` |
|
||||||
|
| [token_endpoint_auth_method](#tokenendpointauthsigningalg) | `client_secret_jwt` | `HS256` |
|
||||||
|
|
||||||
|
### request_object_signing_alg
|
||||||
|
|
||||||
|
{{< confkey type="string" default="RSA256" required="no" >}}
|
||||||
|
|
||||||
|
The JWT signing algorithm accepted for request objects.
|
||||||
|
|
||||||
|
See the request object section of the [integration guide](../../../integration/openid-connect/introduction.md#request-object)
|
||||||
|
for more information including the algorithm column for supported values.
|
||||||
|
|
||||||
|
### id_token_signing_alg
|
||||||
|
|
||||||
|
{{< confkey type="string" default="RS256" required="no" >}}
|
||||||
|
|
||||||
|
The algorithm used to sign the ID Tokens in the token responses.
|
||||||
|
|
||||||
|
See the response object section of the [integration guide](../../../integration/openid-connect/introduction.md#response-object)
|
||||||
|
for more information including the algorithm column for supported values. In addition to the values listed we also
|
||||||
|
support `none` as a value for this endpoint.
|
||||||
|
|
||||||
|
### userinfo_signing_alg
|
||||||
|
|
||||||
|
{{< confkey type="string" default="none" required="no" >}}
|
||||||
|
|
||||||
|
The algorithm used to sign the userinfo endpoint responses.
|
||||||
|
|
||||||
|
See the response object section of the [integration guide](../../../integration/openid-connect/introduction.md#response-object)
|
||||||
|
for more information including the algorithm column for supported values. In addition to the values listed we also
|
||||||
|
support `none` as a value for this endpoint.
|
||||||
|
|
||||||
|
### public_keys
|
||||||
|
|
||||||
|
This section configures the trusted JSON Web Keys or JWKS for this registered client. This can either be static values
|
||||||
|
(recommended) or a URI using the `https` scheme. This section is situational required. These are used to validate the
|
||||||
|
[JWT] assertions from clients.
|
||||||
|
|
||||||
|
Required when the following options are configured:
|
||||||
|
|
||||||
|
- [request_object_signing_alg](#requestobjectsigningalg)
|
||||||
|
- [token_endpoint_auth_signing_alg](#tokenendpointauthsigningalg)
|
||||||
|
|
||||||
|
Required when the following options are configured to specific values:
|
||||||
|
|
||||||
|
- [token_endpoint_auth_method](#tokenendpointauthsigningalg): `private_key_jwt`
|
||||||
|
|
||||||
|
#### uri
|
||||||
|
|
||||||
|
{{< confkey type="string" required="no" >}}
|
||||||
|
|
||||||
|
The fully qualified, `https` scheme, and appropriately signed URI for the JWKS endpoint that implements
|
||||||
|
[RFC7517 Section 5](https://datatracker.ietf.org/doc/html/rfc7517#section-5). Must not be configured at the same time
|
||||||
|
as [values](#values). It's recommended that you do not configure this option, but statically configure [values](#values)
|
||||||
|
instead.
|
||||||
|
|
||||||
|
*__Important Note:__ the URL given in this value MUST be resolvable by Authelia and MUST present a certificate signed by
|
||||||
|
a certificate trusted by your environment. It is beyond our intentions to support anything other than this.*
|
||||||
|
|
||||||
|
#### values
|
||||||
|
|
||||||
|
{{< confkey type="list(object)" required="situational" >}}
|
||||||
|
|
||||||
|
A list of static keys.
|
||||||
|
|
||||||
|
##### key_id
|
||||||
|
|
||||||
|
{{< confkey type="string" required="yes" >}}
|
||||||
|
|
||||||
|
The Key ID used to match the request object's JWT header `kid` value against.
|
||||||
|
|
||||||
|
##### key
|
||||||
|
|
||||||
|
{{< confkey type="string" required="yes" >}}
|
||||||
|
|
||||||
|
The public key portion of the JSON Web Key
|
||||||
|
|
||||||
|
The public key the clients use to sign/encrypt the [OpenID Connect 1.0] asserted [JWT]'s. The key is generated by the
|
||||||
|
client application or the administrator of the client application.
|
||||||
|
|
||||||
|
The key *__MUST__*:
|
||||||
|
|
||||||
|
* Be a PEM block encoded in the DER base64 format ([RFC4648]).
|
||||||
|
* Be either:
|
||||||
|
* An RSA public key:
|
||||||
|
* With a key size of at least 2048 bits.
|
||||||
|
* An ECDSA public key with one of:
|
||||||
|
* A P-256 elliptical curve.
|
||||||
|
* A P-384 elliptical curve.
|
||||||
|
* A P-512 elliptical curve.
|
||||||
|
|
||||||
|
If the [issuer_certificate_chain](#issuercertificatechain) is provided the private key must include matching public
|
||||||
|
key data for the first certificate in the chain.
|
||||||
|
|
||||||
|
|
||||||
|
## Integration
|
||||||
|
|
||||||
|
To integrate Authelia's [OpenID Connect 1.0] implementation with a relying party please see the
|
||||||
|
[integration docs](../../../integration/openid-connect/introduction.md).
|
||||||
|
|
||||||
|
[token lifespan]: https://docs.apigee.com/api-platform/antipatterns/oauth-long-expiration
|
||||||
|
[OpenID Connect 1.0]: https://openid.net/connect/
|
||||||
|
[Token Endpoint]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
||||||
|
[JWT]: https://datatracker.ietf.org/doc/html/rfc7519
|
||||||
|
[RFC6234]: https://datatracker.ietf.org/doc/html/rfc6234
|
||||||
|
[RFC4648]: https://datatracker.ietf.org/doc/html/rfc4648
|
||||||
|
[RFC7468]: https://datatracker.ietf.org/doc/html/rfc7468
|
||||||
|
[RFC6749 Section 2.1]: https://datatracker.ietf.org/doc/html/rfc6749#section-2.1
|
||||||
|
[PKCE]: https://datatracker.ietf.org/doc/html/rfc7636
|
||||||
|
[Authorization Code Flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
|
||||||
|
[Subject Identifier Type]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
|
||||||
|
[Pairwise Identifier Algorithm]: https://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg
|
||||||
|
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126
|
||||||
|
|
|
@ -0,0 +1,428 @@
|
||||||
|
---
|
||||||
|
title: "OpenID Connect 1.0 Provider"
|
||||||
|
description: "OpenID Connect 1.0 Provider Configuration"
|
||||||
|
lead: "Authelia can operate as an OpenID Connect 1.0 Provider. This section describes how to configure this."
|
||||||
|
date: 2023-05-08T13:38:08+10:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
menu:
|
||||||
|
configuration:
|
||||||
|
parent: "openid-connect"
|
||||||
|
weight: 190200
|
||||||
|
toc: true
|
||||||
|
aliases:
|
||||||
|
- /c/oidc
|
||||||
|
- /docs/configuration/identity-providers/oidc.html
|
||||||
|
---
|
||||||
|
|
||||||
|
__Authelia__ currently supports the [OpenID Connect 1.0] Provider role as an open
|
||||||
|
[__beta__](../../../roadmap/active/openid-connect.md) feature. We currently do not support the [OpenID Connect 1.0] Relying
|
||||||
|
Party role. This means other applications that implement the [OpenID Connect 1.0] Relying Party role can use Authelia as
|
||||||
|
an [OpenID Connect 1.0] Provider similar to how you may use social media or development platforms for login.
|
||||||
|
|
||||||
|
The [OpenID Connect 1.0] Relying Party role is the role which allows an application to use GitHub, Google, or other
|
||||||
|
[OpenID Connect 1.0] Providers for authentication and authorization. We do not intend to support this functionality at
|
||||||
|
this moment in time.
|
||||||
|
|
||||||
|
This section covers the [OpenID Connect 1.0] Provider configuration. For information on configuring individual
|
||||||
|
registered clients see the [OpenID Connect 1.0 Clients](clients.md) documentation.
|
||||||
|
|
||||||
|
More information about the beta can be found in the [roadmap](../../../roadmap/active/openid-connect.md) and in the
|
||||||
|
[integration](../../../integration/openid-connect/introduction.md) documentation.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The following snippet provides a configuration example for the [OpenID Connect 1.0] Provider. This is not
|
||||||
|
intended for production use it's used to provide context and an indentation example.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
identity_providers:
|
||||||
|
oidc:
|
||||||
|
hmac_secret: this_is_a_secret_abc123abc123abc
|
||||||
|
issuer_private_keys:
|
||||||
|
- key_id: example
|
||||||
|
algorithm: RS256
|
||||||
|
use: sig
|
||||||
|
key: |
|
||||||
|
-----BEGIN RSA PUBLIC KEY-----
|
||||||
|
MEgCQQDAwV26ZA1lodtOQxNrJ491gWT+VzFum9IeZ+WTmMypYWyW1CzXKwsvTHDz
|
||||||
|
9ec+jserR3EMQ0Rr24lj13FL1ib5AgMBAAE=
|
||||||
|
-----END RSA PUBLIC KEY----
|
||||||
|
certificate_chain: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
issuer_private_key: |
|
||||||
|
-----BEGIN RSA PUBLIC KEY-----
|
||||||
|
MEgCQQDAwV26ZA1lodtOQxNrJ491gWT+VzFum9IeZ+WTmMypYWyW1CzXKwsvTHDz
|
||||||
|
9ec+jserR3EMQ0Rr24lj13FL1ib5AgMBAAE=
|
||||||
|
-----END RSA PUBLIC KEY----
|
||||||
|
issuer_certificate_chain: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
access_token_lifespan: 1h
|
||||||
|
authorize_code_lifespan: 1m
|
||||||
|
id_token_lifespan: 1h
|
||||||
|
refresh_token_lifespan: 90m
|
||||||
|
enable_client_debug_messages: false
|
||||||
|
minimum_parameter_entropy: 8
|
||||||
|
enforce_pkce: public_clients_only
|
||||||
|
enable_pkce_plain_challenge: false
|
||||||
|
pushed_authorizations:
|
||||||
|
enforce: false
|
||||||
|
context_lifespan: 5m
|
||||||
|
cors:
|
||||||
|
endpoints:
|
||||||
|
- authorization
|
||||||
|
- token
|
||||||
|
- revocation
|
||||||
|
- introspection
|
||||||
|
allowed_origins:
|
||||||
|
- https://example.com
|
||||||
|
allowed_origins_from_client_redirect_uris: false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### hmac_secret
|
||||||
|
|
||||||
|
{{< confkey type="string" required="yes" >}}
|
||||||
|
|
||||||
|
*__Important Note:__ This can also be defined using a [secret](../../methods/secrets.md) which is __strongly recommended__
|
||||||
|
especially for containerized deployments.*
|
||||||
|
|
||||||
|
The HMAC secret used to sign the [JWT]'s. The provided string is hashed to a SHA256 ([RFC6234]) byte string for the
|
||||||
|
purpose of meeting the required format.
|
||||||
|
|
||||||
|
It's __strongly recommended__ this is a
|
||||||
|
[Random Alphanumeric String](../../../reference/guides/generating-secure-values.md#generating-a-random-alphanumeric-string)
|
||||||
|
with 64 or more characters.
|
||||||
|
|
||||||
|
### issuer_private_keys
|
||||||
|
|
||||||
|
The key *__MUST__*:
|
||||||
|
|
||||||
|
* Be a PEM block encoded in the DER base64 format ([RFC4648]).
|
||||||
|
* Be either:
|
||||||
|
* An RSA public key:
|
||||||
|
* With a key size of at least 2048 bits.
|
||||||
|
* An ECDSA public key with one of:
|
||||||
|
* A P-256 elliptical curve.
|
||||||
|
* A P-384 elliptical curve.
|
||||||
|
* A P-512 elliptical curve.
|
||||||
|
|
||||||
|
### issuer_private_keys
|
||||||
|
|
||||||
|
{{< confkey type="list(object" required="no" >}}
|
||||||
|
|
||||||
|
The list of JWKS instead of or in addition to the [issuer_private_key](#issuerprivatekey) and
|
||||||
|
[issuer_certificate_chain](#issuercertificatechain). Can also accept ECDSA Private Key's and Certificates.
|
||||||
|
|
||||||
|
#### key_id
|
||||||
|
|
||||||
|
{{< confkey type="string" default="<thumbprint of public key>" required="no" >}}
|
||||||
|
|
||||||
|
Completely optional, and generally discouraged unless there is a collision between the automatically generated key id's.
|
||||||
|
If provided must be a unique string with 7 or less alphanumeric characters.
|
||||||
|
|
||||||
|
This value is the first 7 characters of the public key thumbprint (SHA1) encoded into hexadecimal.
|
||||||
|
|
||||||
|
#### algorithm
|
||||||
|
|
||||||
|
{{< confkey type="string" default="RS256" required="no" >}}
|
||||||
|
|
||||||
|
The algorithm for this key. This value must be unique. It's automatically detected based on the type of key.
|
||||||
|
|
||||||
|
See the response object table in the [integration guide](../../../integration/openid-connect/introduction.md#response-object)
|
||||||
|
including the algorithm column for the supported values and the key type column for the default algorithm value.
|
||||||
|
|
||||||
|
#### use
|
||||||
|
|
||||||
|
{{< confkey type="string" default="sig" required="no" >}}
|
||||||
|
|
||||||
|
The key usage. Defaults to `sig` which is the only available option at this time.
|
||||||
|
|
||||||
|
#### key
|
||||||
|
|
||||||
|
{{< confkey type="string" required="yes" >}}
|
||||||
|
|
||||||
|
The private key associated with this key entry.
|
||||||
|
|
||||||
|
The private key used to sign/encrypt the [OpenID Connect 1.0] issued [JWT]'s. The key must be generated by the administrator
|
||||||
|
and can be done by following the
|
||||||
|
[Generating an RSA Keypair](../../../reference/guides/generating-secure-values.md#generating-an-rsa-keypair) guide.
|
||||||
|
|
||||||
|
The private key *__MUST__*:
|
||||||
|
* Be a PEM block encoded in the DER base64 format ([RFC4648]).
|
||||||
|
* Be one of:
|
||||||
|
* An RSA key with a key size of at least 2048 bits.
|
||||||
|
* An ECDSA private key with one of the P-256, P-384, or P-521 elliptical curves.
|
||||||
|
|
||||||
|
If the [certificate_chain](#certificatechain) is provided the private key must include matching public
|
||||||
|
key data for the first certificate in the chain.
|
||||||
|
|
||||||
|
#### certificate_chain
|
||||||
|
|
||||||
|
{{< confkey type="string" required="no" >}}
|
||||||
|
|
||||||
|
The certificate chain/bundle to be used with the [key](#key) DER base64 ([RFC4648])
|
||||||
|
encoded PEM format used to sign/encrypt the [OpenID Connect 1.0] [JWT]'s. When configured it enables the [x5c] and [x5t]
|
||||||
|
JSON key's in the JWKs [Discoverable Endpoint](../../../integration/openid-connect/introduction.md#discoverable-endpoints)
|
||||||
|
as per [RFC7517].
|
||||||
|
|
||||||
|
[RFC7517]: https://datatracker.ietf.org/doc/html/rfc7517
|
||||||
|
[x5c]: https://datatracker.ietf.org/doc/html/rfc7517#section-4.7
|
||||||
|
[x5t]: https://datatracker.ietf.org/doc/html/rfc7517#section-4.8
|
||||||
|
|
||||||
|
The first certificate in the chain must have the public key for the [key](#key), each certificate in the chain must be
|
||||||
|
valid for the current date, and each certificate in the chain should be signed by the certificate immediately following
|
||||||
|
it if present.
|
||||||
|
|
||||||
|
### issuer_private_key
|
||||||
|
|
||||||
|
{{< confkey type="string" required="yes" >}}
|
||||||
|
|
||||||
|
*__Important Note:__ This can also be defined using a [secret](../../methods/secrets.md) which is __strongly recommended__
|
||||||
|
especially for containerized deployments.*
|
||||||
|
|
||||||
|
The private key used to sign/encrypt the [OpenID Connect 1.0] issued [JWT]'s. The key must be generated by the administrator
|
||||||
|
and can be done by following the
|
||||||
|
[Generating an RSA Keypair](../../../reference/guides/generating-secure-values.md#generating-an-rsa-keypair) guide.
|
||||||
|
|
||||||
|
This private key is automatically appended to the [issuer_private_keys](#issuerprivatekeys) and assumed to be for the
|
||||||
|
RS256 algorithm. As such no other key in this list should be RS256 if this is configured.
|
||||||
|
|
||||||
|
The issuer private key *__MUST__*:
|
||||||
|
|
||||||
|
* Be a PEM block encoded in the DER base64 format ([RFC4648]).
|
||||||
|
* Be an RSA private key:
|
||||||
|
* With a key size of at least 2048 bits.
|
||||||
|
|
||||||
|
If the [issuer_certificate_chain](#issuercertificatechain) is provided the private key must include matching public
|
||||||
|
key data for the first certificate in the chain.
|
||||||
|
|
||||||
|
### issuer_certificate_chain
|
||||||
|
|
||||||
|
{{< confkey type="string" required="no" >}}
|
||||||
|
|
||||||
|
The certificate chain/bundle to be used with the [issuer_private_key](#issuer_private_key) DER base64 ([RFC4648])
|
||||||
|
encoded PEM format used to sign/encrypt the [OpenID Connect 1.0] [JWT]'s. When configured it enables the [x5c] and [x5t]
|
||||||
|
JSON key's in the JWKs [Discoverable Endpoint](../../../integration/openid-connect/introduction.md#discoverable-endpoints)
|
||||||
|
as per [RFC7517].
|
||||||
|
|
||||||
|
[RFC7517]: https://datatracker.ietf.org/doc/html/rfc7517
|
||||||
|
[x5c]: https://datatracker.ietf.org/doc/html/rfc7517#section-4.7
|
||||||
|
[x5t]: https://datatracker.ietf.org/doc/html/rfc7517#section-4.8
|
||||||
|
|
||||||
|
The first certificate in the chain must have the public key for the [issuer_private_key](#issuerprivatekey), each
|
||||||
|
certificate in the chain must be valid for the current date, and each certificate in the chain should be signed by the
|
||||||
|
certificate immediately following it if present.
|
||||||
|
|
||||||
|
### access_token_lifespan
|
||||||
|
|
||||||
|
{{< confkey type="duration" default="1h" required="no" >}}
|
||||||
|
|
||||||
|
The maximum lifetime of an access token. It's generally recommended keeping this short similar to the default.
|
||||||
|
For more information read these docs about [token lifespan].
|
||||||
|
|
||||||
|
### authorize_code_lifespan
|
||||||
|
|
||||||
|
{{< confkey type="duration" default="1m" required="no" >}}
|
||||||
|
|
||||||
|
The maximum lifetime of an authorize code. This can be rather short, as the authorize code should only be needed to
|
||||||
|
obtain the other token types. For more information read these docs about [token lifespan].
|
||||||
|
|
||||||
|
### id_token_lifespan
|
||||||
|
|
||||||
|
{{< confkey type="duration" default="1h" required="no" >}}
|
||||||
|
|
||||||
|
The maximum lifetime of an ID token. For more information read these docs about [token lifespan].
|
||||||
|
|
||||||
|
### refresh_token_lifespan
|
||||||
|
|
||||||
|
{{< confkey type="string" default="90m" required="no" >}}
|
||||||
|
|
||||||
|
The maximum lifetime of a refresh token. The
|
||||||
|
refresh token can be used to obtain new refresh tokens as well as access tokens or id tokens with an
|
||||||
|
up-to-date expiration. For more information read these docs about [token lifespan].
|
||||||
|
|
||||||
|
A good starting point is 50% more or 30 minutes more (which ever is less) time than the highest lifespan out of the
|
||||||
|
[access token lifespan](#accesstokenlifespan), the [authorize code lifespan](#authorizecodelifespan), and the
|
||||||
|
[id token lifespan](#idtokenlifespan). For instance the default for all of these is 60 minutes, so the default refresh
|
||||||
|
token lifespan is 90 minutes.
|
||||||
|
|
||||||
|
### enable_client_debug_messages
|
||||||
|
|
||||||
|
{{< confkey type="boolean" default="false" required="no" >}}
|
||||||
|
|
||||||
|
Allows additional debug messages to be sent to the clients.
|
||||||
|
|
||||||
|
### minimum_parameter_entropy
|
||||||
|
|
||||||
|
{{< confkey type="integer" default="8" required="no" >}}
|
||||||
|
|
||||||
|
This controls the minimum length of the `nonce` and `state` parameters.
|
||||||
|
|
||||||
|
*__Security Notice:__* Changing this value is generally discouraged, reducing it from the default can theoretically
|
||||||
|
make certain scenarios less secure. It is highly encouraged that if your OpenID Connect RP does not send these
|
||||||
|
parameters or sends parameters with a lower length than the default that they implement a change rather than changing
|
||||||
|
this value.
|
||||||
|
|
||||||
|
### enforce_pkce
|
||||||
|
|
||||||
|
{{< confkey type="string" default="public_clients_only" required="no" >}}
|
||||||
|
|
||||||
|
[Proof Key for Code Exchange](https://datatracker.ietf.org/doc/html/rfc7636) enforcement policy: if specified, must be
|
||||||
|
either `never`, `public_clients_only` or `always`.
|
||||||
|
|
||||||
|
If set to `public_clients_only` (default), [PKCE] will be required for public clients using the
|
||||||
|
[Authorization Code Flow].
|
||||||
|
|
||||||
|
When set to `always`, [PKCE] will be required for all clients using the Authorization Code flow.
|
||||||
|
|
||||||
|
*__Security Notice:__* Changing this value to `never` is generally discouraged, reducing it from the default can
|
||||||
|
theoretically make certain client-side applications (mobile applications, SPA) vulnerable to CSRF and authorization code
|
||||||
|
interception attacks.
|
||||||
|
|
||||||
|
### enable_pkce_plain_challenge
|
||||||
|
|
||||||
|
{{< confkey type="boolean" default="false" required="no" >}}
|
||||||
|
|
||||||
|
Allows [PKCE] `plain` challenges when set to `true`.
|
||||||
|
|
||||||
|
*__Security Notice:__* Changing this value is generally discouraged. Applications should use the `S256` [PKCE] challenge
|
||||||
|
method instead.
|
||||||
|
|
||||||
|
### pushed_authorizations
|
||||||
|
|
||||||
|
Controls the behaviour of [Pushed Authorization Requests].
|
||||||
|
|
||||||
|
#### enforce
|
||||||
|
|
||||||
|
{{< confkey type="boolean" default="false" required="no" >}}
|
||||||
|
|
||||||
|
When enabled all authorization requests must use the [Pushed Authorization Requests] flow.
|
||||||
|
|
||||||
|
#### context_lifespan
|
||||||
|
|
||||||
|
{{< confkey type="duration" default="5m" required="no" >}}
|
||||||
|
|
||||||
|
The maximum amount of time between the [Pushed Authorization Requests] flow being initiated and the generated
|
||||||
|
`request_uri` being utilized by a client.
|
||||||
|
|
||||||
|
### cors
|
||||||
|
|
||||||
|
Some [OpenID Connect 1.0] Endpoints need to allow cross-origin resource sharing, however some are optional. This section allows
|
||||||
|
you to configure the optional parts. We reply with CORS headers when the request includes the Origin header.
|
||||||
|
|
||||||
|
#### endpoints
|
||||||
|
|
||||||
|
{{< confkey type="list(string)" required="no" >}}
|
||||||
|
|
||||||
|
A list of endpoints to configure with cross-origin resource sharing headers. It is recommended that the `userinfo`
|
||||||
|
option is at least in this list. The potential endpoints which this can be enabled on are as follows:
|
||||||
|
|
||||||
|
* authorization
|
||||||
|
* pushed-authorization-request
|
||||||
|
* token
|
||||||
|
* revocation
|
||||||
|
* introspection
|
||||||
|
* userinfo
|
||||||
|
|
||||||
|
#### allowed_origins
|
||||||
|
|
||||||
|
{{< confkey type="list(string)" required="no" >}}
|
||||||
|
|
||||||
|
A list of permitted origins.
|
||||||
|
|
||||||
|
Any origin with https is permitted unless this option is configured or the
|
||||||
|
[allowed_origins_from_client_redirect_uris](#allowedoriginsfromclientredirecturis) option is enabled. This means
|
||||||
|
you must configure this option manually if you want http endpoints to be permitted to make cross-origin requests to the
|
||||||
|
[OpenID Connect 1.0] endpoints, however this is not recommended.
|
||||||
|
|
||||||
|
Origins must only have the scheme, hostname and port, they may not have a trailing slash or path.
|
||||||
|
|
||||||
|
In addition to an Origin URI, you may specify the wildcard origin in the allowed_origins. It MUST be specified by itself
|
||||||
|
and the [allowed_origins_from_client_redirect_uris](#allowedoriginsfromclientredirecturis) MUST NOT be enabled. The
|
||||||
|
wildcard origin is denoted as `*`. Examples:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
identity_providers:
|
||||||
|
oidc:
|
||||||
|
cors:
|
||||||
|
allowed_origins: "*"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
identity_providers:
|
||||||
|
oidc:
|
||||||
|
cors:
|
||||||
|
allowed_origins:
|
||||||
|
- "*"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### allowed_origins_from_client_redirect_uris
|
||||||
|
|
||||||
|
{{< confkey type="boolean" default="false" required="no" >}}
|
||||||
|
|
||||||
|
Automatically adds the origin portion of all redirect URI's on all clients to the list of
|
||||||
|
[allowed_origins](#allowed_origins), provided they have the scheme http or https and do not have the hostname of
|
||||||
|
localhost.
|
||||||
|
|
||||||
|
### clients
|
||||||
|
|
||||||
|
See the [OpenID Connect 1.0 Registered Clients](clients.md) documentation for configuring clients.
|
||||||
|
|
||||||
|
## Integration
|
||||||
|
|
||||||
|
To integrate Authelia's [OpenID Connect 1.0] implementation with a relying party please see the
|
||||||
|
[integration docs](../../integration/openid-connect/introduction.md).
|
||||||
|
|
||||||
|
[token lifespan]: https://docs.apigee.com/api-platform/antipatterns/oauth-long-expiration
|
||||||
|
[OpenID Connect 1.0]: https://openid.net/connect/
|
||||||
|
[Token Endpoint]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
||||||
|
[JWT]: https://datatracker.ietf.org/doc/html/rfc7519
|
||||||
|
[RFC6234]: https://datatracker.ietf.org/doc/html/rfc6234
|
||||||
|
[RFC4648]: https://datatracker.ietf.org/doc/html/rfc4648
|
||||||
|
[RFC7468]: https://datatracker.ietf.org/doc/html/rfc7468
|
||||||
|
[RFC6749 Section 2.1]: https://datatracker.ietf.org/doc/html/rfc6749#section-2.1
|
||||||
|
[PKCE]: https://datatracker.ietf.org/doc/html/rfc7636
|
||||||
|
[Authorization Code Flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
|
||||||
|
[Subject Identifier Type]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
|
||||||
|
[Pairwise Identifier Algorithm]: https://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg
|
||||||
|
[Pushed Authorization Requests]: https://datatracker.ietf.org/doc/html/rfc9126
|
|
@ -77,9 +77,9 @@ other configuration using the environment but instead of loading a file the valu
|
||||||
[authentication_backend.ldap.password]: ../first-factor/ldap.md#password
|
[authentication_backend.ldap.password]: ../first-factor/ldap.md#password
|
||||||
[authentication_backend.ldap.tls.certificate_chain]: ../first-factor/ldap.md#tls
|
[authentication_backend.ldap.tls.certificate_chain]: ../first-factor/ldap.md#tls
|
||||||
[authentication_backend.ldap.tls.private_key]: ../first-factor/ldap.md#tls
|
[authentication_backend.ldap.tls.private_key]: ../first-factor/ldap.md#tls
|
||||||
[identity_providers.oidc.issuer_certificate_chain]: ../identity-providers/open-id-connect.md#issuercertificatechain
|
[identity_providers.oidc.issuer_certificate_chain]: ../identity-providers/openid-connect.md#issuercertificatechain
|
||||||
[identity_providers.oidc.issuer_private_key]: ../identity-providers/open-id-connect.md#issuerprivatekey
|
[identity_providers.oidc.issuer_private_key]: ../identity-providers/openid-connect.md#issuerprivatekey
|
||||||
[identity_providers.oidc.hmac_secret]: ../identity-providers/open-id-connect.md#hmacsecret
|
[identity_providers.oidc.hmac_secret]: ../identity-providers/openid-connect.md#hmacsecret
|
||||||
|
|
||||||
|
|
||||||
## Secrets in configuration file
|
## Secrets in configuration file
|
||||||
|
|
|
@ -44,7 +44,7 @@ accepted is recorded and checked in the browser
|
||||||
If the user has not accepted the policy they should not be able to interact with the Authelia UI via normal means.
|
If the user has not accepted the policy they should not be able to interact with the Authelia UI via normal means.
|
||||||
|
|
||||||
Administrators who are required to abide by the [GDPR] or other privacy laws should be advised that
|
Administrators who are required to abide by the [GDPR] or other privacy laws should be advised that
|
||||||
[OpenID Connect 1.0](../identity-providers/open-id-connect.md) clients configured with the `implicit` consent mode are
|
[OpenID Connect 1.0](../identity-providers/openid-connect.md) clients configured with the `implicit` consent mode are
|
||||||
unlikely to trigger the display of the Authelia UI if the user is already authenticated.
|
unlikely to trigger the display of the Authelia UI if the user is already authenticated.
|
||||||
|
|
||||||
We wont be adding checks like this to the `implicit` consent mode when that mode in particular is unlikely to be
|
We wont be adding checks like this to the `implicit` consent mode when that mode in particular is unlikely to be
|
||||||
|
|
|
@ -53,7 +53,7 @@ openid-groups-claim-type: groups
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with
|
||||||
[Apache Guacamole] which will operate with the above example:
|
[Apache Guacamole] which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -78,7 +78,7 @@ identity_providers:
|
||||||
- 'id_token'
|
- 'id_token'
|
||||||
grant_types:
|
grant_types:
|
||||||
- 'implicit'
|
- 'implicit'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -56,7 +56,7 @@ requestedScopes:
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Argo CD]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Argo CD]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -77,7 +77,7 @@ identity_providers:
|
||||||
- 'groups'
|
- 'groups'
|
||||||
- 'email'
|
- 'email'
|
||||||
- 'profile'
|
- 'profile'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
- id: 'argocd-cli'
|
- id: 'argocd-cli'
|
||||||
description: 'Argo CD (CLI)'
|
description: 'Argo CD (CLI)'
|
||||||
public: true
|
public: true
|
||||||
|
@ -90,7 +90,7 @@ identity_providers:
|
||||||
- 'email'
|
- 'email'
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'offline_access'
|
- 'offline_access'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -58,7 +58,7 @@ To configure [BookStack] to utilize Authelia as an [OpenID Connect 1.0] Provider
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [BookStack]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [BookStack]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -78,7 +78,7 @@ identity_providers:
|
||||||
- 'openid'
|
- 'openid'
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -66,7 +66,7 @@ To configure [Cloudflare Zero Trust] to utilize Authelia as an [OpenID Connect 1
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Cloudflare]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Cloudflare]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -86,7 +86,7 @@ identity_providers:
|
||||||
- 'openid'
|
- 'openid'
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -67,7 +67,7 @@ descriptions.
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Firezone] which
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Firezone] which
|
||||||
will operate with the above example:
|
will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -89,7 +89,7 @@ identity_providers:
|
||||||
- 'openid'
|
- 'openid'
|
||||||
- 'email'
|
- 'email'
|
||||||
- 'profile'
|
- 'profile'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -77,7 +77,7 @@ descriptions.
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Gitea] which
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Gitea] which
|
||||||
will operate with the above example:
|
will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -97,7 +97,7 @@ identity_providers:
|
||||||
- 'openid'
|
- 'openid'
|
||||||
- 'email'
|
- 'email'
|
||||||
- 'profile'
|
- 'profile'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -69,7 +69,7 @@ gitlab_rails['omniauth_providers'] = [
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [GitLab]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [GitLab]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -90,7 +90,7 @@ identity_providers:
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'groups'
|
- 'groups'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -87,7 +87,7 @@ Configure the following environment variables:
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Grafana]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Grafana]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -108,7 +108,7 @@ identity_providers:
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'groups'
|
- 'groups'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -60,7 +60,7 @@ To configure [Harbor] to utilize Authelia as an [OpenID Connect 1.0] Provider:
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Harbor]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Harbor]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -81,7 +81,7 @@ identity_providers:
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'groups'
|
- 'groups'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -43,7 +43,7 @@ To configure [HashiCorp Vault] to utilize Authelia as an [OpenID Connect 1.0] Pr
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [HashiCorp Vault]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [HashiCorp Vault]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -65,7 +65,7 @@ identity_providers:
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'groups'
|
- 'groups'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -18,8 +18,10 @@ Authelia can act as an [OpenID Connect 1.0] Provider as part of an open beta. Th
|
||||||
specifics that can be used for integrating Authelia with an [OpenID Connect 1.0] Relying Party, as well as specific
|
specifics that can be used for integrating Authelia with an [OpenID Connect 1.0] Relying Party, as well as specific
|
||||||
documentation for some [OpenID Connect 1.0] Relying Party implementations.
|
documentation for some [OpenID Connect 1.0] Relying Party implementations.
|
||||||
|
|
||||||
See the [configuration documentation](../../configuration/identity-providers/open-id-connect.md) for information on how
|
See the [OpenID Connect 1.0 Provider](../../configuration/identity-providers/openid-connect/provider.md) and
|
||||||
to configure the Authelia [OpenID Connect 1.0] Provider.
|
[OpenID Connect 1.0 Clients](../../configuration/identity-providers/openid-connect/clients.md) configuration guides for
|
||||||
|
information on how to configure the Authelia [OpenID Connect 1.0] Provider (note the clients guide is for configuring
|
||||||
|
the registered clients in the provider).
|
||||||
|
|
||||||
This page is intended as an integration reference point for any implementers who wish to integrate an
|
This page is intended as an integration reference point for any implementers who wish to integrate an
|
||||||
[OpenID Connect 1.0] Relying Party (client application) either as a developer or user of the third party Reyling Party.
|
[OpenID Connect 1.0] Relying Party (client application) either as a developer or user of the third party Reyling Party.
|
||||||
|
@ -124,6 +126,7 @@ Authelia's response objects can have the following signature algorithms:
|
||||||
|
|
||||||
### Request Object
|
### Request Object
|
||||||
|
|
||||||
|
Authelia accepts a wide variety of request object types.
|
||||||
|
|
||||||
| Algorithm | Key Type | Hashing Algorithm | Use | Notes |
|
| Algorithm | Key Type | Hashing Algorithm | Use | Notes |
|
||||||
|:---------:|:------------------:|:-----------------:|:---------:|:--------------------------------------------------:|
|
|:---------:|:------------------:|:-----------------:|:---------:|:--------------------------------------------------:|
|
||||||
|
@ -131,6 +134,15 @@ Authelia's response objects can have the following signature algorithms:
|
||||||
| HS256 | HMAC Shared Secret | SHA-256 | Signature | [Client Authentication Method] `client_secret_jwt` |
|
| HS256 | HMAC Shared Secret | SHA-256 | Signature | [Client Authentication Method] `client_secret_jwt` |
|
||||||
| HS384 | HMAC Shared Secret | SHA-384 | Signature | [Client Authentication Method] `client_secret_jwt` |
|
| HS384 | HMAC Shared Secret | SHA-384 | Signature | [Client Authentication Method] `client_secret_jwt` |
|
||||||
| HS512 | HMAC Shared Secret | SHA-512 | Signature | [Client Authentication Method] `client_secret_jwt` |
|
| HS512 | HMAC Shared Secret | SHA-512 | Signature | [Client Authentication Method] `client_secret_jwt` |
|
||||||
|
| RS256 | RSA | SHA-256 | Signature | [Client Authentication Method] `private_key_jwt` |
|
||||||
|
| RS384 | RSA | SHA-384 | Signature | [Client Authentication Method] `private_key_jwt` |
|
||||||
|
| RS512 | RSA | SHA-512 | Signature | [Client Authentication Method] `private_key_jwt` |
|
||||||
|
| ES256 | ECDSA P-256 | SHA-256 | Signature | [Client Authentication Method] `private_key_jwt` |
|
||||||
|
| ES384 | ECDSA P-384 | SHA-384 | Signature | [Client Authentication Method] `private_key_jwt` |
|
||||||
|
| ES512 | ECDSA P-521 | SHA-512 | Signature | [Client Authentication Method] `private_key_jwt` |
|
||||||
|
| PS256 | RSA (MFG1) | SHA-256 | Signature | [Client Authentication Method] `private_key_jwt` |
|
||||||
|
| PS384 | RSA (MFG1) | SHA-384 | Signature | [Client Authentication Method] `private_key_jwt` |
|
||||||
|
| PS512 | RSA (MFG1) | SHA-512 | Signature | [Client Authentication Method] `private_key_jwt` |
|
||||||
|
|
||||||
[Client Authentication Method]: #client-authentication-method
|
[Client Authentication Method]: #client-authentication-method
|
||||||
|
|
||||||
|
@ -208,7 +220,7 @@ specification and the [OAuth 2.0 - Client Types] specification for more informat
|
||||||
| Secret via HTTP Basic Auth Scheme | `client_secret_basic` | `confidential` | N/A | N/A |
|
| Secret via HTTP Basic Auth Scheme | `client_secret_basic` | `confidential` | N/A | N/A |
|
||||||
| Secret via HTTP POST Body | `client_secret_post` | `confidential` | N/A | N/A |
|
| Secret via HTTP POST Body | `client_secret_post` | `confidential` | N/A | N/A |
|
||||||
| JWT (signed by secret) | `client_secret_jwt` | `confidential` | N/A | `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
|
| JWT (signed by secret) | `client_secret_jwt` | `confidential` | N/A | `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
|
||||||
| JWT (signed by private key) | `private_key_jwt` | Not Supported | N/A | `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
|
| JWT (signed by private key) | `private_key_jwt` | `confidential` | N/A | `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
|
||||||
| [OAuth 2.0 Mutual-TLS] | `tls_client_auth` | Not Supported | N/A | N/A |
|
| [OAuth 2.0 Mutual-TLS] | `tls_client_auth` | Not Supported | N/A | N/A |
|
||||||
| [OAuth 2.0 Mutual-TLS] (Self Signed) | `self_signed_tls_client_auth` | Not Supported | N/A | N/A |
|
| [OAuth 2.0 Mutual-TLS] (Self Signed) | `self_signed_tls_client_auth` | Not Supported | N/A | N/A |
|
||||||
| No Authentication | `none` | `public` | `public` | N/A |
|
| No Authentication | `none` | `public` | `public` | N/A |
|
||||||
|
@ -243,7 +255,7 @@ Below is a list of the potential values we place in the [Claim] and their meanin
|
||||||
## User Information Signing Algorithm
|
## User Information Signing Algorithm
|
||||||
|
|
||||||
The following table describes the response from the [UserInfo] endpoint depending on the
|
The following table describes the response from the [UserInfo] endpoint depending on the
|
||||||
[userinfo_signing_algorithm](../../configuration/identity-providers/open-id-connect.md#userinfosigningalgorithm).
|
[userinfo_signing_alg](../../configuration/identity-providers/openid-connect/clients.md#userinfosigningalg).
|
||||||
|
|
||||||
| Signing Algorithm | Encoding | Content Type |
|
| Signing Algorithm | Encoding | Content Type |
|
||||||
|:-----------------:|:------------:|:-----------------------------------:|
|
|:-----------------:|:------------:|:-----------------------------------:|
|
||||||
|
|
|
@ -80,7 +80,7 @@ identity_providers:
|
||||||
- 'groups'
|
- 'groups'
|
||||||
- 'email'
|
- 'email'
|
||||||
consent_mode: 'implicit'
|
consent_mode: 'implicit'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -65,7 +65,7 @@ spring:
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Komga]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Komga]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -87,7 +87,7 @@ identity_providers:
|
||||||
- 'email'
|
- 'email'
|
||||||
grant_types:
|
grant_types:
|
||||||
- 'authorization_code'
|
- 'authorization_code'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -63,7 +63,7 @@ To configure [MinIO] to utilize Authelia as an [OpenID Connect 1.0] Provider:
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [MinIO]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [MinIO]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -84,7 +84,7 @@ identity_providers:
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'email'
|
- 'email'
|
||||||
- 'groups'
|
- 'groups'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -79,7 +79,7 @@ To configure [Misago] to utilize Authelia as an [OpenID Connect 1.0](https://www
|
||||||
|
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example **Authelia** [client configuration](https://www.authelia.com/configuration/identity-providers/open-id-connect/#clients) for use with [Misago] which will operate with the above example:
|
The following YAML configuration is an example **Authelia** [client configuration](https://www.authelia.com/configuration/identity-providers/openid-connect/#clients) for use with [Misago] which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
identity_providers:
|
identity_providers:
|
||||||
|
@ -104,7 +104,7 @@ identity_providers:
|
||||||
- 'code'
|
- 'code'
|
||||||
response_modes:
|
response_modes:
|
||||||
- 'query'
|
- 'query'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
@ -86,7 +86,7 @@ $CONFIG = array (
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Nextcloud]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Nextcloud]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -107,7 +107,7 @@ identity_providers:
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'email'
|
- 'email'
|
||||||
- 'groups'
|
- 'groups'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -60,7 +60,7 @@ OIDC_SCOPES="openid offline_access profile email"
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Outline]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Outline]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -81,7 +81,7 @@ identity_providers:
|
||||||
- 'offline_access'
|
- 'offline_access'
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -61,7 +61,7 @@ To configure [Portainer] to utilize Authelia as an [OpenID Connect 1.0] Provider
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Portainer]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Portainer]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -82,7 +82,7 @@ identity_providers:
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'groups'
|
- 'groups'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -65,7 +65,7 @@ To configure [Proxmox] to utilize Authelia as an [OpenID Connect 1.0] Provider:
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Proxmox]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Proxmox]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -85,7 +85,7 @@ identity_providers:
|
||||||
- 'openid'
|
- 'openid'
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -69,7 +69,7 @@ OAUTH_ATTRIBUTE_MAP = {
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Seafile]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Seafile]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -89,7 +89,7 @@ identity_providers:
|
||||||
- 'openid'
|
- 'openid'
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -63,7 +63,7 @@ oidc_providers:
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Synapse]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Synapse]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -83,7 +83,7 @@ identity_providers:
|
||||||
- 'openid'
|
- 'openid'
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -65,7 +65,7 @@ To configure [Synology DSM] to utilize Authelia as an [OpenID Connect 1.0] Provi
|
||||||
### Authelia
|
### Authelia
|
||||||
|
|
||||||
The following YAML configuration is an example __Authelia__
|
The following YAML configuration is an example __Authelia__
|
||||||
[client configuration](../../../configuration/identity-providers/open-id-connect.md#clients) for use with [Synology DSM]
|
[client configuration](../../../configuration/identity-providers/openid-connect/clients.md) for use with [Synology DSM]
|
||||||
which will operate with the above example:
|
which will operate with the above example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -86,7 +86,7 @@ identity_providers:
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'groups'
|
- 'groups'
|
||||||
- 'email'
|
- 'email'
|
||||||
userinfo_signing_algorithm: 'none'
|
userinfo_signing_alg: 'none'
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
@ -58,7 +58,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/openid-connect/provider.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
|
||||||
* Using the [SWAG] default configurations are more difficult to support as our specific familiarity is with our own
|
* Using the [SWAG] default configurations are more difficult to support as our specific familiarity is with our own
|
||||||
|
|
|
@ -16,6 +16,6 @@ configure your applications to use Authelia as an [OpenID Connect 1.0 Provider](
|
||||||
currently operate as an [OpenID Connect 1.0 Relying Party](https://openid.net/connect/). This like all single-sign on
|
currently operate as an [OpenID Connect 1.0 Relying Party](https://openid.net/connect/). This like all single-sign on
|
||||||
technologies requires support by the protected application.
|
technologies requires support by the protected application.
|
||||||
|
|
||||||
See the [OpenID Connect 1.0 Configuration Guide](../../configuration/identity-providers/open-id-connect.md) and the
|
See the [OpenID Connect 1.0 Provider Configuration Guide](../../configuration/identity-providers/openid-connect/provider.md), and the
|
||||||
[OpenID Connect 1.0 Integration Guide](../../integration/openid-connect/introduction.md) for more information.
|
[OpenID Connect 1.0 Integration Guide](../../integration/openid-connect/introduction.md) for more information.
|
||||||
|
|
||||||
|
|
|
@ -115,8 +115,15 @@ Feature List:
|
||||||
|
|
||||||
{{< roadmap-status stage="in-progress" version="v4.38.0" >}}
|
{{< roadmap-status stage="in-progress" version="v4.38.0" >}}
|
||||||
|
|
||||||
* [OAuth 2.0 Pushed Authorization Requests](https://datatracker.ietf.org/doc/html/rfc9126)
|
* [RFC9126: OAuth 2.0 Pushed Authorization Requests](https://datatracker.ietf.org/doc/html/rfc9126)
|
||||||
|
* [RFC7523: JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants](https://datatracker.ietf.org/doc/html/rfc7523):
|
||||||
|
* Client Auth Method `client_secret_jwt`
|
||||||
|
* Client Auth Method `private_key_jwt`
|
||||||
* Per-Client [Proof Key Code Exchange (PKCE)] Policy
|
* Per-Client [Proof Key Code Exchange (PKCE)] Policy
|
||||||
|
* Multiple Issuer JWKs:
|
||||||
|
* RS256, RS384, RS512
|
||||||
|
* PS256, PS384, PS512
|
||||||
|
* ES256, ES384, ES512
|
||||||
|
|
||||||
### Beta 7
|
### Beta 7
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,4 +1,4 @@
|
||||||
{{ $faq := "../frequently-asked-questions/" }}{{ $config := "../../../configuration/identity-providers/open-id-connect.md" }}
|
{{ $faq := "../frequently-asked-questions/" }}{{ $config := "../../../configuration/identity-providers/openid-connect/" }}
|
||||||
{{- with .Get "faq" }}{{ $faq = . }}{{ end }}
|
{{- with .Get "faq" }}{{ $faq = . }}{{ end }}
|
||||||
{{- with .Get "config" }}{{ $config = . }}{{ end }}
|
{{- with .Get "config" }}{{ $config = . }}{{ end }}
|
||||||
### Common Notes
|
### Common Notes
|
||||||
|
@ -15,4 +15,6 @@
|
||||||
guaranteed to be supported in the future. See the [Plaintext]({{ $faq }}#plaintext) guide for more
|
guaranteed to be supported in the future. See the [Plaintext]({{ $faq }}#plaintext) guide for more
|
||||||
information.
|
information.
|
||||||
3. The Configuration example for Authelia is only a portion of the required configuration and it should be used as a
|
3. The Configuration example for Authelia is only a portion of the required configuration and it should be used as a
|
||||||
guide in conjunction with the standard [OpenID Connect 1.0 Configuration]({{ $config }}) guide.
|
guide in conjunction with the standard
|
||||||
|
[OpenID Connect 1.0 Provider Configuration]({{ printf "%s/provider.md" $config }}) and
|
||||||
|
[OpenID Connect 1.0 Clients Configuration]({{ printf "%s/clients.md" $config }}) guides.
|
|
@ -4,7 +4,16 @@
|
||||||
# Authelia Configuration #
|
# Authelia Configuration #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
## Note: the container by default expects to find this file at /config/configuration.yml.
|
##
|
||||||
|
## Notes:
|
||||||
|
##
|
||||||
|
## - the default location of this file is assumed to be configuration.yml unless otherwise noted
|
||||||
|
## - when using docker the container expects this by default to be at /config/configuration.yml
|
||||||
|
## - the default location where this file is loaded from can be overridden with the X_AUTHELIA_CONFIG environment var
|
||||||
|
## - the comments in this configuration file are helpful but users should consult the official documentation on the
|
||||||
|
## website at https://www.authelia.com/ or https://www.authelia.com/configuration/prologue/introduction/
|
||||||
|
## - this configuration file template is not automatically updated
|
||||||
|
##
|
||||||
|
|
||||||
## Certificates directory specifies where Authelia will load trusted certificates (public portion) from in addition to
|
## Certificates directory specifies where Authelia will load trusted certificates (public portion) from in addition to
|
||||||
## the system certificates store.
|
## the system certificates store.
|
||||||
|
@ -357,73 +366,37 @@ authentication_backend:
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# certificate_chain: |
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# private_key: |
|
# private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
## The distinguished name of the container searched for objects in the directory information tree.
|
## The distinguished name of the container searched for objects in the directory information tree.
|
||||||
|
@ -485,7 +458,7 @@ authentication_backend:
|
||||||
# permit_referrals: false
|
# permit_referrals: false
|
||||||
|
|
||||||
## The username and password of the admin user.
|
## The username and password of the admin user.
|
||||||
# user: cn=admin,dc=example,dc=com
|
# user: 'cn=admin,dc=example,dc=com'
|
||||||
## Password can also be set using a secret: https://www.authelia.com/c/secrets
|
## Password can also be set using a secret: https://www.authelia.com/c/secrets
|
||||||
# password: 'password'
|
# password: 'password'
|
||||||
|
|
||||||
|
@ -622,7 +595,7 @@ access_control:
|
||||||
# networks:
|
# networks:
|
||||||
# - '10.10.0.0/16'
|
# - '10.10.0.0/16'
|
||||||
# - '192.168.2.0/24'
|
# - '192.168.2.0/24'
|
||||||
# - name: VPN
|
# - name: 'VPN'
|
||||||
# networks: '10.9.0.0/16'
|
# networks: '10.9.0.0/16'
|
||||||
|
|
||||||
# rules:
|
# rules:
|
||||||
|
@ -748,7 +721,8 @@ session:
|
||||||
# expiration: '1h'
|
# expiration: '1h'
|
||||||
|
|
||||||
## The time before the cookie expires and the session is destroyed if remember me IS selected by the user. Setting
|
## The time before the cookie expires and the session is destroyed if remember me IS selected by the user. Setting
|
||||||
## this value to -1 disables remember me for this session cookie domain.
|
## this value to -1 disables remember me for this session cookie domain. If allowed and the user uses the remember
|
||||||
|
## me checkbox this overrides the expiration option and disables the inactivity option.
|
||||||
# remember_me: '1M'
|
# remember_me: '1M'
|
||||||
|
|
||||||
## Cookie Session Domain default 'name' value.
|
## Cookie Session Domain default 'name' value.
|
||||||
|
@ -816,73 +790,37 @@ session:
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# certificate_chain: |
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# private_key: |
|
# private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
## The Redis HA configuration options.
|
## The Redis HA configuration options.
|
||||||
|
@ -997,73 +935,37 @@ regulation:
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# certificate_chain: |
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# private_key: |
|
# private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -1116,73 +1018,37 @@ regulation:
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# certificate_chain: |
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# private_key: |
|
# private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -1270,73 +1136,37 @@ notifier:
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# certificate_chain: |
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
## The private key used with the certificate_chain if the server requests TLS Client Authentication
|
||||||
## i.e. Mutual TLS.
|
## i.e. Mutual TLS.
|
||||||
# private_key: |
|
# private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -1354,80 +1184,88 @@ notifier:
|
||||||
## HMAC Secret can also be set using a secret: https://www.authelia.com/c/secrets
|
## HMAC Secret can also be set using a secret: https://www.authelia.com/c/secrets
|
||||||
# hmac_secret: 'this_is_a_secret_abc123abc123abc'
|
# hmac_secret: 'this_is_a_secret_abc123abc123abc'
|
||||||
|
|
||||||
## The issuer_certificate_chain is an optional PEM encoded certificate chain. It's used in conjunction with the
|
## Issuer JWKS configures multiple JSON Web Keys. It's required that at least one of these is RS256 or the
|
||||||
## issuer_private_key to sign JWT's. All certificates in the chain must be within the validity period, and every
|
## option issuer_private_key is configured. There must only be one key per algorithm at this time.
|
||||||
## certificate included must be signed by the certificate immediately after it if provided.
|
## For RSA keys the minimum is a 2048 bit key.
|
||||||
# issuer_certificate_chain: |
|
# issuer_private_keys:
|
||||||
|
# -
|
||||||
|
## Key ID embedded into the JWT header for key matching. Must be an alphanumeric string with 7 or less characters.
|
||||||
|
## This value is automatically generated if not provided. It's recommended to not configure this.
|
||||||
|
# key_id: 'example'
|
||||||
|
|
||||||
|
## The key algorithm used with this key.
|
||||||
|
# algorithm: 'RS256'
|
||||||
|
|
||||||
|
## The key use expected with this key. Currently only 'sig' is supported.
|
||||||
|
# use: 'sig'
|
||||||
|
|
||||||
|
## Required Private Key in PEM DER form.
|
||||||
|
# key: |
|
||||||
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
|
|
||||||
|
## Optional matching certificate chain in PEM DER form that matches the key. All certificates within the chain
|
||||||
|
## must be valid and current, and from top to bottom each certificate must be signed by the subsequent one.
|
||||||
|
# certificate_chain: |
|
||||||
# -----BEGIN CERTIFICATE-----
|
# -----BEGIN CERTIFICATE-----
|
||||||
# MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
# ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
# /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
# LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
# 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
# kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
# Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
|
||||||
# AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
|
||||||
# AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
|
||||||
# /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
|
||||||
# lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
|
||||||
# wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
|
||||||
# OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
|
||||||
# ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
|
||||||
# -----BEGIN CERTIFICATE-----
|
|
||||||
# MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw
|
|
||||||
# EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
|
||||||
# MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
|
||||||
# ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S
|
|
||||||
# zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50
|
|
||||||
# 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou
|
|
||||||
# kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7
|
|
||||||
# ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi
|
|
||||||
# Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
||||||
# AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
||||||
# Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/
|
|
||||||
# kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf
|
|
||||||
# 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ
|
|
||||||
# HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB
|
|
||||||
# D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj
|
|
||||||
# 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b
|
|
||||||
# qocikt3WAdU^invalid DO NOT USE=
|
|
||||||
# -----END CERTIFICATE-----
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The issuer_private_key is used to sign the JWT forged by OpenID Connect.
|
## The issuer_private_key is used to sign the JWT forged by OpenID Connect. This is in addition to the
|
||||||
|
## issuer_private_keys option. Assumed to use the RS256 algorithm, and must not be specified if any of the
|
||||||
|
## keys in issuer_private_keys also has the algorithm RS256 or are an RSA key without an algorithm.
|
||||||
## Issuer Private Key can also be set using a secret: https://www.authelia.com/c/secrets
|
## Issuer Private Key can also be set using a secret: https://www.authelia.com/c/secrets
|
||||||
# issuer_private_key: |
|
# issuer_private_key: |
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
# -----BEGIN RSA PRIVATE KEY-----
|
||||||
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
# MIIBPAIBAAJBAK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZF
|
||||||
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
# p7aTcToHMf00z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAQJBAJdpB0+RQ9ZFwy9Uk38P
|
||||||
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
# 5zZpUB8cL8ZFeEFluQeVbt0vyNa+cPLvDLouY87onduXtMz5AKIatLaTOjuG2thh
|
||||||
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
# SKECIQDY6G8gvsYJdXCE9UJ7ukoLrRHxt/frhAtmSY5lVAPuMwIhAMzuDrJo73LH
|
||||||
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
# ZyEaqIXc5pIiX3Sag43csPDHfuXdtT2NAiEAhyRKGJzDxiDlefFU+sGWYK/z/iYg
|
||||||
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
# 0Rvz/kbV8UvnJwECIQDAYN6VJ6NZmc27qv33JIejOfdoTEEhZMMKVg1PlxE0ZQIg
|
||||||
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
# HFpJiFxZES3QvVPr8deBXORPurqD5uU85NKsf61AdRsDO_NOT_USE=
|
||||||
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
|
||||||
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
|
||||||
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
|
||||||
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
|
||||||
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
|
||||||
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
|
||||||
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
|
||||||
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
|
||||||
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
|
||||||
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
|
||||||
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
|
||||||
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
|
||||||
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
|
||||||
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
|
||||||
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
|
||||||
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
|
||||||
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
|
||||||
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
|
||||||
# DO NOT USE==
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
|
## Optional matching certificate chain in PEM DER form that matches the issuer_private_key. All certificates within
|
||||||
|
## the chain must be valid and current, and from top to bottom each certificate must be signed by the next
|
||||||
|
## certificate in the chain if provided.
|
||||||
|
# issuer_certificate_chain: |
|
||||||
|
# -----BEGIN CERTIFICATE-----
|
||||||
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
# -----END CERTIFICATE-----
|
||||||
|
# -----BEGIN CERTIFICATE-----
|
||||||
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
# -----END CERTIFICATE-----
|
||||||
|
|
||||||
## The lifespans configure the expiration for these token types in the duration common syntax.
|
## The lifespans configure the expiration for these token types in the duration common syntax.
|
||||||
# access_token_lifespan: '1h'
|
# access_token_lifespan: '1h'
|
||||||
# authorize_code_lifespan: '1m'
|
# authorize_code_lifespan: '1m'
|
||||||
|
@ -1499,6 +1337,11 @@ notifier:
|
||||||
# - 'email'
|
# - 'email'
|
||||||
# - 'profile'
|
# - 'profile'
|
||||||
|
|
||||||
|
## Grant Types configures which grants this client can obtain.
|
||||||
|
## It's not recommended to define this unless you know what you're doing.
|
||||||
|
# grant_types:
|
||||||
|
# - 'authorization_code'
|
||||||
|
|
||||||
## Response Types configures which responses this client can be sent.
|
## Response Types configures which responses this client can be sent.
|
||||||
## It's not recommended to define this unless you know what you're doing.
|
## It's not recommended to define this unless you know what you're doing.
|
||||||
# response_types:
|
# response_types:
|
||||||
|
@ -1509,25 +1352,19 @@ notifier:
|
||||||
# - 'form_post'
|
# - 'form_post'
|
||||||
# - 'query'
|
# - 'query'
|
||||||
|
|
||||||
## Grant Types configures which grants this client can obtain.
|
|
||||||
## It's not recommended to define this unless you know what you're doing.
|
|
||||||
# grant_types:
|
|
||||||
# - 'authorization_code'
|
|
||||||
|
|
||||||
## The permitted client authentication method for the Token Endpoint for this client.
|
|
||||||
# token_endpoint_auth_method: 'client_secret_basic'
|
|
||||||
|
|
||||||
## The permitted client authentication signing algorithm for the Token Endpoint for this client when using
|
|
||||||
## the 'client_secret_jwt' token_endpoint_auth_method.
|
|
||||||
# token_endpoint_auth_signing_alg: HS256
|
|
||||||
|
|
||||||
## The permitted client authentication signing algorithm for the Token Endpoint for this client when using
|
|
||||||
## the 'client_secret_jwt' token_endpoint_auth_method.
|
|
||||||
# token_endpoint_auth_signing_alg: HS256
|
|
||||||
|
|
||||||
## The policy to require for this client; one_factor or two_factor.
|
## The policy to require for this client; one_factor or two_factor.
|
||||||
# authorization_policy: 'two_factor'
|
# authorization_policy: 'two_factor'
|
||||||
|
|
||||||
|
## The consent mode controls how consent is obtained.
|
||||||
|
# consent_mode: 'auto'
|
||||||
|
|
||||||
|
## This value controls the duration a consent on this client remains remembered when the consent mode is
|
||||||
|
## configured as 'auto' or 'pre-configured' in the duration common syntax.
|
||||||
|
# pre_configured_consent_duration: '1w'
|
||||||
|
|
||||||
|
## Enforces the use of Pushed Authorization Requests for this client when set to true.
|
||||||
|
# enforce_par: false
|
||||||
|
|
||||||
## Enforces the use of PKCE for this client when set to true.
|
## Enforces the use of PKCE for this client when set to true.
|
||||||
# enforce_pkce: false
|
# enforce_pkce: false
|
||||||
|
|
||||||
|
@ -1535,13 +1372,69 @@ notifier:
|
||||||
## Options are 'plain' and 'S256'.
|
## Options are 'plain' and 'S256'.
|
||||||
# pkce_challenge_method: 'S256'
|
# pkce_challenge_method: 'S256'
|
||||||
|
|
||||||
|
## The permitted client authentication method for the Token Endpoint for this client.
|
||||||
|
# token_endpoint_auth_method: 'client_secret_basic'
|
||||||
|
|
||||||
|
## The permitted client authentication signing algorithm for the Token Endpoint for this client when using
|
||||||
|
## the 'client_secret_jwt' or 'private_key_jwt' token_endpoint_auth_method.
|
||||||
|
# token_endpoint_auth_signing_alg: 'RS256'
|
||||||
|
|
||||||
|
## The signing algorithm which must be used for request objects. A client JWK with a matching algorithm must be
|
||||||
|
## included if configured.
|
||||||
|
# request_object_signing_alg: 'RS256'
|
||||||
|
|
||||||
|
## The signing algorithm used for ID Tokens. Am issuer JWK with a matching algorithm must be included.
|
||||||
|
# id_token_signing_alg: 'RS256'
|
||||||
|
|
||||||
## The algorithm used to sign userinfo endpoint responses for this client, either none or RS256.
|
## The algorithm used to sign userinfo endpoint responses for this client, either none or RS256.
|
||||||
# userinfo_signing_algorithm: 'none'
|
# userinfo_signing_alg: 'none'
|
||||||
|
|
||||||
## The consent mode controls how consent is obtained.
|
## Trusted public keys configuration for request object signing for things such as private_key_jwt
|
||||||
# consent_mode: 'auto'
|
# public_keys:
|
||||||
|
|
||||||
## This value controls the duration a consent on this client remains remembered when the consent mode is
|
## URL of the HTTPS endpoint which serves the keys. It's recommended to manually configure them in the
|
||||||
## configured as 'auto' or 'pre-configured' in the duration common syntax.
|
## values option below. Please note the URL and the individual values are mutually exclusive.
|
||||||
# pre_configured_consent_duration: '1w'
|
# uri: 'https://app.example.com/jwks.json'
|
||||||
|
|
||||||
|
## Values from the individual keys.
|
||||||
|
# values:
|
||||||
|
# -
|
||||||
|
## Key ID used to match the JWT's to an individual identifier. This option is required if configured.
|
||||||
|
# key_id: 'example'
|
||||||
|
|
||||||
|
## The key algorithm expected with this key.
|
||||||
|
# algorithm: 'RS256'
|
||||||
|
|
||||||
|
## The key use expected with this key. Currently only 'sig' is supported.
|
||||||
|
# use: 'sig'
|
||||||
|
|
||||||
|
## Required Public Key in PEM DER form.
|
||||||
|
# key: |
|
||||||
|
# -----BEGIN RSA PUBLIC KEY-----
|
||||||
|
# MEgCQQDAwV26ZA1lodtOQxNrJ491gWT+VzFum9IeZ+WTmMypYWyW1CzXKwsvTHDz
|
||||||
|
# 9ec+jserR3EMQ0Rr24lj13FL1ib5AgMBAAE=
|
||||||
|
# -----END RSA PUBLIC KEY----
|
||||||
|
|
||||||
|
## The matching certificate chain in PEM DER form that matches the key if available.
|
||||||
|
# certificate_chain: |
|
||||||
|
# -----BEGIN CERTIFICATE-----
|
||||||
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
# -----END CERTIFICATE-----
|
||||||
|
# -----BEGIN CERTIFICATE-----
|
||||||
|
# MIIBWzCCAQWgAwIBAgIQYAKsXhJOXKfyySlmpKicTzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
# MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMzA0MjEwMDA3NDRaFw0yNDA0MjAwMDA3
|
||||||
|
# NDRaMBMxETAPBgNVBAoTCEF1dGhlbGlhMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
|
||||||
|
# AK2i7RlJEYo/Xa6mQmv9zmT0XUj3DcEhRJGPVw2qMyadUFxNg/ZFp7aTcToHMf00
|
||||||
|
# z6T3b7mwdBkCFQOL3Kb7WRcCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
# JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADQQB8
|
||||||
|
# Of2iM7fPadmtChCMna8lYWH+lEplj6BxOJlRuGRawxszLwi78bnq0sCR33LU6xMx
|
||||||
|
# 1oAPwIHNaJJwC4z6oG9E_DO_NOT_USE=
|
||||||
|
# -----END CERTIFICATE-----
|
||||||
...
|
...
|
||||||
|
|
|
@ -253,4 +253,12 @@ var deprecations = map[string]Deprecation{
|
||||||
MapFunc: nil,
|
MapFunc: nil,
|
||||||
ErrFunc: nil,
|
ErrFunc: nil,
|
||||||
},
|
},
|
||||||
|
"identity_providers.oidc.clients[].userinfo_signing_algorithm": {
|
||||||
|
Version: model.SemanticVersion{Major: 4, Minor: 38},
|
||||||
|
Key: "identity_providers.oidc.clients[].userinfo_signing_algorithm",
|
||||||
|
NewKey: "identity_providers.oidc.clients[].userinfo_signing_alg",
|
||||||
|
AutoMap: true,
|
||||||
|
MapFunc: nil,
|
||||||
|
ErrFunc: nil,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,37 +245,6 @@ func TestShouldLoadURLList(t *testing.T) {
|
||||||
assert.Equal(t, "https://example.com", config.IdentityProviders.OIDC.CORS.AllowedOrigins[1].String())
|
assert.Equal(t, "https://example.com", config.IdentityProviders.OIDC.CORS.AllowedOrigins[1].String())
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func TestShouldLoadNewOIDCConfig(t *testing.T) {
|
|
||||||
val := schema.NewStructValidator()
|
|
||||||
_, config, err := Load(val, NewDefaultSources([]string{"./test_resources/config_oidc_modern.yml"}, DefaultEnvPrefix, DefaultEnvDelimiter)...)
|
|
||||||
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Len(t, val.Errors(), 0)
|
|
||||||
assert.Len(t, val.Warnings(), 0)
|
|
||||||
|
|
||||||
val.Clear()
|
|
||||||
|
|
||||||
validator.ValidateIdentityProviders(&config.IdentityProviders, val)
|
|
||||||
|
|
||||||
assert.Len(t, val.Errors(), 0)
|
|
||||||
|
|
||||||
assert.Len(t, config.IdentityProviders.OIDC.IssuerJWKS.Keys, 2)
|
|
||||||
assert.Equal(t, "keya", config.IdentityProviders.OIDC.IssuerJWKS.DefaultKeyID)
|
|
||||||
|
|
||||||
assert.Equal(t, oidc.KeyUseSignature, config.IdentityProviders.OIDC.IssuerJWKS.Keys["keya"].Use)
|
|
||||||
assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, config.IdentityProviders.OIDC.IssuerJWKS.Keys["keya"].Algorithm)
|
|
||||||
|
|
||||||
assert.Equal(t, oidc.KeyUseSignature, config.IdentityProviders.OIDC.IssuerJWKS.Keys["ec521"].Use)
|
|
||||||
assert.Equal(t, oidc.SigningAlgECDSAUsingP521AndSHA512, config.IdentityProviders.OIDC.IssuerJWKS.Keys["ec521"].Algorithm)
|
|
||||||
|
|
||||||
assert.Contains(t, config.IdentityProviders.OIDC.Discovery.RegisteredJWKSigningAlgs, oidc.SigningAlgRSAUsingSHA256)
|
|
||||||
assert.Contains(t, config.IdentityProviders.OIDC.Discovery.RegisteredJWKSigningAlgs, oidc.SigningAlgECDSAUsingP521AndSHA512)
|
|
||||||
}.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
func TestShouldConfigureConsent(t *testing.T) {
|
func TestShouldConfigureConsent(t *testing.T) {
|
||||||
val := schema.NewStructValidator()
|
val := schema.NewStructValidator()
|
||||||
keys, config, err := Load(val, NewDefaultSources([]string{"./test_resources/config_oidc.yml"}, DefaultEnvPrefix, DefaultEnvDelimiter)...)
|
keys, config, err := Load(val, NewDefaultSources([]string{"./test_resources/config_oidc.yml"}, DefaultEnvPrefix, DefaultEnvDelimiter)...)
|
||||||
|
@ -289,6 +258,7 @@ func TestShouldConfigureConsent(t *testing.T) {
|
||||||
|
|
||||||
require.Len(t, config.IdentityProviders.OIDC.Clients, 1)
|
require.Len(t, config.IdentityProviders.OIDC.Clients, 1)
|
||||||
assert.Equal(t, config.IdentityProviders.OIDC.Clients[0].ConsentMode, "explicit")
|
assert.Equal(t, config.IdentityProviders.OIDC.Clients[0].ConsentMode, "explicit")
|
||||||
|
assert.Equal(t, "none", config.IdentityProviders.OIDC.Clients[0].UserinfoSigningAlg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldValidateAndRaiseErrorsOnBadConfiguration(t *testing.T) {
|
func TestShouldValidateAndRaiseErrorsOnBadConfiguration(t *testing.T) {
|
||||||
|
|
|
@ -14,11 +14,11 @@ type IdentityProvidersConfiguration struct {
|
||||||
// OpenIDConnectConfiguration configuration for OpenID Connect.
|
// OpenIDConnectConfiguration configuration for OpenID Connect.
|
||||||
type OpenIDConnectConfiguration struct {
|
type OpenIDConnectConfiguration struct {
|
||||||
HMACSecret string `koanf:"hmac_secret"`
|
HMACSecret string `koanf:"hmac_secret"`
|
||||||
|
IssuerPrivateKeys []JWK `koanf:"issuer_private_keys"`
|
||||||
|
|
||||||
IssuerCertificateChain X509CertificateChain `koanf:"issuer_certificate_chain"`
|
IssuerCertificateChain X509CertificateChain `koanf:"issuer_certificate_chain"`
|
||||||
IssuerPrivateKey *rsa.PrivateKey `koanf:"issuer_private_key"`
|
IssuerPrivateKey *rsa.PrivateKey `koanf:"issuer_private_key"`
|
||||||
|
|
||||||
IssuerJWKS []JWK `koanf:"issuer_jwks"`
|
|
||||||
|
|
||||||
AccessTokenLifespan time.Duration `koanf:"access_token_lifespan"`
|
AccessTokenLifespan time.Duration `koanf:"access_token_lifespan"`
|
||||||
AuthorizeCodeLifespan time.Duration `koanf:"authorize_code_lifespan"`
|
AuthorizeCodeLifespan time.Duration `koanf:"authorize_code_lifespan"`
|
||||||
IDTokenLifespan time.Duration `koanf:"id_token_lifespan"`
|
IDTokenLifespan time.Duration `koanf:"id_token_lifespan"`
|
||||||
|
@ -30,8 +30,8 @@ type OpenIDConnectConfiguration struct {
|
||||||
EnforcePKCE string `koanf:"enforce_pkce"`
|
EnforcePKCE string `koanf:"enforce_pkce"`
|
||||||
EnablePKCEPlainChallenge bool `koanf:"enable_pkce_plain_challenge"`
|
EnablePKCEPlainChallenge bool `koanf:"enable_pkce_plain_challenge"`
|
||||||
|
|
||||||
CORS OpenIDConnectCORSConfiguration `koanf:"cors"`
|
|
||||||
PAR OpenIDConnectPARConfiguration `koanf:"pushed_authorizations"`
|
PAR OpenIDConnectPARConfiguration `koanf:"pushed_authorizations"`
|
||||||
|
CORS OpenIDConnectCORSConfiguration `koanf:"cors"`
|
||||||
|
|
||||||
Clients []OpenIDConnectClientConfiguration `koanf:"clients"`
|
Clients []OpenIDConnectClientConfiguration `koanf:"clients"`
|
||||||
|
|
||||||
|
@ -40,7 +40,8 @@ type OpenIDConnectConfiguration struct {
|
||||||
|
|
||||||
type OpenIDConnectDiscovery struct {
|
type OpenIDConnectDiscovery struct {
|
||||||
DefaultKeyID string
|
DefaultKeyID string
|
||||||
RegisteredJWKSigningAlgs []string
|
ResponseObjectSigningAlgs []string
|
||||||
|
RequestObjectSigningAlgs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenIDConnectPARConfiguration represents an OpenID Connect PAR config.
|
// OpenIDConnectPARConfiguration represents an OpenID Connect PAR config.
|
||||||
|
@ -73,21 +74,31 @@ type OpenIDConnectClientConfiguration struct {
|
||||||
ResponseTypes []string `koanf:"response_types"`
|
ResponseTypes []string `koanf:"response_types"`
|
||||||
ResponseModes []string `koanf:"response_modes"`
|
ResponseModes []string `koanf:"response_modes"`
|
||||||
|
|
||||||
TokenEndpointAuthMethod string `koanf:"token_endpoint_auth_method"`
|
|
||||||
TokenEndpointAuthSigningAlg string `koanf:"token_endpoint_auth_signing_alg"`
|
|
||||||
|
|
||||||
IDTokenSigningAlg string `koanf:"id_token_signing_alg"`
|
|
||||||
|
|
||||||
Policy string `koanf:"authorization_policy"`
|
Policy string `koanf:"authorization_policy"`
|
||||||
|
|
||||||
|
ConsentMode string `koanf:"consent_mode"`
|
||||||
|
ConsentPreConfiguredDuration *time.Duration `koanf:"pre_configured_consent_duration"`
|
||||||
|
|
||||||
EnforcePAR bool `koanf:"enforce_par"`
|
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"`
|
||||||
UserinfoSigningAlg string `koanf:"userinfo_signing_algorithm"`
|
|
||||||
|
|
||||||
ConsentMode string `koanf:"consent_mode"`
|
TokenEndpointAuthMethod string `koanf:"token_endpoint_auth_method"`
|
||||||
ConsentPreConfiguredDuration *time.Duration `koanf:"pre_configured_consent_duration"`
|
|
||||||
|
TokenEndpointAuthSigningAlg string `koanf:"token_endpoint_auth_signing_alg"`
|
||||||
|
RequestObjectSigningAlg string `koanf:"request_object_signing_alg"`
|
||||||
|
IDTokenSigningAlg string `koanf:"id_token_signing_alg"`
|
||||||
|
UserinfoSigningAlg string `koanf:"userinfo_signing_alg"`
|
||||||
|
|
||||||
|
PublicKeys OpenIDConnectClientPublicKeys `koanf:"public_keys"`
|
||||||
|
|
||||||
|
Discovery OpenIDConnectDiscovery
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenIDConnectClientPublicKeys struct {
|
||||||
|
URI *url.URL `koanf:"uri"`
|
||||||
|
Values []JWK `koanf:"values"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultOpenIDConnectConfiguration contains defaults for OIDC.
|
// DefaultOpenIDConnectConfiguration contains defaults for OIDC.
|
||||||
|
|
|
@ -18,14 +18,14 @@ var Keys = []string{
|
||||||
"log.file_path",
|
"log.file_path",
|
||||||
"log.keep_stdout",
|
"log.keep_stdout",
|
||||||
"identity_providers.oidc.hmac_secret",
|
"identity_providers.oidc.hmac_secret",
|
||||||
|
"identity_providers.oidc.issuer_private_keys",
|
||||||
|
"identity_providers.oidc.issuer_private_keys[].key_id",
|
||||||
|
"identity_providers.oidc.issuer_private_keys[]",
|
||||||
|
"identity_providers.oidc.issuer_private_keys[].algorithm",
|
||||||
|
"identity_providers.oidc.issuer_private_keys[].key",
|
||||||
|
"identity_providers.oidc.issuer_private_keys[].certificate_chain",
|
||||||
"identity_providers.oidc.issuer_certificate_chain",
|
"identity_providers.oidc.issuer_certificate_chain",
|
||||||
"identity_providers.oidc.issuer_private_key",
|
"identity_providers.oidc.issuer_private_key",
|
||||||
"identity_providers.oidc.issuer_jwks",
|
|
||||||
"identity_providers.oidc.issuer_jwks[].key_id",
|
|
||||||
"identity_providers.oidc.issuer_jwks[]",
|
|
||||||
"identity_providers.oidc.issuer_jwks[].algorithm",
|
|
||||||
"identity_providers.oidc.issuer_jwks[].key",
|
|
||||||
"identity_providers.oidc.issuer_jwks[].certificate_chain",
|
|
||||||
"identity_providers.oidc.access_token_lifespan",
|
"identity_providers.oidc.access_token_lifespan",
|
||||||
"identity_providers.oidc.authorize_code_lifespan",
|
"identity_providers.oidc.authorize_code_lifespan",
|
||||||
"identity_providers.oidc.id_token_lifespan",
|
"identity_providers.oidc.id_token_lifespan",
|
||||||
|
@ -34,11 +34,11 @@ var Keys = []string{
|
||||||
"identity_providers.oidc.minimum_parameter_entropy",
|
"identity_providers.oidc.minimum_parameter_entropy",
|
||||||
"identity_providers.oidc.enforce_pkce",
|
"identity_providers.oidc.enforce_pkce",
|
||||||
"identity_providers.oidc.enable_pkce_plain_challenge",
|
"identity_providers.oidc.enable_pkce_plain_challenge",
|
||||||
|
"identity_providers.oidc.pushed_authorizations.enforce",
|
||||||
|
"identity_providers.oidc.pushed_authorizations.context_lifespan",
|
||||||
"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",
|
||||||
|
@ -51,16 +51,25 @@ var Keys = []string{
|
||||||
"identity_providers.oidc.clients[].grant_types",
|
"identity_providers.oidc.clients[].grant_types",
|
||||||
"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[].token_endpoint_auth_method",
|
|
||||||
"identity_providers.oidc.clients[].token_endpoint_auth_signing_alg",
|
|
||||||
"identity_providers.oidc.clients[].id_token_signing_alg",
|
|
||||||
"identity_providers.oidc.clients[].authorization_policy",
|
"identity_providers.oidc.clients[].authorization_policy",
|
||||||
|
"identity_providers.oidc.clients[].consent_mode",
|
||||||
|
"identity_providers.oidc.clients[].pre_configured_consent_duration",
|
||||||
"identity_providers.oidc.clients[].enforce_par",
|
"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[].token_endpoint_auth_method",
|
||||||
"identity_providers.oidc.clients[].consent_mode",
|
"identity_providers.oidc.clients[].token_endpoint_auth_signing_alg",
|
||||||
"identity_providers.oidc.clients[].pre_configured_consent_duration",
|
"identity_providers.oidc.clients[].request_object_signing_alg",
|
||||||
|
"identity_providers.oidc.clients[].id_token_signing_alg",
|
||||||
|
"identity_providers.oidc.clients[].userinfo_signing_alg",
|
||||||
|
"identity_providers.oidc.clients[].public_keys.uri",
|
||||||
|
"identity_providers.oidc.clients[].public_keys.values",
|
||||||
|
"identity_providers.oidc.clients[].public_keys.values[].key_id",
|
||||||
|
"identity_providers.oidc.clients[].public_keys.values[]",
|
||||||
|
"identity_providers.oidc.clients[].public_keys.values[].algorithm",
|
||||||
|
"identity_providers.oidc.clients[].public_keys.values[].key",
|
||||||
|
"identity_providers.oidc.clients[].public_keys.values[].certificate_chain",
|
||||||
|
"identity_providers.oidc.clients[]",
|
||||||
"identity_providers.oidc",
|
"identity_providers.oidc",
|
||||||
"authentication_backend.password_reset.disable",
|
"authentication_backend.password_reset.disable",
|
||||||
"authentication_backend.password_reset.custom_url",
|
"authentication_backend.password_reset.custom_url",
|
||||||
|
|
|
@ -102,6 +102,7 @@ func NewX509CertificateChain(in string) (chain *X509CertificateChain, err error)
|
||||||
return chain, nil
|
return chain, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewX509CertificateChainFromCerts returns a chain from a given list of certificates without validation.
|
||||||
func NewX509CertificateChainFromCerts(in []*x509.Certificate) (chain X509CertificateChain) {
|
func NewX509CertificateChainFromCerts(in []*x509.Certificate) (chain X509CertificateChain) {
|
||||||
return X509CertificateChain{certs: in}
|
return X509CertificateChain{certs: in}
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,6 +218,11 @@ func TestNewX509CertificateChain(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewX509CertificateChainFromCerts(t *testing.T) {
|
||||||
|
have := NewX509CertificateChainFromCerts(nil)
|
||||||
|
assert.NotNil(t, have)
|
||||||
|
}
|
||||||
|
|
||||||
func TestX509CertificateChain(t *testing.T) {
|
func TestX509CertificateChain(t *testing.T) {
|
||||||
chain := &X509CertificateChain{}
|
chain := &X509CertificateChain{}
|
||||||
|
|
||||||
|
|
|
@ -131,4 +131,5 @@ identity_providers:
|
||||||
- id: 'abc'
|
- id: 'abc'
|
||||||
secret: '123'
|
secret: '123'
|
||||||
consent_mode: 'explicit'
|
consent_mode: 'explicit'
|
||||||
|
userinfo_signing_alg: 'none'
|
||||||
...
|
...
|
||||||
|
|
|
@ -127,7 +127,7 @@ notifier:
|
||||||
identity_providers:
|
identity_providers:
|
||||||
oidc:
|
oidc:
|
||||||
hmac_secret: 1nb2j3kh1b23kjh1b23jh1b23j1h2b3
|
hmac_secret: 1nb2j3kh1b23kjh1b23jh1b23j1h2b3
|
||||||
issuer_jwks:
|
issuer_private_keys:
|
||||||
keys:
|
keys:
|
||||||
keya:
|
keya:
|
||||||
key: |
|
key: |
|
||||||
|
|
|
@ -142,15 +142,25 @@ const (
|
||||||
|
|
||||||
// OpenID Error constants.
|
// OpenID Error constants.
|
||||||
const (
|
const (
|
||||||
errFmtOIDCNoClientsConfigured = "identity_providers: oidc: option 'clients' must have one or " +
|
errFmtOIDCProviderNoClientsConfigured = "identity_providers: oidc: option 'clients' must have one or " +
|
||||||
"more clients configured"
|
"more clients configured"
|
||||||
errFmtOIDCNoPrivateKey = "identity_providers: oidc: option 'issuer_private_key' or `issuer_jwks` is required"
|
errFmtOIDCProviderNoPrivateKey = "identity_providers: oidc: option `issuer_private_keys` or 'issuer_private_key' is required"
|
||||||
errFmtOIDCInvalidPrivateKeyBitSize = "identity_providers: oidc: option 'issuer_private_key' must be an RSA private key with %d bits or more but it only has %d bits"
|
errFmtOIDCProviderEnforcePKCEInvalidValue = "identity_providers: oidc: option 'enforce_pkce' must be 'never', " +
|
||||||
errFmtOIDCInvalidPrivateKeyMalformedMissingPublicKey = "identity_providers: oidc: option 'issuer_private_key' must be a valid RSA private key but the provided data is missing the public key bits"
|
|
||||||
errFmtOIDCCertificateMismatch = "identity_providers: oidc: option 'issuer_private_key' does not appear to be the private key the certificate provided by option 'issuer_certificate_chain'"
|
|
||||||
errFmtOIDCCertificateChain = "identity_providers: oidc: option 'issuer_certificate_chain' produced an error during validation of the chain: %w"
|
|
||||||
errFmtOIDCEnforcePKCEInvalidValue = "identity_providers: oidc: option 'enforce_pkce' must be 'never', " +
|
|
||||||
"'public_clients_only' or 'always', but it's configured as '%s'"
|
"'public_clients_only' or 'always', but it's configured as '%s'"
|
||||||
|
errFmtOIDCProviderInsecureParameterEntropy = "openid connect provider: SECURITY ISSUE - minimum parameter entropy is " +
|
||||||
|
"configured to an unsafe value, it should be above 8 but it's configured to %d"
|
||||||
|
errFmtOIDCProviderPrivateKeysInvalid = "identity_providers: oidc: issuer_private_keys: key #%d: option 'key' must be a valid private key but the provided data is malformed as it's missing the public key bits"
|
||||||
|
errFmtOIDCProviderPrivateKeysCalcThumbprint = "identity_providers: oidc: issuer_private_keys: key #%d: option 'key' failed to calculate thumbprint to configure key id value: %w"
|
||||||
|
errFmtOIDCProviderPrivateKeysKeyIDLength = "identity_providers: oidc: issuer_private_keys: key #%d with key id '%s': option `key_id`` must be 7 characters or less"
|
||||||
|
errFmtOIDCProviderPrivateKeysAttributeNotUnique = "identity_providers: oidc: issuer_private_keys: key #%d with key id '%s': option '%s' must be unique"
|
||||||
|
errFmtOIDCProviderPrivateKeysKeyIDNotAlphaNumeric = "identity_providers: oidc: issuer_private_keys: key #%d with key id '%s': option 'key_id' must only have alphanumeric characters"
|
||||||
|
errFmtOIDCProviderPrivateKeysProperties = "identity_providers: oidc: issuer_private_keys: key #%d with key id '%s': option 'key' failed to get key properties: %w"
|
||||||
|
errFmtOIDCProviderPrivateKeysInvalidOptionOneOf = "identity_providers: oidc: issuer_private_keys: key #%d with key id '%s': option '%s' must be one of %s but it's configured as '%s'"
|
||||||
|
errFmtOIDCProviderPrivateKeysRSAKeyLessThan2048Bits = "identity_providers: oidc: issuer_private_keys: key #%d with key id '%s': option 'key' is an RSA %d bit private key but it must at minimum be a RSA 2048 bit private key"
|
||||||
|
errFmtOIDCProviderPrivateKeysKeyNotRSAOrECDSA = "identity_providers: oidc: issuer_private_keys: key #%d with key id '%s': option 'key' must be a RSA private key or ECDSA private key but it's type is %T"
|
||||||
|
errFmtOIDCProviderPrivateKeysKeyCertificateMismatch = "identity_providers: oidc: issuer_private_keys: key #%d with key id '%s': option 'certificate_chain' does not appear to contain the public key for the private key provided by option 'key'"
|
||||||
|
errFmtOIDCProviderPrivateKeysCertificateChainInvalid = "identity_providers: oidc: issuer_private_keys: key #%d with key id '%s': option 'certificate_chain' produced an error during validation of the chain: %w"
|
||||||
|
errFmtOIDCProviderPrivateKeysNoRS256 = "identity_providers: oidc: issuer_private_keys: keys: must at least have one key supporting the '%s' algorithm but only has %s"
|
||||||
|
|
||||||
errFmtOIDCCORSInvalidOrigin = "identity_providers: oidc: cors: option 'allowed_origins' contains an invalid value '%s' as it has a %s: origins must only be scheme, hostname, and an optional port"
|
errFmtOIDCCORSInvalidOrigin = "identity_providers: oidc: cors: option 'allowed_origins' contains an invalid value '%s' as it has a %s: origins must only be scheme, hostname, and an optional port"
|
||||||
errFmtOIDCCORSInvalidOriginWildcard = "identity_providers: oidc: cors: option 'allowed_origins' contains the wildcard origin '*' with more than one origin but the wildcard origin must be defined by itself"
|
errFmtOIDCCORSInvalidOriginWildcard = "identity_providers: oidc: cors: option 'allowed_origins' contains the wildcard origin '*' with more than one origin but the wildcard origin must be defined by itself"
|
||||||
|
@ -161,45 +171,61 @@ const (
|
||||||
errFmtOIDCClientsWithEmptyID = "identity_providers: oidc: clients: option 'id' is required but was absent on the clients in positions %s"
|
errFmtOIDCClientsWithEmptyID = "identity_providers: oidc: clients: option 'id' is required but was absent on the clients in positions %s"
|
||||||
errFmtOIDCClientsDeprecated = "identity_providers: oidc: clients: warnings for clients above indicate deprecated functionality and it's strongly suggested these issues are checked and fixed if they're legitimate issues or reported if they are not as in a future version these warnings will become errors"
|
errFmtOIDCClientsDeprecated = "identity_providers: oidc: clients: warnings for clients above indicate deprecated functionality and it's strongly suggested these issues are checked and fixed if they're legitimate issues or reported if they are not as in a future version these warnings will become errors"
|
||||||
|
|
||||||
errFmtOIDCClientInvalidSecret = "identity_providers: oidc: client '%s': option 'secret' is required"
|
errFmtOIDCClientInvalidSecret = "identity_providers: oidc: clients: client '%s': option 'secret' is required"
|
||||||
errFmtOIDCClientInvalidSecretPlainText = "identity_providers: oidc: client '%s': option 'secret' is plaintext but for clients not using the 'token_endpoint_auth_method' of 'client_secret_jwt' it should be a hashed value as plaintext values are deprecated with the exception of 'client_secret_jwt' and will be removed when oidc becomes stable"
|
errFmtOIDCClientInvalidSecretPlainText = "identity_providers: oidc: clients: client '%s': option 'secret' is plaintext but for clients not using the 'token_endpoint_auth_method' of 'client_secret_jwt' it should be a hashed value as plaintext values are deprecated with the exception of 'client_secret_jwt' and will be removed when oidc becomes stable"
|
||||||
errFmtOIDCClientInvalidSecretNotPlainText = "identity_providers: oidc: client '%s': option 'secret' must be plaintext with option 'token_endpoint_auth_method' with a value of 'client_secret_jwt'"
|
errFmtOIDCClientInvalidSecretNotPlainText = "identity_providers: oidc: clients: client '%s': option 'secret' must be plaintext with option 'token_endpoint_auth_method' with a value of 'client_secret_jwt'"
|
||||||
errFmtOIDCClientPublicInvalidSecret = "identity_providers: oidc: client '%s': option 'secret' is " +
|
errFmtOIDCClientPublicInvalidSecret = "identity_providers: oidc: clients: 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: clients: 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"
|
||||||
errFmtOIDCClientRedirectURIPublic = "identity_providers: oidc: client '%s': option 'redirect_uris' has the " +
|
errFmtOIDCClientRedirectURIPublic = "identity_providers: oidc: clients: client '%s': option 'redirect_uris' has the " +
|
||||||
"redirect uri '%s' when option 'public' is false but this is invalid as this uri is not valid " +
|
"redirect uri '%s' when option 'public' is false but this is invalid as this uri is not valid " +
|
||||||
"for the openid connect confidential client type"
|
"for the openid connect confidential client type"
|
||||||
errFmtOIDCClientRedirectURIAbsolute = "identity_providers: oidc: client '%s': option 'redirect_uris' has an " +
|
errFmtOIDCClientRedirectURIAbsolute = "identity_providers: oidc: clients: client '%s': option 'redirect_uris' has an " +
|
||||||
"invalid value: redirect uri '%s' must have a scheme but it's absent"
|
"invalid value: redirect uri '%s' must have a scheme but it's absent"
|
||||||
errFmtOIDCClientInvalidConsentMode = "identity_providers: oidc: client '%s': consent: option 'mode' must be one of " +
|
errFmtOIDCClientInvalidConsentMode = "identity_providers: oidc: clients: client '%s': consent: option 'mode' must be one of " +
|
||||||
"%s but it's configured as '%s'"
|
"%s but it's configured as '%s'"
|
||||||
errFmtOIDCClientInvalidEntries = "identity_providers: oidc: client '%s': option '%s' must only have the values " +
|
errFmtOIDCClientInvalidEntries = "identity_providers: oidc: clients: client '%s': option '%s' must only have the values " +
|
||||||
"%s but the values %s are present"
|
"%s but the values %s are present"
|
||||||
errFmtOIDCClientInvalidEntryDuplicates = "identity_providers: oidc: client '%s': option '%s' must have unique values but the values %s are duplicated"
|
errFmtOIDCClientInvalidEntryDuplicates = "identity_providers: oidc: clients: client '%s': option '%s' must have unique values but the values %s are duplicated"
|
||||||
errFmtOIDCClientInvalidValue = "identity_providers: oidc: client '%s': option " +
|
errFmtOIDCClientInvalidValue = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
"'%s' must be one of %s but it's configured as '%s'"
|
"'%s' must be one of %s but it's configured as '%s'"
|
||||||
errFmtOIDCClientInvalidTokenEndpointAuthMethod = "identity_providers: oidc: client '%s': option " +
|
errFmtOIDCClientInvalidTokenEndpointAuthMethod = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
"'token_endpoint_auth_method' must be one of %s when configured as the confidential client type unless it only includes implicit flow response types such as %s but it's configured as '%s'"
|
"'token_endpoint_auth_method' must be one of %s when configured as the confidential client type unless it only includes implicit flow response types such as %s but it's configured as '%s'"
|
||||||
errFmtOIDCClientInvalidTokenEndpointAuthMethodPublic = "identity_providers: oidc: client '%s': option " +
|
errFmtOIDCClientInvalidTokenEndpointAuthMethodPublic = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
"'token_endpoint_auth_method' must be 'none' when configured as the public client type but it's configured as '%s'"
|
"'token_endpoint_auth_method' must be 'none' when configured as the public client type but it's configured as '%s'"
|
||||||
errFmtOIDCClientInvalidTokenEndpointAuthSigAlg = "identity_providers: oidc: client '%s': option " +
|
errFmtOIDCClientInvalidTokenEndpointAuthSigAlg = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
"'token_endpoint_auth_signing_alg' must be %s when option 'token_endpoint_auth_method' is %s"
|
"'token_endpoint_auth_signing_alg' must be one of %s when option 'token_endpoint_auth_method' is configured to '%s'"
|
||||||
errFmtOIDCClientInvalidSectorIdentifier = "identity_providers: oidc: client '%s': option " +
|
errFmtOIDCClientInvalidTokenEndpointAuthSigAlgReg = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
|
"'token_endpoint_auth_signing_alg' must be one of registered public key algorithm values %s when option 'token_endpoint_auth_method' is configured to '%s'"
|
||||||
|
errFmtOIDCClientInvalidTokenEndpointAuthSigAlgMissingPrivateKeyJWT = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
|
"'token_endpoint_auth_signing_alg' is required when option 'token_endpoint_auth_method' is configured to 'private_key_jwt'"
|
||||||
|
errFmtOIDCClientInvalidPublicKeysPrivateKeyJWT = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
|
"'public_keys' is required with 'token_endpoint_auth_method' set to 'private_key_jwt'"
|
||||||
|
errFmtOIDCClientInvalidSectorIdentifier = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
"'sector_identifier' with value '%s': must be a URL with only the host component for example '%s' but it has a %s with the value '%s'"
|
"'sector_identifier' with value '%s': must be a URL with only the host component for example '%s' but it has a %s with the value '%s'"
|
||||||
errFmtOIDCClientInvalidSectorIdentifierWithoutValue = "identity_providers: oidc: client '%s': option " +
|
errFmtOIDCClientInvalidSectorIdentifierWithoutValue = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
"'sector_identifier' with value '%s': must be a URL with only the host component for example '%s' but it has a %s"
|
"'sector_identifier' with value '%s': must be a URL with only the host component for example '%s' but it has a %s"
|
||||||
errFmtOIDCClientInvalidSectorIdentifierHost = "identity_providers: oidc: client '%s': option " +
|
errFmtOIDCClientInvalidSectorIdentifierHost = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
"'sector_identifier' with value '%s': must be a URL with only the host component but appears to be invalid"
|
"'sector_identifier' with value '%s': must be a URL with only the host component but appears to be invalid"
|
||||||
errFmtOIDCClientInvalidGrantTypeMatch = "identity_providers: oidc: client '%s': option " +
|
errFmtOIDCClientInvalidGrantTypeMatch = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
"'grant_types' should only have grant type values which are valid with the configured 'response_types' for the client but '%s' expects a response type %s such as %s but the response types are %s"
|
"'grant_types' should only have grant type values which are valid with the configured 'response_types' for the client but '%s' expects a response type %s such as %s but the response types are %s"
|
||||||
errFmtOIDCClientInvalidGrantTypeRefresh = "identity_providers: oidc: client '%s': option " +
|
errFmtOIDCClientInvalidGrantTypeRefresh = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
"'grant_types' should only have the 'refresh_token' value if the client is also configured with the 'offline_access' scope"
|
"'grant_types' should only have the 'refresh_token' value if the client is also configured with the 'offline_access' scope"
|
||||||
errFmtOIDCClientInvalidRefreshTokenOptionWithoutCodeResponseType = "identity_providers: oidc: client '%s': option " +
|
errFmtOIDCClientInvalidRefreshTokenOptionWithoutCodeResponseType = "identity_providers: oidc: clients: client '%s': option " +
|
||||||
"'%s' should only have the values %s if the client is also configured with a 'response_type' such as %s which respond with authorization codes"
|
"'%s' should only have the values %s if the client is also configured with a 'response_type' such as %s which respond with authorization codes"
|
||||||
errFmtOIDCServerInsecureParameterEntropy = "openid connect provider: SECURITY ISSUE - minimum parameter entropy is " +
|
|
||||||
"configured to an unsafe value, it should be above 8 but it's configured to %d"
|
errFmtOIDCClientPublicKeysBothURIAndValuesConfigured = "identity_providers: oidc: clients: client '%s': public_keys: option 'uri' must not be defined at the same time as option 'values'"
|
||||||
|
errFmtOIDCClientPublicKeysURIInvalidScheme = "identity_providers: oidc: clients: client '%s': public_keys: option 'uri' must have the 'https' scheme but the scheme is '%s'"
|
||||||
|
errFmtOIDCClientPublicKeysProperties = "identity_providers: oidc: clients: client '%s': public_keys: values: key #%d with key id '%s': option 'key' failed to get key properties: %w"
|
||||||
|
errFmtOIDCClientPublicKeysInvalidOptionOneOf = "identity_providers: oidc: clients: client '%s': public_keys: values: key #%d with key id '%s': option '%s' must be one of %s but it's configured as '%s'"
|
||||||
|
errFmtOIDCClientPublicKeysInvalidOptionMissingOneOf = "identity_providers: oidc: clients: client '%s': public_keys: values: key #%d: option '%s' must be provided"
|
||||||
|
errFmtOIDCClientPublicKeysKeyMalformed = "identity_providers: oidc: clients: client '%s': public_keys: values: key #%d: option 'key' option 'key' must be a valid private key but the provided data is malformed as it's missing the public key bits"
|
||||||
|
errFmtOIDCClientPublicKeysRSAKeyLessThan2048Bits = "identity_providers: oidc: clients: client '%s': public_keys: values: key #%d with key id '%s': option 'key' is an RSA %d bit private key but it must at minimum be a RSA 2048 bit private key"
|
||||||
|
errFmtOIDCClientPublicKeysKeyNotRSAOrECDSA = "identity_providers: oidc: clients: client '%s': public_keys: values: key #%d with key id '%s': option 'key' must be a RSA public key or ECDSA public key but it's type is %T"
|
||||||
|
errFmtOIDCClientPublicKeysCertificateChainKeyMismatch = "identity_providers: oidc: clients: client '%s': public_keys: values: key #%d with key id '%s': option 'certificate_chain' does not appear to contain the public key for the public key provided by option 'key'"
|
||||||
|
errFmtOIDCClientPublicKeysCertificateChainInvalid = "identity_providers: oidc: clients: client '%s': public_keys: values: key #%d with key id '%s': option 'certificate_chain' produced an error during validation of the chain: %w"
|
||||||
|
errFmtOIDCClientPublicKeysROSAMissingAlgorithm = "identity_providers: oidc: clients: client '%s': option 'request_object_signing_alg' must be one of %s configured in the client option 'public_keys'"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WebAuthn Error constants.
|
// WebAuthn Error constants.
|
||||||
|
@ -290,9 +316,6 @@ const (
|
||||||
const (
|
const (
|
||||||
errFmtServerTLSCert = "server: tls: option 'key' must also be accompanied by option 'certificate'"
|
errFmtServerTLSCert = "server: tls: option 'key' must also be accompanied by option 'certificate'"
|
||||||
errFmtServerTLSKey = "server: tls: option 'certificate' must also be accompanied by option 'key'"
|
errFmtServerTLSKey = "server: tls: option 'certificate' must also be accompanied by option 'key'"
|
||||||
errFmtServerTLSFileNotExist = "server: tls: option '%s' the file '%s' does not exist"
|
|
||||||
errFmtServerTLSFileNotExistErr = "server: tls: option '%s' could not determine if the file '%s' exists: %w"
|
|
||||||
|
|
||||||
errFmtServerTLSClientAuthNoAuth = "server: tls: client authentication cannot be configured if no server certificate and key are provided"
|
errFmtServerTLSClientAuthNoAuth = "server: tls: client authentication cannot be configured if no server certificate and key are provided"
|
||||||
|
|
||||||
errFmtServerAddressLegacyAndModern = "server: option 'host' and 'port' can't be configured at the same time as 'address'"
|
errFmtServerAddressLegacyAndModern = "server: option 'host' and 'port' can't be configured at the same time as 'address'"
|
||||||
|
@ -402,13 +425,17 @@ var (
|
||||||
var validDefault2FAMethods = []string{"totp", "webauthn", "mobile_push"}
|
var validDefault2FAMethods = []string{"totp", "webauthn", "mobile_push"}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
attrOIDCKey = "key"
|
||||||
|
attrOIDCKeyID = "key_id"
|
||||||
|
attrOIDCKeyUse = "use"
|
||||||
|
attrOIDCAlgorithm = "algorithm"
|
||||||
attrOIDCScopes = "scopes"
|
attrOIDCScopes = "scopes"
|
||||||
attrOIDCResponseTypes = "response_types"
|
attrOIDCResponseTypes = "response_types"
|
||||||
attrOIDCResponseModes = "response_modes"
|
attrOIDCResponseModes = "response_modes"
|
||||||
attrOIDCGrantTypes = "grant_types"
|
attrOIDCGrantTypes = "grant_types"
|
||||||
attrOIDCRedirectURIs = "redirect_uris"
|
attrOIDCRedirectURIs = "redirect_uris"
|
||||||
attrOIDCTokenAuthMethod = "token_endpoint_auth_method"
|
attrOIDCTokenAuthMethod = "token_endpoint_auth_method"
|
||||||
attrOIDCUsrSigAlg = "userinfo_signing_algorithm"
|
attrOIDCUsrSigAlg = "userinfo_signing_alg"
|
||||||
attrOIDCIDTokenSigAlg = "id_token_signing_alg"
|
attrOIDCIDTokenSigAlg = "id_token_signing_alg"
|
||||||
attrOIDCPKCEChallengeMethod = "pkce_challenge_method"
|
attrOIDCPKCEChallengeMethod = "pkce_challenge_method"
|
||||||
)
|
)
|
||||||
|
@ -425,9 +452,9 @@ var (
|
||||||
validOIDCClientResponseTypesRefreshToken = []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeHybridFlowIDToken, oidc.ResponseTypeHybridFlowToken, oidc.ResponseTypeHybridFlowBoth}
|
validOIDCClientResponseTypesRefreshToken = []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeHybridFlowIDToken, oidc.ResponseTypeHybridFlowToken, oidc.ResponseTypeHybridFlowBoth}
|
||||||
validOIDCClientGrantTypes = []string{oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeAuthorizationCode}
|
validOIDCClientGrantTypes = []string{oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeAuthorizationCode}
|
||||||
|
|
||||||
validOIDCClientTokenEndpointAuthMethods = []string{oidc.ClientAuthMethodNone, oidc.ClientAuthMethodClientSecretPost, oidc.ClientAuthMethodClientSecretBasic, oidc.ClientAuthMethodClientSecretJWT}
|
validOIDCClientTokenEndpointAuthMethods = []string{oidc.ClientAuthMethodNone, oidc.ClientAuthMethodClientSecretPost, oidc.ClientAuthMethodClientSecretBasic, oidc.ClientAuthMethodPrivateKeyJWT, oidc.ClientAuthMethodClientSecretJWT}
|
||||||
validOIDCClientTokenEndpointAuthMethodsConfidential = []string{oidc.ClientAuthMethodClientSecretPost, oidc.ClientAuthMethodClientSecretBasic}
|
validOIDCClientTokenEndpointAuthMethodsConfidential = []string{oidc.ClientAuthMethodClientSecretPost, oidc.ClientAuthMethodClientSecretBasic, oidc.ClientAuthMethodPrivateKeyJWT}
|
||||||
validOIDCClientTokenEndpointAuthSigAlgs = []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512}
|
validOIDCClientTokenEndpointAuthSigAlgsClientSecretJWT = []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512}
|
||||||
validOIDCIssuerJWKSigningAlgs = []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgRSAPSSUsingSHA256, oidc.SigningAlgECDSAUsingP256AndSHA256, oidc.SigningAlgRSAUsingSHA384, oidc.SigningAlgRSAPSSUsingSHA384, oidc.SigningAlgECDSAUsingP384AndSHA384, oidc.SigningAlgRSAUsingSHA512, oidc.SigningAlgRSAPSSUsingSHA512, oidc.SigningAlgECDSAUsingP521AndSHA512}
|
validOIDCIssuerJWKSigningAlgs = []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgRSAPSSUsingSHA256, oidc.SigningAlgECDSAUsingP256AndSHA256, oidc.SigningAlgRSAUsingSHA384, oidc.SigningAlgRSAPSSUsingSHA384, oidc.SigningAlgECDSAUsingP384AndSHA384, oidc.SigningAlgRSAUsingSHA512, oidc.SigningAlgRSAPSSUsingSHA512, oidc.SigningAlgECDSAUsingP521AndSHA512}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ const (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
exampleDotCom = "example.com"
|
exampleDotCom = "example.com"
|
||||||
|
rs256 = "rs256"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gopkg.in/square/go-jose.v2"
|
jose "gopkg.in/square/go-jose.v2"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
"github.com/authelia/authelia/v4/internal/oidc"
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
|
@ -33,20 +33,23 @@ func validateOIDC(config *schema.OpenIDConnectConfiguration, val *schema.StructV
|
||||||
|
|
||||||
validateOIDCIssuer(config, val)
|
validateOIDCIssuer(config, val)
|
||||||
|
|
||||||
sort.Sort(oidc.SortedSigningAlgs(config.Discovery.RegisteredJWKSigningAlgs))
|
sort.Sort(oidc.SortedSigningAlgs(config.Discovery.ResponseObjectSigningAlgs))
|
||||||
|
|
||||||
if config.MinimumParameterEntropy != 0 && config.MinimumParameterEntropy < 8 {
|
if config.MinimumParameterEntropy != 0 && config.MinimumParameterEntropy < 8 {
|
||||||
val.PushWarning(fmt.Errorf(errFmtOIDCServerInsecureParameterEntropy, config.MinimumParameterEntropy))
|
val.PushWarning(fmt.Errorf(errFmtOIDCProviderInsecureParameterEntropy, config.MinimumParameterEntropy))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.EnforcePKCE != "never" && config.EnforcePKCE != "public_clients_only" && config.EnforcePKCE != "always" {
|
switch config.EnforcePKCE {
|
||||||
val.Push(fmt.Errorf(errFmtOIDCEnforcePKCEInvalidValue, config.EnforcePKCE))
|
case "always", "never", "public_clients_only":
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCProviderEnforcePKCEInvalidValue, config.EnforcePKCE))
|
||||||
}
|
}
|
||||||
|
|
||||||
validateOIDCOptionsCORS(config, val)
|
validateOIDCOptionsCORS(config, val)
|
||||||
|
|
||||||
if len(config.Clients) == 0 {
|
if len(config.Clients) == 0 {
|
||||||
val.Push(fmt.Errorf(errFmtOIDCNoClientsConfigured))
|
val.Push(fmt.Errorf(errFmtOIDCProviderNoClientsConfigured))
|
||||||
} else {
|
} else {
|
||||||
validateOIDCClients(config, val)
|
validateOIDCClients(config, val)
|
||||||
}
|
}
|
||||||
|
@ -55,160 +58,161 @@ func validateOIDC(config *schema.OpenIDConnectConfiguration, val *schema.StructV
|
||||||
func validateOIDCIssuer(config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
func validateOIDCIssuer(config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||||
switch {
|
switch {
|
||||||
case config.IssuerPrivateKey != nil:
|
case config.IssuerPrivateKey != nil:
|
||||||
validateOIDCIssuerLegacy(config, val)
|
validateOIDCIssuerPrivateKey(config)
|
||||||
|
|
||||||
fallthrough
|
fallthrough
|
||||||
case len(config.IssuerJWKS) != 0:
|
case len(config.IssuerPrivateKeys) != 0:
|
||||||
validateOIDCIssuerModern(config, val)
|
validateOIDCIssuerPrivateKeys(config, val)
|
||||||
default:
|
default:
|
||||||
val.Push(fmt.Errorf(errFmtOIDCNoPrivateKey))
|
val.Push(fmt.Errorf(errFmtOIDCProviderNoPrivateKey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateOIDCIssuerLegacy(config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
func validateOIDCIssuerPrivateKey(config *schema.OpenIDConnectConfiguration) {
|
||||||
j := &jose.JSONWebKey{Key: &config.IssuerPrivateKey.PublicKey}
|
config.IssuerPrivateKeys = append([]schema.JWK{{
|
||||||
|
|
||||||
thumbprint, err := j.Thumbprint(crypto.SHA1)
|
|
||||||
if err != nil {
|
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: option 'issuer_private_key' failed to calculate thumbprint to configure key id value: %w", err))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config.IssuerJWKS = append(config.IssuerJWKS, schema.JWK{
|
|
||||||
KeyID: fmt.Sprintf("%x", thumbprint)[:6],
|
|
||||||
Algorithm: oidc.SigningAlgRSAUsingSHA256,
|
Algorithm: oidc.SigningAlgRSAUsingSHA256,
|
||||||
Use: oidc.KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Key: config.IssuerPrivateKey,
|
Key: config.IssuerPrivateKey,
|
||||||
CertificateChain: config.IssuerCertificateChain,
|
CertificateChain: config.IssuerCertificateChain,
|
||||||
})
|
}}, config.IssuerPrivateKeys...)
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocyclo // Refactor time permitting.
|
func jwkCalculateThumbprint(key schema.CryptographicKey) (thumbprintStr string, err error) {
|
||||||
func validateOIDCIssuerModern(config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
j := jose.JSONWebKey{}
|
||||||
|
|
||||||
|
switch k := key.(type) {
|
||||||
|
case schema.CryptographicPrivateKey:
|
||||||
|
j.Key = k.Public()
|
||||||
|
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
|
||||||
|
j.Key = k
|
||||||
|
default:
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var thumbprint []byte
|
||||||
|
|
||||||
|
if thumbprint, err = j.Thumbprint(crypto.SHA256); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%x", thumbprint)[:6], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateOIDCIssuerPrivateKeys(config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||||
var (
|
var (
|
||||||
props *JWKProperties
|
props *JWKProperties
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
kids := make([]string, len(config.IssuerJWKS))
|
kids := make([]string, len(config.IssuerPrivateKeys))
|
||||||
|
|
||||||
for i := 0; i < len(config.IssuerJWKS); i++ {
|
for i := 0; i < len(config.IssuerPrivateKeys); i++ {
|
||||||
if key, ok := config.IssuerJWKS[i].Key.(*rsa.PrivateKey); ok && key.PublicKey.N == nil {
|
if key, ok := config.IssuerPrivateKeys[i].Key.(*rsa.PrivateKey); ok && key.PublicKey.N == nil {
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d: option 'key' must be a valid RSA private key but the provided data is malformed as it's missing the public key bits", i+1))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysInvalid, i+1))
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch n := len(config.IssuerJWKS[i].KeyID); {
|
switch n := len(config.IssuerPrivateKeys[i].KeyID); {
|
||||||
case n == 0:
|
case n == 0:
|
||||||
j := jose.JSONWebKey{}
|
if config.IssuerPrivateKeys[i].KeyID, err = jwkCalculateThumbprint(config.IssuerPrivateKeys[i].Key); err != nil {
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysCalcThumbprint, i+1, err))
|
||||||
switch key := config.IssuerJWKS[i].Key.(type) {
|
|
||||||
case schema.CryptographicPrivateKey:
|
|
||||||
j.Key = key.Public()
|
|
||||||
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
|
|
||||||
j.Key = key
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if j.Key == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
var thumbprint []byte
|
|
||||||
|
|
||||||
if thumbprint, err = j.Thumbprint(crypto.SHA1); err != nil {
|
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d: option 'key' failed to calculate thumbprint to configure key id value: %w", i+1, err))
|
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
config.IssuerJWKS[i].KeyID = fmt.Sprintf("%x", thumbprint)[:6]
|
|
||||||
case n > 7:
|
case n > 7:
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option `key_id`` must be 7 characters or less", i+1, config.IssuerJWKS[i].KeyID))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysKeyIDLength, i+1, config.IssuerPrivateKeys[i].KeyID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.IssuerJWKS[i].KeyID != "" && utils.IsStringInSlice(config.IssuerJWKS[i].KeyID, kids) {
|
if config.IssuerPrivateKeys[i].KeyID != "" && utils.IsStringInSlice(config.IssuerPrivateKeys[i].KeyID, kids) {
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key_id' must be unique", i+1, config.IssuerJWKS[i].KeyID))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysAttributeNotUnique, i+1, config.IssuerPrivateKeys[i].KeyID, attrOIDCKeyID))
|
||||||
}
|
}
|
||||||
|
|
||||||
kids[i] = config.IssuerJWKS[i].KeyID
|
kids[i] = config.IssuerPrivateKeys[i].KeyID
|
||||||
|
|
||||||
if !utils.IsStringAlphaNumeric(config.IssuerJWKS[i].KeyID) {
|
if !utils.IsStringAlphaNumeric(config.IssuerPrivateKeys[i].KeyID) {
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key_id' must only have alphanumeric characters", i+1, config.IssuerJWKS[i].KeyID))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysKeyIDNotAlphaNumeric, i+1, config.IssuerPrivateKeys[i].KeyID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if props, err = schemaJWKGetProperties(config.IssuerJWKS[i]); err != nil {
|
if props, err = schemaJWKGetProperties(config.IssuerPrivateKeys[i]); err != nil {
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key' failed to get key properties: %w", i+1, config.IssuerJWKS[i].KeyID, err))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysProperties, i+1, config.IssuerPrivateKeys[i].KeyID, err))
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch config.IssuerJWKS[i].Use {
|
validateOIDCIssuerPrivateKeysUseAlg(i, props, config, val)
|
||||||
|
validateOIDCIssuerPrivateKeyPair(i, config, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.Discovery.ResponseObjectSigningAlgs) != 0 && !utils.IsStringInSlice(oidc.SigningAlgRSAUsingSHA256, config.Discovery.ResponseObjectSigningAlgs) {
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysNoRS256, oidc.SigningAlgRSAUsingSHA256, strJoinAnd(config.Discovery.ResponseObjectSigningAlgs)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateOIDCIssuerPrivateKeysUseAlg(i int, props *JWKProperties, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||||
|
switch config.IssuerPrivateKeys[i].Use {
|
||||||
case "":
|
case "":
|
||||||
config.IssuerJWKS[i].Use = props.Use
|
config.IssuerPrivateKeys[i].Use = props.Use
|
||||||
case oidc.KeyUseSignature:
|
case oidc.KeyUseSignature:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option '%s' must be one of %s but it's configured as '%s'", i+1, config.IssuerJWKS[i].KeyID, "use", strJoinOr([]string{oidc.KeyUseSignature}), config.IssuerJWKS[i].Use))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysInvalidOptionOneOf, i+1, config.IssuerPrivateKeys[i].KeyID, attrOIDCKeyUse, strJoinOr([]string{oidc.KeyUseSignature}), config.IssuerPrivateKeys[i].Use))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.IssuerJWKS[i].Algorithm == "":
|
case config.IssuerPrivateKeys[i].Algorithm == "":
|
||||||
config.IssuerJWKS[i].Algorithm = props.Algorithm
|
config.IssuerPrivateKeys[i].Algorithm = props.Algorithm
|
||||||
case utils.IsStringInSlice(config.IssuerJWKS[i].Algorithm, validOIDCIssuerJWKSigningAlgs):
|
case utils.IsStringInSlice(config.IssuerPrivateKeys[i].Algorithm, validOIDCIssuerJWKSigningAlgs):
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option '%s' must be one of %s but it's configured as '%s'", i+1, config.IssuerJWKS[i].KeyID, "algorithm", strJoinOr(validOIDCIssuerJWKSigningAlgs), config.IssuerJWKS[i].Algorithm))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysInvalidOptionOneOf, i+1, config.IssuerPrivateKeys[i].KeyID, attrOIDCAlgorithm, strJoinOr(validOIDCIssuerJWKSigningAlgs), config.IssuerPrivateKeys[i].Algorithm))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.IssuerJWKS[i].Algorithm != "" {
|
if config.IssuerPrivateKeys[i].Algorithm != "" {
|
||||||
if utils.IsStringInSlice(config.IssuerJWKS[i].Algorithm, config.Discovery.RegisteredJWKSigningAlgs) {
|
if utils.IsStringInSlice(config.IssuerPrivateKeys[i].Algorithm, config.Discovery.ResponseObjectSigningAlgs) {
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'algorithm' must be unique but another key is using it", i+1, config.IssuerJWKS[i].KeyID))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysAttributeNotUnique, i+1, config.IssuerPrivateKeys[i].KeyID, attrOIDCAlgorithm))
|
||||||
} else {
|
} else {
|
||||||
config.Discovery.RegisteredJWKSigningAlgs = append(config.Discovery.RegisteredJWKSigningAlgs, config.IssuerJWKS[i].Algorithm)
|
config.Discovery.ResponseObjectSigningAlgs = append(config.Discovery.ResponseObjectSigningAlgs, config.IssuerPrivateKeys[i].Algorithm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.IssuerJWKS[i].Algorithm == oidc.SigningAlgRSAUsingSHA256 && config.Discovery.DefaultKeyID == "" {
|
if config.IssuerPrivateKeys[i].Algorithm == oidc.SigningAlgRSAUsingSHA256 && config.Discovery.DefaultKeyID == "" {
|
||||||
config.Discovery.DefaultKeyID = config.IssuerJWKS[i].KeyID
|
config.Discovery.DefaultKeyID = config.IssuerPrivateKeys[i].KeyID
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkEqualKey bool
|
func validateOIDCIssuerPrivateKeyPair(i int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||||
|
var (
|
||||||
|
checkEqualKey bool
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
switch key := config.IssuerJWKS[i].Key.(type) {
|
switch key := config.IssuerPrivateKeys[i].Key.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
checkEqualKey = true
|
checkEqualKey = true
|
||||||
|
|
||||||
if key.Size() < 256 {
|
if key.Size() < 256 {
|
||||||
checkEqualKey = false
|
checkEqualKey = false
|
||||||
|
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key' is an RSA %d bit private key but it must be a RSA 2048 bit private key", i+1, config.IssuerJWKS[i].KeyID, key.Size()*8))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysRSAKeyLessThan2048Bits, i+1, config.IssuerPrivateKeys[i].KeyID, key.Size()*8))
|
||||||
}
|
}
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PrivateKey:
|
||||||
checkEqualKey = true
|
checkEqualKey = true
|
||||||
default:
|
default:
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key' must be a *rsa.PrivateKey or *ecdsa.PrivateKey but it's a %T", i+1, config.IssuerJWKS[i].KeyID, key))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysKeyNotRSAOrECDSA, i+1, config.IssuerPrivateKeys[i].KeyID, key))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.IssuerJWKS[i].CertificateChain.HasCertificates() {
|
if config.IssuerPrivateKeys[i].CertificateChain.HasCertificates() {
|
||||||
if checkEqualKey && !config.IssuerJWKS[i].CertificateChain.EqualKey(config.IssuerJWKS[i].Key) {
|
if checkEqualKey && !config.IssuerPrivateKeys[i].CertificateChain.EqualKey(config.IssuerPrivateKeys[i].Key) {
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key' does not appear to be the private key the certificate provided by option 'certificate_chain'", i+1, config.IssuerJWKS[i].KeyID))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysKeyCertificateMismatch, i+1, config.IssuerPrivateKeys[i].KeyID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = config.IssuerJWKS[i].CertificateChain.Validate(); err != nil {
|
if err = config.IssuerPrivateKeys[i].CertificateChain.Validate(); err != nil {
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'certificate_chain' produced an error during validation of the chain: %w", i+1, config.IssuerJWKS[i].KeyID, err))
|
val.Push(fmt.Errorf(errFmtOIDCProviderPrivateKeysCertificateChainInvalid, i+1, config.IssuerPrivateKeys[i].KeyID, err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(config.Discovery.RegisteredJWKSigningAlgs) != 0 && !utils.IsStringInSlice(oidc.SigningAlgRSAUsingSHA256, config.Discovery.RegisteredJWKSigningAlgs) {
|
|
||||||
val.Push(fmt.Errorf("identity_providers: oidc: issuer_jwks: keys: must at least have one key supporting the '%s' algorithm but only has %s", oidc.SigningAlgRSAUsingSHA256, strJoinAnd(config.Discovery.RegisteredJWKSigningAlgs)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setOIDCDefaults(config *schema.OpenIDConnectConfiguration) {
|
func setOIDCDefaults(config *schema.OpenIDConnectConfiguration) {
|
||||||
if config.AccessTokenLifespan == time.Duration(0) {
|
if config.AccessTokenLifespan == time.Duration(0) {
|
||||||
config.AccessTokenLifespan = schema.DefaultOpenIDConnectConfiguration.AccessTokenLifespan
|
config.AccessTokenLifespan = schema.DefaultOpenIDConnectConfiguration.AccessTokenLifespan
|
||||||
|
@ -356,7 +360,7 @@ func validateOIDCClient(c int, config *schema.OpenIDConnectConfiguration, val *s
|
||||||
case policyOneFactor, policyTwoFactor:
|
case policyOneFactor, policyTwoFactor:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue, config.Clients[c].ID, "policy", strJoinOr([]string{policyOneFactor, policyTwoFactor}), config.Clients[c].Policy))
|
val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue, config.Clients[c].ID, "authorization_policy", strJoinOr([]string{policyOneFactor, policyTwoFactor}), config.Clients[c].Policy))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch config.Clients[c].PKCEChallengeMethod {
|
switch config.Clients[c].PKCEChallengeMethod {
|
||||||
|
@ -374,10 +378,113 @@ func validateOIDCClient(c int, config *schema.OpenIDConnectConfiguration, val *s
|
||||||
validateOIDCClientGrantTypes(c, config, val, errDeprecatedFunc)
|
validateOIDCClientGrantTypes(c, config, val, errDeprecatedFunc)
|
||||||
validateOIDCClientRedirectURIs(c, config, val, errDeprecatedFunc)
|
validateOIDCClientRedirectURIs(c, config, val, errDeprecatedFunc)
|
||||||
|
|
||||||
validateOIDCClientTokenEndpointAuth(c, config, val)
|
|
||||||
validateOIDDClientSigningAlgs(c, config, val)
|
validateOIDDClientSigningAlgs(c, config, val)
|
||||||
|
|
||||||
validateOIDCClientSectorIdentifier(c, config, val)
|
validateOIDCClientSectorIdentifier(c, config, val)
|
||||||
|
|
||||||
|
validateOIDCClientPublicKeys(c, config, val)
|
||||||
|
validateOIDCClientTokenEndpointAuth(c, config, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateOIDCClientPublicKeys(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||||
|
switch {
|
||||||
|
case config.Clients[c].PublicKeys.URI != nil && len(config.Clients[c].PublicKeys.Values) != 0:
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysBothURIAndValuesConfigured, config.Clients[c].ID))
|
||||||
|
case config.Clients[c].PublicKeys.URI != nil:
|
||||||
|
if config.Clients[c].PublicKeys.URI.Scheme != schemeHTTPS {
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysURIInvalidScheme, config.Clients[c].ID, config.Clients[c].PublicKeys.URI.Scheme))
|
||||||
|
}
|
||||||
|
case len(config.Clients[c].PublicKeys.Values) != 0:
|
||||||
|
validateOIDCClientJSONWebKeysList(c, config, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateOIDCClientJSONWebKeysList(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||||
|
var (
|
||||||
|
props *JWKProperties
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := 0; i < len(config.Clients[c].PublicKeys.Values); i++ {
|
||||||
|
if config.Clients[c].PublicKeys.Values[i].KeyID == "" {
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysInvalidOptionMissingOneOf, config.Clients[c].ID, i+1, attrOIDCKeyID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if props, err = schemaJWKGetProperties(config.Clients[c].PublicKeys.Values[i]); err != nil {
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysProperties, config.Clients[c].ID, i+1, config.Clients[c].PublicKeys.Values[i].KeyID, err))
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
validateOIDCClientJSONWebKeysListKeyUseAlg(c, i, props, config, val)
|
||||||
|
|
||||||
|
var checkEqualKey bool
|
||||||
|
|
||||||
|
switch key := config.Clients[c].PublicKeys.Values[i].Key.(type) {
|
||||||
|
case nil:
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysInvalidOptionMissingOneOf, config.Clients[c].ID, i+1, attrOIDCKey))
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
checkEqualKey = true
|
||||||
|
|
||||||
|
if key.N == nil {
|
||||||
|
checkEqualKey = false
|
||||||
|
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysKeyMalformed, config.Clients[c].ID, i+1))
|
||||||
|
} else if key.Size() < 256 {
|
||||||
|
checkEqualKey = false
|
||||||
|
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysRSAKeyLessThan2048Bits, config.Clients[c].ID, i+1, config.Clients[c].PublicKeys.Values[i].KeyID, key.Size()*8))
|
||||||
|
}
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
checkEqualKey = true
|
||||||
|
default:
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysKeyNotRSAOrECDSA, config.Clients[c].ID, i+1, config.Clients[c].PublicKeys.Values[i].KeyID, key))
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Clients[c].PublicKeys.Values[i].CertificateChain.HasCertificates() {
|
||||||
|
if checkEqualKey && !config.Clients[c].PublicKeys.Values[i].CertificateChain.EqualKey(config.Clients[c].PublicKeys.Values[i].Key) {
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysCertificateChainKeyMismatch, config.Clients[c].ID, i+1, config.Clients[c].PublicKeys.Values[i].KeyID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = config.Clients[c].PublicKeys.Values[i].CertificateChain.Validate(); err != nil {
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysCertificateChainInvalid, config.Clients[c].ID, i+1, config.Clients[c].PublicKeys.Values[i].KeyID, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Clients[c].RequestObjectSigningAlg != "" && !utils.IsStringInSlice(config.Clients[c].RequestObjectSigningAlg, config.Clients[c].Discovery.RequestObjectSigningAlgs) {
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysROSAMissingAlgorithm, config.Clients[c].ID, strJoinOr(config.Clients[c].Discovery.RequestObjectSigningAlgs)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateOIDCClientJSONWebKeysListKeyUseAlg(c, i int, props *JWKProperties, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||||
|
switch config.Clients[c].PublicKeys.Values[i].Use {
|
||||||
|
case "":
|
||||||
|
config.Clients[c].PublicKeys.Values[i].Use = props.Use
|
||||||
|
case oidc.KeyUseSignature:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysInvalidOptionOneOf, config.Clients[c].ID, i+1, config.Clients[c].PublicKeys.Values[i].KeyID, attrOIDCKeyUse, strJoinOr([]string{oidc.KeyUseSignature}), config.Clients[c].PublicKeys.Values[i].Use))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case config.Clients[c].PublicKeys.Values[i].Algorithm == "":
|
||||||
|
config.Clients[c].PublicKeys.Values[i].Algorithm = props.Algorithm
|
||||||
|
case utils.IsStringInSlice(config.Clients[c].PublicKeys.Values[i].Algorithm, validOIDCIssuerJWKSigningAlgs):
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientPublicKeysInvalidOptionOneOf, config.Clients[c].ID, i+1, config.Clients[c].PublicKeys.Values[i].KeyID, attrOIDCAlgorithm, strJoinOr(validOIDCIssuerJWKSigningAlgs), config.Clients[c].PublicKeys.Values[i].Algorithm))
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Clients[c].PublicKeys.Values[i].Algorithm != "" {
|
||||||
|
if !utils.IsStringInSlice(config.Clients[c].PublicKeys.Values[i].Algorithm, config.Discovery.RequestObjectSigningAlgs) {
|
||||||
|
config.Discovery.RequestObjectSigningAlgs = append(config.Discovery.RequestObjectSigningAlgs, config.Clients[c].PublicKeys.Values[i].Algorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.IsStringInSlice(config.Clients[c].PublicKeys.Values[i].Algorithm, config.Clients[c].Discovery.RequestObjectSigningAlgs) {
|
||||||
|
config.Clients[c].Discovery.RequestObjectSigningAlgs = append(config.Clients[c].Discovery.RequestObjectSigningAlgs, config.Clients[c].PublicKeys.Values[i].Algorithm)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateOIDCClientSectorIdentifier(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
func validateOIDCClientSectorIdentifier(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||||
|
@ -654,11 +761,34 @@ func validateOIDCClientTokenEndpointAuth(c int, config *schema.OpenIDConnectConf
|
||||||
case "":
|
case "":
|
||||||
break
|
break
|
||||||
case oidc.ClientAuthMethodClientSecretJWT:
|
case oidc.ClientAuthMethodClientSecretJWT:
|
||||||
|
validateOIDCClientTokenEndpointAuthClientSecretJWT(c, config, val)
|
||||||
|
case oidc.ClientAuthMethodPrivateKeyJWT:
|
||||||
|
validateOIDCClientTokenEndpointAuthPublicKeyJWT(config.Clients[c], val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateOIDCClientTokenEndpointAuthClientSecretJWT(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||||
switch {
|
switch {
|
||||||
case config.Clients[c].TokenEndpointAuthSigningAlg == "":
|
case config.Clients[c].TokenEndpointAuthSigningAlg == "":
|
||||||
config.Clients[c].TokenEndpointAuthSigningAlg = oidc.SigningAlgHMACUsingSHA256
|
config.Clients[c].TokenEndpointAuthSigningAlg = oidc.SigningAlgHMACUsingSHA256
|
||||||
case !utils.IsStringInSlice(config.Clients[c].TokenEndpointAuthSigningAlg, validOIDCClientTokenEndpointAuthSigAlgs):
|
case !utils.IsStringInSlice(config.Clients[c].TokenEndpointAuthSigningAlg, validOIDCClientTokenEndpointAuthSigAlgsClientSecretJWT):
|
||||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidTokenEndpointAuthSigAlg, config.Clients[c].ID, strJoinOr(validOIDCClientTokenEndpointAuthSigAlgs), config.Clients[c].TokenEndpointAuthMethod))
|
val.Push(fmt.Errorf(errFmtOIDCClientInvalidTokenEndpointAuthSigAlg, config.Clients[c].ID, strJoinOr(validOIDCClientTokenEndpointAuthSigAlgsClientSecretJWT), config.Clients[c].TokenEndpointAuthMethod))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateOIDCClientTokenEndpointAuthPublicKeyJWT(config schema.OpenIDConnectClientConfiguration, val *schema.StructValidator) {
|
||||||
|
switch {
|
||||||
|
case config.TokenEndpointAuthSigningAlg == "":
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientInvalidTokenEndpointAuthSigAlgMissingPrivateKeyJWT, config.ID))
|
||||||
|
case !utils.IsStringInSlice(config.TokenEndpointAuthSigningAlg, validOIDCIssuerJWKSigningAlgs):
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientInvalidTokenEndpointAuthSigAlg, config.ID, strJoinOr(validOIDCIssuerJWKSigningAlgs), config.TokenEndpointAuthMethod))
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.PublicKeys.URI == nil {
|
||||||
|
if len(config.PublicKeys.Values) == 0 {
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientInvalidPublicKeysPrivateKeyJWT, config.ID))
|
||||||
|
} else if len(config.Discovery.RequestObjectSigningAlgs) != 0 && !utils.IsStringInSlice(config.TokenEndpointAuthSigningAlg, config.Discovery.RequestObjectSigningAlgs) {
|
||||||
|
val.Push(fmt.Errorf(errFmtOIDCClientInvalidTokenEndpointAuthSigAlgReg, config.ID, strJoinOr(config.Discovery.RequestObjectSigningAlgs), config.TokenEndpointAuthMethod))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,15 +796,15 @@ func validateOIDCClientTokenEndpointAuth(c int, config *schema.OpenIDConnectConf
|
||||||
func validateOIDDClientSigningAlgs(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
func validateOIDDClientSigningAlgs(c int, config *schema.OpenIDConnectConfiguration, val *schema.StructValidator) {
|
||||||
if config.Clients[c].UserinfoSigningAlg == "" {
|
if config.Clients[c].UserinfoSigningAlg == "" {
|
||||||
config.Clients[c].UserinfoSigningAlg = schema.DefaultOpenIDConnectClientConfiguration.UserinfoSigningAlg
|
config.Clients[c].UserinfoSigningAlg = schema.DefaultOpenIDConnectClientConfiguration.UserinfoSigningAlg
|
||||||
} else if config.Clients[c].UserinfoSigningAlg != oidc.SigningAlgNone && !utils.IsStringInSlice(config.Clients[c].UserinfoSigningAlg, config.Discovery.RegisteredJWKSigningAlgs) {
|
} else if config.Clients[c].UserinfoSigningAlg != oidc.SigningAlgNone && !utils.IsStringInSlice(config.Clients[c].UserinfoSigningAlg, config.Discovery.ResponseObjectSigningAlgs) {
|
||||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue,
|
val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue,
|
||||||
config.Clients[c].ID, attrOIDCUsrSigAlg, strJoinOr(append(config.Discovery.RegisteredJWKSigningAlgs, oidc.SigningAlgNone)), config.Clients[c].UserinfoSigningAlg))
|
config.Clients[c].ID, attrOIDCUsrSigAlg, strJoinOr(append(config.Discovery.ResponseObjectSigningAlgs, oidc.SigningAlgNone)), config.Clients[c].UserinfoSigningAlg))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Clients[c].IDTokenSigningAlg == "" {
|
if config.Clients[c].IDTokenSigningAlg == "" {
|
||||||
config.Clients[c].IDTokenSigningAlg = schema.DefaultOpenIDConnectClientConfiguration.IDTokenSigningAlg
|
config.Clients[c].IDTokenSigningAlg = schema.DefaultOpenIDConnectClientConfiguration.IDTokenSigningAlg
|
||||||
} else if !utils.IsStringInSlice(config.Clients[c].IDTokenSigningAlg, config.Discovery.RegisteredJWKSigningAlgs) {
|
} else if !utils.IsStringInSlice(config.Clients[c].IDTokenSigningAlg, config.Discovery.ResponseObjectSigningAlgs) {
|
||||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue,
|
val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue,
|
||||||
config.Clients[c].ID, attrOIDCIDTokenSigAlg, strJoinOr(config.Discovery.RegisteredJWKSigningAlgs), config.Clients[c].IDTokenSigningAlg))
|
config.Clients[c].ID, attrOIDCIDTokenSigAlg, strJoinOr(config.Discovery.ResponseObjectSigningAlgs), config.Clients[c].IDTokenSigningAlg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -37,13 +37,7 @@ func validateNotifierTemplates(config *schema.NotifierConfiguration, validator *
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
switch _, err := os.Stat(config.TemplatePath); {
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
_, err = os.Stat(config.TemplatePath)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case os.IsNotExist(err):
|
case os.IsNotExist(err):
|
||||||
validator.Push(fmt.Errorf(errFmtNotifierTemplatePathNotExist, config.TemplatePath))
|
validator.Push(fmt.Errorf(errFmtNotifierTemplatePathNotExist, config.TemplatePath))
|
||||||
return
|
return
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
@ -284,3 +285,17 @@ func (suite *NotifierSuite) TestFileShouldEnsureFilenameIsProvided() {
|
||||||
func TestNotifierSuite(t *testing.T) {
|
func TestNotifierSuite(t *testing.T) {
|
||||||
suite.Run(t, new(NotifierSuite))
|
suite.Run(t, new(NotifierSuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNotifierMiscMissingTemplateTests(t *testing.T) {
|
||||||
|
config := &schema.NotifierConfiguration{
|
||||||
|
TemplatePath: string([]byte{0x0, 0x1}),
|
||||||
|
}
|
||||||
|
|
||||||
|
val := schema.NewStructValidator()
|
||||||
|
|
||||||
|
validateNotifierTemplates(config, val)
|
||||||
|
|
||||||
|
require.Len(t, val.Errors(), 1)
|
||||||
|
|
||||||
|
assert.EqualError(t, val.Errors()[0], "notifier: option 'template_path' refers to location '\x00\x01' which couldn't be opened: stat \x00\x01: invalid argument")
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -19,11 +20,11 @@ func ValidateServerTLS(config *schema.Configuration, validator *schema.StructVal
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Server.TLS.Key != "" {
|
if config.Server.TLS.Key != "" {
|
||||||
validateFileExists(config.Server.TLS.Key, validator, "key")
|
validateServerTLSFileExists("key", config.Server.TLS.Key, validator)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Server.TLS.Certificate != "" {
|
if config.Server.TLS.Certificate != "" {
|
||||||
validateFileExists(config.Server.TLS.Certificate, validator, "certificate")
|
validateServerTLSFileExists("certificate", config.Server.TLS.Certificate, validator)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Server.TLS.Key == "" && config.Server.TLS.Certificate == "" &&
|
if config.Server.TLS.Key == "" && config.Server.TLS.Certificate == "" &&
|
||||||
|
@ -32,7 +33,24 @@ func ValidateServerTLS(config *schema.Configuration, validator *schema.StructVal
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, clientCertPath := range config.Server.TLS.ClientCertificates {
|
for _, clientCertPath := range config.Server.TLS.ClientCertificates {
|
||||||
validateFileExists(clientCertPath, validator, "client_certificates")
|
validateServerTLSFileExists("client_certificates", clientCertPath, validator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateServerTLSFileExists checks whether a file exist.
|
||||||
|
func validateServerTLSFileExists(name, path string, validator *schema.StructValidator) {
|
||||||
|
var (
|
||||||
|
info os.FileInfo
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
switch info, err = os.Stat(path); {
|
||||||
|
case os.IsNotExist(err):
|
||||||
|
validator.Push(fmt.Errorf("server: tls: option '%s' with path '%s' refers to a file that doesn't exist", name, path))
|
||||||
|
case err != nil:
|
||||||
|
validator.Push(fmt.Errorf("server: tls: option '%s' with path '%s' could not be verified due to a file system error: %w", name, path, err))
|
||||||
|
case info.IsDir():
|
||||||
|
validator.Push(fmt.Errorf("server: tls: option '%s' with path '%s' refers to a directory but it should refer to a file", name, path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,13 +215,3 @@ func validateServerEndpointsAuthzStrategies(name string, strategies []schema.Ser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateFileExists checks whether a file exist.
|
|
||||||
func validateFileExists(path string, validator *schema.StructValidator, opt string) {
|
|
||||||
exist, err := utils.FileExists(path)
|
|
||||||
if err != nil {
|
|
||||||
validator.Push(fmt.Errorf(errFmtServerTLSFileNotExistErr, opt, path, err))
|
|
||||||
} else if !exist {
|
|
||||||
validator.Push(fmt.Errorf(errFmtServerTLSFileNotExist, opt, path))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package validator
|
package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -296,7 +297,7 @@ func TestShouldRaiseErrorWhenTLSCertDoesNotExist(t *testing.T) {
|
||||||
|
|
||||||
ValidateServer(&config, validator)
|
ValidateServer(&config, validator)
|
||||||
require.Len(t, validator.Errors(), 1)
|
require.Len(t, validator.Errors(), 1)
|
||||||
assert.EqualError(t, validator.Errors()[0], "server: tls: option 'certificate' the file '/tmp/unexisting_file' does not exist")
|
assert.EqualError(t, validator.Errors()[0], "server: tls: option 'certificate' with path '/tmp/unexisting_file' refers to a file that doesn't exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldRaiseErrorWhenTLSKeyWithoutCertIsProvided(t *testing.T) {
|
func TestShouldRaiseErrorWhenTLSKeyWithoutCertIsProvided(t *testing.T) {
|
||||||
|
@ -329,7 +330,7 @@ func TestShouldRaiseErrorWhenTLSKeyDoesNotExist(t *testing.T) {
|
||||||
|
|
||||||
ValidateServer(&config, validator)
|
ValidateServer(&config, validator)
|
||||||
require.Len(t, validator.Errors(), 1)
|
require.Len(t, validator.Errors(), 1)
|
||||||
assert.EqualError(t, validator.Errors()[0], "server: tls: option 'key' the file '/tmp/unexisting_file' does not exist")
|
assert.EqualError(t, validator.Errors()[0], "server: tls: option 'key' with path '/tmp/unexisting_file' refers to a file that doesn't exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotRaiseErrorWhenBothTLSCertificateAndKeyAreProvided(t *testing.T) {
|
func TestShouldNotRaiseErrorWhenBothTLSCertificateAndKeyAreProvided(t *testing.T) {
|
||||||
|
@ -373,7 +374,7 @@ func TestShouldRaiseErrorWhenTLSClientCertificateDoesNotExist(t *testing.T) {
|
||||||
|
|
||||||
ValidateServer(&config, validator)
|
ValidateServer(&config, validator)
|
||||||
require.Len(t, validator.Errors(), 1)
|
require.Len(t, validator.Errors(), 1)
|
||||||
assert.EqualError(t, validator.Errors()[0], "server: tls: option 'client_certificates' the file '/tmp/unexisting' does not exist")
|
assert.EqualError(t, validator.Errors()[0], "server: tls: option 'client_certificates' with path '/tmp/unexisting' refers to a file that doesn't exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldRaiseErrorWhenTLSClientAuthIsDefinedButNotServerCertificate(t *testing.T) {
|
func TestShouldRaiseErrorWhenTLSClientAuthIsDefinedButNotServerCertificate(t *testing.T) {
|
||||||
|
@ -569,3 +570,25 @@ func TestServerAuthzEndpointLegacyAsImplementationLegacyWhenBlank(t *testing.T)
|
||||||
|
|
||||||
assert.Equal(t, authzImplementationLegacy, config.Server.Endpoints.Authz[legacy].Implementation)
|
assert.Equal(t, authzImplementationLegacy, config.Server.Endpoints.Authz[legacy].Implementation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateTLSPathStatInvalidArgument(t *testing.T) {
|
||||||
|
val := schema.NewStructValidator()
|
||||||
|
|
||||||
|
validateServerTLSFileExists("key", string([]byte{0x0, 0x1}), val)
|
||||||
|
|
||||||
|
require.Len(t, val.Errors(), 1)
|
||||||
|
|
||||||
|
assert.EqualError(t, val.Errors()[0], "server: tls: option 'key' with path '\x00\x01' could not be verified due to a file system error: stat \x00\x01: invalid argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateTLSPathIsDir(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
|
||||||
|
val := schema.NewStructValidator()
|
||||||
|
|
||||||
|
validateServerTLSFileExists("key", dir, val)
|
||||||
|
|
||||||
|
require.Len(t, val.Errors(), 1)
|
||||||
|
|
||||||
|
assert.EqualError(t, val.Errors()[0], fmt.Sprintf("server: tls: option 'key' with path '%s' refers to a directory but it should refer to a file", dir))
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,10 @@ import (
|
||||||
|
|
||||||
// ValidateTLSConfig sets the default values and validates a schema.TLSConfig.
|
// ValidateTLSConfig sets the default values and validates a schema.TLSConfig.
|
||||||
func ValidateTLSConfig(config *schema.TLSConfig, configDefault *schema.TLSConfig) (err error) {
|
func ValidateTLSConfig(config *schema.TLSConfig, configDefault *schema.TLSConfig) (err error) {
|
||||||
|
if configDefault == nil {
|
||||||
|
return errors.New("must provide configDefault")
|
||||||
|
}
|
||||||
|
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -35,7 +39,7 @@ func ValidateTLSConfig(config *schema.TLSConfig, configDefault *schema.TLSConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.CertificateChain.HasCertificates() || config.PrivateKey != nil) && !config.CertificateChain.EqualKey(config.PrivateKey) {
|
if (config.CertificateChain.HasCertificates() || config.PrivateKey != nil) && !config.CertificateChain.EqualKey(config.PrivateKey) {
|
||||||
return errors.New("option 'certificates' is invalid: provided certificate is not the public key for the private key provided")
|
return errors.New("option 'certificates' is invalid: provided certificate does not contain the public key for the private key provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package validator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateTLSConfig(t *testing.T) {
|
||||||
|
var (
|
||||||
|
config, configDefault *schema.TLSConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.EqualError(t, ValidateTLSConfig(config, configDefault), "must provide configDefault")
|
||||||
|
|
||||||
|
configDefault = &schema.TLSConfig{}
|
||||||
|
|
||||||
|
assert.NoError(t, ValidateTLSConfig(config, configDefault))
|
||||||
|
|
||||||
|
config = &schema.TLSConfig{}
|
||||||
|
|
||||||
|
assert.NoError(t, ValidateTLSConfig(config, configDefault))
|
||||||
|
|
||||||
|
config.PrivateKey = keyRSA2048
|
||||||
|
config.CertificateChain = certRSA4096
|
||||||
|
|
||||||
|
assert.EqualError(t, ValidateTLSConfig(config, configDefault), "option 'certificates' is invalid: provided certificate does not contain the public key for the private key provided")
|
||||||
|
}
|
|
@ -125,12 +125,20 @@ type JWKProperties struct {
|
||||||
func schemaJWKGetProperties(jwk schema.JWK) (properties *JWKProperties, err error) {
|
func schemaJWKGetProperties(jwk schema.JWK) (properties *JWKProperties, err error) {
|
||||||
switch key := jwk.Key.(type) {
|
switch key := jwk.Key.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
return nil, fmt.Errorf("private key is nil")
|
return nil, nil
|
||||||
case ed25519.PrivateKey, ed25519.PublicKey:
|
case ed25519.PrivateKey, ed25519.PublicKey:
|
||||||
return &JWKProperties{}, nil
|
return &JWKProperties{}, nil
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
|
if key.PublicKey.N == nil {
|
||||||
|
return &JWKProperties{oidc.KeyUseSignature, oidc.SigningAlgRSAUsingSHA256, 0, nil}, nil
|
||||||
|
}
|
||||||
|
|
||||||
return &JWKProperties{oidc.KeyUseSignature, oidc.SigningAlgRSAUsingSHA256, key.Size(), nil}, nil
|
return &JWKProperties{oidc.KeyUseSignature, oidc.SigningAlgRSAUsingSHA256, key.Size(), nil}, nil
|
||||||
case *rsa.PublicKey:
|
case *rsa.PublicKey:
|
||||||
|
if key.N == nil {
|
||||||
|
return &JWKProperties{oidc.KeyUseSignature, oidc.SigningAlgRSAUsingSHA256, 0, nil}, nil
|
||||||
|
}
|
||||||
|
|
||||||
return &JWKProperties{oidc.KeyUseSignature, oidc.SigningAlgRSAUsingSHA256, key.Size(), nil}, nil
|
return &JWKProperties{oidc.KeyUseSignature, oidc.SigningAlgRSAUsingSHA256, key.Size(), nil}, nil
|
||||||
case *ecdsa.PublicKey:
|
case *ecdsa.PublicKey:
|
||||||
switch key.Curve {
|
switch key.Curve {
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
package validator
|
package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rsa"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsCookieDomainValid(t *testing.T) {
|
func TestIsCookieDomainValid(t *testing.T) {
|
||||||
|
@ -38,3 +43,39 @@ func TestIsCookieDomainValid(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildStringFuncsMissingTests(t *testing.T) {
|
||||||
|
assert.Equal(t, "", buildJoinedString(".", ":", "'", nil))
|
||||||
|
assert.Equal(t, "'abc', '123'", strJoinComma("", []string{"abc", "123"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSchemaJWKGetPropertiesMissingTests(t *testing.T) {
|
||||||
|
props, err := schemaJWKGetProperties(schema.JWK{Key: keyECDSAP224})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, oidc.KeyUseSignature, props.Use)
|
||||||
|
assert.Equal(t, "", props.Algorithm)
|
||||||
|
assert.Equal(t, elliptic.P224(), props.Curve)
|
||||||
|
assert.Equal(t, -1, props.Bits)
|
||||||
|
|
||||||
|
props, err = schemaJWKGetProperties(schema.JWK{Key: keyECDSAP224.Public()})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, oidc.KeyUseSignature, props.Use)
|
||||||
|
assert.Equal(t, "", props.Algorithm)
|
||||||
|
assert.Equal(t, elliptic.P224(), props.Curve)
|
||||||
|
assert.Equal(t, -1, props.Bits)
|
||||||
|
|
||||||
|
rsa := &rsa.PrivateKey{}
|
||||||
|
|
||||||
|
*rsa = *keyRSA2048
|
||||||
|
rsa.PublicKey.N = nil
|
||||||
|
|
||||||
|
props, err = schemaJWKGetProperties(schema.JWK{Key: rsa})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, oidc.KeyUseSignature, props.Use)
|
||||||
|
assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, props.Algorithm)
|
||||||
|
assert.Equal(t, nil, props.Curve)
|
||||||
|
assert.Equal(t, 0, props.Bits)
|
||||||
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ 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)
|
||||||
|
|
||||||
session := oidc.NewSessionWithAuthorizeRequest(issuer, ctx.Providers.OpenIDConnect.KeyManager.GetKIDFromAlg(ctx, client.GetIDTokenSigningAlg()),
|
session := oidc.NewSessionWithAuthorizeRequest(issuer, ctx.Providers.OpenIDConnect.KeyManager.GetKeyIDFromAlg(ctx, client.GetIDTokenSigningAlg()),
|
||||||
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",
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package oidc
|
package oidc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testAMRWant struct {
|
type testAMRWant struct {
|
||||||
|
@ -17,13 +19,13 @@ type testAMRWant struct {
|
||||||
func TestAuthenticationMethodsReferences(t *testing.T) {
|
func TestAuthenticationMethodsReferences(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
is AuthenticationMethodsReferences
|
is oidc.AuthenticationMethodsReferences
|
||||||
want testAMRWant
|
want testAMRWant
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "Username and Password",
|
desc: "Username and Password",
|
||||||
|
|
||||||
is: AuthenticationMethodsReferences{UsernameAndPassword: true},
|
is: oidc.AuthenticationMethodsReferences{UsernameAndPassword: true},
|
||||||
want: testAMRWant{
|
want: testAMRWant{
|
||||||
FactorKnowledge: true,
|
FactorKnowledge: true,
|
||||||
FactorPossession: false,
|
FactorPossession: false,
|
||||||
|
@ -37,7 +39,7 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "TOTP",
|
desc: "TOTP",
|
||||||
|
|
||||||
is: AuthenticationMethodsReferences{TOTP: true},
|
is: oidc.AuthenticationMethodsReferences{TOTP: true},
|
||||||
want: testAMRWant{
|
want: testAMRWant{
|
||||||
FactorKnowledge: false,
|
FactorKnowledge: false,
|
||||||
FactorPossession: true,
|
FactorPossession: true,
|
||||||
|
@ -51,7 +53,7 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "WebAuthn",
|
desc: "WebAuthn",
|
||||||
|
|
||||||
is: AuthenticationMethodsReferences{WebAuthn: true},
|
is: oidc.AuthenticationMethodsReferences{WebAuthn: true},
|
||||||
want: testAMRWant{
|
want: testAMRWant{
|
||||||
FactorKnowledge: false,
|
FactorKnowledge: false,
|
||||||
FactorPossession: true,
|
FactorPossession: true,
|
||||||
|
@ -65,7 +67,7 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "WebAuthn User Presence",
|
desc: "WebAuthn User Presence",
|
||||||
|
|
||||||
is: AuthenticationMethodsReferences{WebAuthnUserPresence: true},
|
is: oidc.AuthenticationMethodsReferences{WebAuthnUserPresence: true},
|
||||||
want: testAMRWant{
|
want: testAMRWant{
|
||||||
FactorKnowledge: false,
|
FactorKnowledge: false,
|
||||||
FactorPossession: false,
|
FactorPossession: false,
|
||||||
|
@ -79,7 +81,7 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "WebAuthn User Verified",
|
desc: "WebAuthn User Verified",
|
||||||
|
|
||||||
is: AuthenticationMethodsReferences{WebAuthnUserVerified: true},
|
is: oidc.AuthenticationMethodsReferences{WebAuthnUserVerified: true},
|
||||||
want: testAMRWant{
|
want: testAMRWant{
|
||||||
FactorKnowledge: false,
|
FactorKnowledge: false,
|
||||||
FactorPossession: false,
|
FactorPossession: false,
|
||||||
|
@ -93,7 +95,7 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "WebAuthn with User Presence and Verified",
|
desc: "WebAuthn with User Presence and Verified",
|
||||||
|
|
||||||
is: AuthenticationMethodsReferences{WebAuthn: true, WebAuthnUserVerified: true, WebAuthnUserPresence: true},
|
is: oidc.AuthenticationMethodsReferences{WebAuthn: true, WebAuthnUserVerified: true, WebAuthnUserPresence: true},
|
||||||
want: testAMRWant{
|
want: testAMRWant{
|
||||||
FactorKnowledge: false,
|
FactorKnowledge: false,
|
||||||
FactorPossession: true,
|
FactorPossession: true,
|
||||||
|
@ -107,7 +109,7 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Duo",
|
desc: "Duo",
|
||||||
|
|
||||||
is: AuthenticationMethodsReferences{Duo: true},
|
is: oidc.AuthenticationMethodsReferences{Duo: true},
|
||||||
want: testAMRWant{
|
want: testAMRWant{
|
||||||
FactorKnowledge: false,
|
FactorKnowledge: false,
|
||||||
FactorPossession: true,
|
FactorPossession: true,
|
||||||
|
@ -121,7 +123,7 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Duo WebAuthn TOTP",
|
desc: "Duo WebAuthn TOTP",
|
||||||
|
|
||||||
is: AuthenticationMethodsReferences{Duo: true, WebAuthn: true, TOTP: true},
|
is: oidc.AuthenticationMethodsReferences{Duo: true, WebAuthn: true, TOTP: true},
|
||||||
want: testAMRWant{
|
want: testAMRWant{
|
||||||
FactorKnowledge: false,
|
FactorKnowledge: false,
|
||||||
FactorPossession: true,
|
FactorPossession: true,
|
||||||
|
@ -135,7 +137,7 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Duo TOTP",
|
desc: "Duo TOTP",
|
||||||
|
|
||||||
is: AuthenticationMethodsReferences{Duo: true, TOTP: true},
|
is: oidc.AuthenticationMethodsReferences{Duo: true, TOTP: true},
|
||||||
want: testAMRWant{
|
want: testAMRWant{
|
||||||
FactorKnowledge: false,
|
FactorKnowledge: false,
|
||||||
FactorPossession: true,
|
FactorPossession: true,
|
||||||
|
@ -149,7 +151,7 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Username and Password with Duo",
|
desc: "Username and Password with Duo",
|
||||||
|
|
||||||
is: AuthenticationMethodsReferences{Duo: true, UsernameAndPassword: true},
|
is: oidc.AuthenticationMethodsReferences{Duo: true, UsernameAndPassword: true},
|
||||||
want: testAMRWant{
|
want: testAMRWant{
|
||||||
FactorKnowledge: true,
|
FactorKnowledge: true,
|
||||||
FactorPossession: true,
|
FactorPossession: true,
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-crypt/crypt"
|
||||||
|
"github.com/go-crypt/crypt/algorithm"
|
||||||
"github.com/go-crypt/crypt/algorithm/plaintext"
|
"github.com/go-crypt/crypt/algorithm/plaintext"
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
|
@ -20,6 +22,46 @@ import (
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewHasher returns a new Hasher.
|
||||||
|
func NewHasher() (hasher *Hasher, err error) {
|
||||||
|
hasher = &Hasher{}
|
||||||
|
|
||||||
|
if hasher.decoder, err = crypt.NewDefaultDecoder(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = plaintext.RegisterDecoderPlainText(hasher.decoder); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasher, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hasher implements the fosite.Hasher interface and adaptively compares hashes.
|
||||||
|
type Hasher struct {
|
||||||
|
decoder algorithm.DecoderRegister
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare compares the hash with the data and returns an error if they don't match.
|
||||||
|
func (h Hasher) Compare(_ context.Context, hash, data []byte) (err error) {
|
||||||
|
var digest algorithm.Digest
|
||||||
|
|
||||||
|
if digest, err = h.decoder.Decode(string(hash)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if digest.MatchBytes(data) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errPasswordsDoNotMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash creates a new hash from data.
|
||||||
|
func (h Hasher) Hash(_ context.Context, data []byte) (hash []byte, err error) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultClientAuthenticationStrategy is a copy of fosite's with the addition of the client_secret_jwt method and some
|
// DefaultClientAuthenticationStrategy is a copy of fosite's with the addition of the client_secret_jwt method and some
|
||||||
// minor superficial changes.
|
// minor superficial changes.
|
||||||
//
|
//
|
|
@ -1,14 +1,10 @@
|
||||||
package handlers
|
package oidc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/x509"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -21,6 +17,8 @@ import (
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
|
|
||||||
|
@ -31,6 +29,58 @@ import (
|
||||||
"github.com/authelia/authelia/v4/internal/oidc"
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
|
||||||
|
hasher, err := oidc.NewHasher()
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
a := []byte("$plaintext$abc")
|
||||||
|
b := []byte("abc")
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
assert.NoError(t, hasher.Compare(ctx, a, b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShouldNotRaiseErrorOnEqualPasswordsPlainTextWithSeparator(t *testing.T) {
|
||||||
|
hasher, err := oidc.NewHasher()
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
a := []byte("$plaintext$abc$123")
|
||||||
|
b := []byte("abc$123")
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
assert.NoError(t, hasher.Compare(ctx, a, b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
|
||||||
|
hasher, err := oidc.NewHasher()
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
a := []byte("$plaintext$abc")
|
||||||
|
b := []byte("abcd")
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
assert.EqualError(t, hasher.Compare(ctx, a, b), "The provided client secret did not match the registered client secret.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShouldHashPassword(t *testing.T) {
|
||||||
|
hasher := oidc.Hasher{}
|
||||||
|
|
||||||
|
data := []byte("abc")
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
hash, err := hasher.Hash(ctx, data)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, data, hash)
|
||||||
|
}
|
||||||
|
|
||||||
func TestClientAuthenticationStrategySuite(t *testing.T) {
|
func TestClientAuthenticationStrategySuite(t *testing.T) {
|
||||||
suite.Run(t, &ClientAuthenticationStrategySuite{})
|
suite.Run(t, &ClientAuthenticationStrategySuite{})
|
||||||
}
|
}
|
||||||
|
@ -135,7 +185,9 @@ func (s *ClientAuthenticationStrategySuite) GetAssertionRequest(token string) (r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) GetCtx() oidc.OpenIDConnectContext {
|
func (s *ClientAuthenticationStrategySuite) GetCtx() oidc.OpenIDConnectContext {
|
||||||
return &oidc.MockOpenIDConnectContext{
|
fmt.Println(s.GetIssuerURL())
|
||||||
|
|
||||||
|
return &MockOpenIDConnectContext{
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
MockIssuerURL: s.GetIssuerURL(),
|
MockIssuerURL: s.GetIssuerURL(),
|
||||||
}
|
}
|
||||||
|
@ -145,12 +197,12 @@ func (s *ClientAuthenticationStrategySuite) SetupTest() {
|
||||||
s.ctrl = gomock.NewController(s.T())
|
s.ctrl = gomock.NewController(s.T())
|
||||||
s.store = mocks.NewMockStorage(s.ctrl)
|
s.store = mocks.NewMockStorage(s.ctrl)
|
||||||
|
|
||||||
secret := MustDecodeSecret("$plaintext$client-secret")
|
secret := tOpenIDConnectPlainTextClientSecret
|
||||||
|
|
||||||
s.provider = oidc.NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
s.provider = oidc.NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerJWKS: []schema.JWK{},
|
IssuerPrivateKeys: []schema.JWK{
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
{Key: keyRSA2048, CertificateChain: certRSA2048, Use: oidc.KeyUseSignature, Algorithm: oidc.SigningAlgRSAUsingSHA256},
|
||||||
IssuerPrivateKey: MustParseRSAPrivateKey(exampleRSAPrivateKey),
|
},
|
||||||
HMACSecret: "abc123",
|
HMACSecret: "abc123",
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
|
@ -184,7 +236,7 @@ func (s *ClientAuthenticationStrategySuite) SetupTest() {
|
||||||
TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA512,
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA512,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "rs256",
|
ID: rs256,
|
||||||
Secret: secret,
|
Secret: secret,
|
||||||
Policy: authorization.OneFactor.String(),
|
Policy: authorization.OneFactor.String(),
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -273,6 +325,142 @@ func (s *ClientAuthenticationStrategySuite) SetupTest() {
|
||||||
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
||||||
TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP521AndSHA512,
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP521AndSHA512,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
ID: "rs256k",
|
||||||
|
Secret: secret,
|
||||||
|
Policy: authorization.OneFactor.String(),
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://client.example.com",
|
||||||
|
},
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
||||||
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA256,
|
||||||
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
|
Values: []schema.JWK{
|
||||||
|
{KeyID: rs256, Key: keyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAUsingSHA256, Use: oidc.KeyUseSignature},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "rs384k",
|
||||||
|
Secret: secret,
|
||||||
|
Policy: authorization.OneFactor.String(),
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://client.example.com",
|
||||||
|
},
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
||||||
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA384,
|
||||||
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
|
Values: []schema.JWK{
|
||||||
|
{KeyID: "rs384", Key: keyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAUsingSHA384, Use: oidc.KeyUseSignature},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "rs512k",
|
||||||
|
Secret: secret,
|
||||||
|
Policy: authorization.OneFactor.String(),
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://client.example.com",
|
||||||
|
},
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
||||||
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA512,
|
||||||
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
|
Values: []schema.JWK{
|
||||||
|
{KeyID: "rs512", Key: keyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAUsingSHA512, Use: oidc.KeyUseSignature},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "ps256k",
|
||||||
|
Secret: secret,
|
||||||
|
Policy: authorization.OneFactor.String(),
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://client.example.com",
|
||||||
|
},
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
||||||
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA256,
|
||||||
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
|
Values: []schema.JWK{
|
||||||
|
{KeyID: "ps256", Key: keyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAPSSUsingSHA256, Use: oidc.KeyUseSignature},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "ps384k",
|
||||||
|
Secret: secret,
|
||||||
|
Policy: authorization.OneFactor.String(),
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://client.example.com",
|
||||||
|
},
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
||||||
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA384,
|
||||||
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
|
Values: []schema.JWK{
|
||||||
|
{KeyID: "ps384", Key: keyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAPSSUsingSHA384, Use: oidc.KeyUseSignature},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "ps512k",
|
||||||
|
Secret: secret,
|
||||||
|
Policy: authorization.OneFactor.String(),
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://client.example.com",
|
||||||
|
},
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
||||||
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA512,
|
||||||
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
|
Values: []schema.JWK{
|
||||||
|
{KeyID: "ps512", Key: keyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAPSSUsingSHA512, Use: oidc.KeyUseSignature},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "es256k",
|
||||||
|
Secret: secret,
|
||||||
|
Policy: authorization.OneFactor.String(),
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://client.example.com",
|
||||||
|
},
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
||||||
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP256AndSHA256,
|
||||||
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
|
Values: []schema.JWK{
|
||||||
|
{KeyID: "es256", Key: keyECDSAP256.PublicKey, Algorithm: oidc.SigningAlgECDSAUsingP256AndSHA256, Use: oidc.KeyUseSignature},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "es384k",
|
||||||
|
Secret: secret,
|
||||||
|
Policy: authorization.OneFactor.String(),
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://client.example.com",
|
||||||
|
},
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
||||||
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP384AndSHA384,
|
||||||
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
|
Values: []schema.JWK{
|
||||||
|
{KeyID: "es384", Key: keyECDSAP384.PublicKey, Algorithm: oidc.SigningAlgECDSAUsingP384AndSHA384, Use: oidc.KeyUseSignature},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "es512k",
|
||||||
|
Secret: secret,
|
||||||
|
Policy: authorization.OneFactor.String(),
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://client.example.com",
|
||||||
|
},
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
||||||
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP521AndSHA512,
|
||||||
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
|
Values: []schema.JWK{
|
||||||
|
{KeyID: "es512", Key: keyECDSAP521.PublicKey, Algorithm: oidc.SigningAlgECDSAUsingP521AndSHA512, Use: oidc.KeyUseSignature},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ID: "hs5122",
|
ID: "hs5122",
|
||||||
Secret: secret,
|
Secret: secret,
|
||||||
|
@ -471,7 +659,7 @@ func (s *ClientAuthenticationStrategySuite) TestShouldValidateAssertionHS512() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnMismatchedAlg() {
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnMismatchedAlg() {
|
||||||
assertion := NewAssertion("rs256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
assertion := NewAssertion(rs256, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion)
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion)
|
||||||
|
|
||||||
|
@ -507,11 +695,11 @@ func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnMismatchedAlgS
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS256() {
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS256() {
|
||||||
assertion := NewAssertion("rs256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
assertion := NewAssertion(rs256, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion)
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion)
|
||||||
|
|
||||||
token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey))
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotEqual("", token)
|
s.Require().NotEqual("", token)
|
||||||
|
@ -524,12 +712,100 @@ func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKe
|
||||||
s.Nil(client)
|
s.Nil(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnBadAlgRS256() {
|
||||||
|
assertion := NewAssertion("rs256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS256, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = rs256
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The 'client_assertion' uses signing algorithm 'PS256' but the requested OAuth 2.0 Client enforces signing algorithm 'RS256'.")
|
||||||
|
s.Nil(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnBadKidRS256() {
|
||||||
|
assertion := NewAssertion("rs256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "nokey"
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.EqualError(ErrorToRFC6749ErrorTest(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The JSON Web Token uses signing key with kid 'nokey', which could not be found.")
|
||||||
|
s.Nil(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnBadTypRS256() {
|
||||||
|
assertion := NewAssertion("rs256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES256, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = rs256
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyECDSAP256)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The 'client_assertion' uses signing algorithm 'ES256' but the requested OAuth 2.0 Client enforces signing algorithm 'RS256'.")
|
||||||
|
s.Nil(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysRS256() {
|
||||||
|
assertion := NewAssertion("rs256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = rs256
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID)))
|
||||||
|
|
||||||
|
ctx := s.GetCtx()
|
||||||
|
|
||||||
|
gomock.InOrder(
|
||||||
|
s.store.
|
||||||
|
EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig).
|
||||||
|
Return(nil, sql.ErrNoRows),
|
||||||
|
|
||||||
|
s.store.
|
||||||
|
EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}).
|
||||||
|
Return(nil),
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.NoError(err)
|
||||||
|
s.Require().NotNil(client)
|
||||||
|
|
||||||
|
s.Equal("rs256k", client.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS384() {
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS384() {
|
||||||
assertion := NewAssertion("rs384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
assertion := NewAssertion("rs384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS384, assertion)
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS384, assertion)
|
||||||
|
|
||||||
token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey))
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotEqual("", token)
|
s.Require().NotEqual("", token)
|
||||||
|
@ -542,12 +818,46 @@ func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKe
|
||||||
s.Nil(client)
|
s.Nil(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysRS384() {
|
||||||
|
assertion := NewAssertion("rs384k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS384, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "rs384"
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID)))
|
||||||
|
|
||||||
|
ctx := s.GetCtx()
|
||||||
|
|
||||||
|
gomock.InOrder(
|
||||||
|
s.store.
|
||||||
|
EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig).
|
||||||
|
Return(nil, sql.ErrNoRows),
|
||||||
|
|
||||||
|
s.store.
|
||||||
|
EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}).
|
||||||
|
Return(nil),
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.NoError(err)
|
||||||
|
s.Require().NotNil(client)
|
||||||
|
|
||||||
|
s.Equal("rs384k", client.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS512() {
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS512() {
|
||||||
assertion := NewAssertion("rs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
assertion := NewAssertion("rs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS512, assertion)
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS512, assertion)
|
||||||
|
|
||||||
token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey))
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotEqual("", token)
|
s.Require().NotEqual("", token)
|
||||||
|
@ -560,12 +870,46 @@ func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKe
|
||||||
s.Nil(client)
|
s.Nil(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysRS512() {
|
||||||
|
assertion := NewAssertion("rs512k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS512, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "rs512"
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID)))
|
||||||
|
|
||||||
|
ctx := s.GetCtx()
|
||||||
|
|
||||||
|
gomock.InOrder(
|
||||||
|
s.store.
|
||||||
|
EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig).
|
||||||
|
Return(nil, sql.ErrNoRows),
|
||||||
|
|
||||||
|
s.store.
|
||||||
|
EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}).
|
||||||
|
Return(nil),
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.NoError(err)
|
||||||
|
s.Require().NotNil(client)
|
||||||
|
|
||||||
|
s.Equal("rs512k", client.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS256() {
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS256() {
|
||||||
assertion := NewAssertion("ps256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
assertion := NewAssertion("ps256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS256, assertion)
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS256, assertion)
|
||||||
|
|
||||||
token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey))
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotEqual("", token)
|
s.Require().NotEqual("", token)
|
||||||
|
@ -578,12 +922,46 @@ func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKe
|
||||||
s.Nil(client)
|
s.Nil(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysPS256() {
|
||||||
|
assertion := NewAssertion("ps256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS256, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "ps256"
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID)))
|
||||||
|
|
||||||
|
ctx := s.GetCtx()
|
||||||
|
|
||||||
|
gomock.InOrder(
|
||||||
|
s.store.
|
||||||
|
EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig).
|
||||||
|
Return(nil, sql.ErrNoRows),
|
||||||
|
|
||||||
|
s.store.
|
||||||
|
EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}).
|
||||||
|
Return(nil),
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.NoError(err)
|
||||||
|
s.Require().NotNil(client)
|
||||||
|
|
||||||
|
s.Equal("ps256k", client.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS384() {
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS384() {
|
||||||
assertion := NewAssertion("ps384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
assertion := NewAssertion("ps384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS384, assertion)
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS384, assertion)
|
||||||
|
|
||||||
token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey))
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotEqual("", token)
|
s.Require().NotEqual("", token)
|
||||||
|
@ -596,12 +974,46 @@ func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKe
|
||||||
s.Nil(client)
|
s.Nil(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysPS384() {
|
||||||
|
assertion := NewAssertion("ps384k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS384, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "ps384"
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID)))
|
||||||
|
|
||||||
|
ctx := s.GetCtx()
|
||||||
|
|
||||||
|
gomock.InOrder(
|
||||||
|
s.store.
|
||||||
|
EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig).
|
||||||
|
Return(nil, sql.ErrNoRows),
|
||||||
|
|
||||||
|
s.store.
|
||||||
|
EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}).
|
||||||
|
Return(nil),
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.NoError(err)
|
||||||
|
s.Require().NotNil(client)
|
||||||
|
|
||||||
|
s.Equal("ps384k", client.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS512() {
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS512() {
|
||||||
assertion := NewAssertion("ps512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
assertion := NewAssertion("ps512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS512, assertion)
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS512, assertion)
|
||||||
|
|
||||||
token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey))
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotEqual("", token)
|
s.Require().NotEqual("", token)
|
||||||
|
@ -614,12 +1026,46 @@ func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKe
|
||||||
s.Nil(client)
|
s.Nil(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysPS512() {
|
||||||
|
assertion := NewAssertion("ps512k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS512, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "ps512"
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyRSA2048)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID)))
|
||||||
|
|
||||||
|
ctx := s.GetCtx()
|
||||||
|
|
||||||
|
gomock.InOrder(
|
||||||
|
s.store.
|
||||||
|
EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig).
|
||||||
|
Return(nil, sql.ErrNoRows),
|
||||||
|
|
||||||
|
s.store.
|
||||||
|
EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}).
|
||||||
|
Return(nil),
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.NoError(err)
|
||||||
|
s.Require().NotNil(client)
|
||||||
|
|
||||||
|
s.Equal("ps512k", client.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES256() {
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES256() {
|
||||||
assertion := NewAssertion("es256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
assertion := NewAssertion("es256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES256, assertion)
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES256, assertion)
|
||||||
|
|
||||||
token, err := assertionJWT.SignedString(MustParseECPrivateKey(exampleECP256PrivateKey))
|
token, err := assertionJWT.SignedString(keyECDSAP256)
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotEqual("", token)
|
s.Require().NotEqual("", token)
|
||||||
|
@ -632,12 +1078,47 @@ func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKe
|
||||||
s.Nil(client)
|
s.Nil(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysES256() {
|
||||||
|
assertion := NewAssertion("es256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES256, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "es256"
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyECDSAP256)
|
||||||
|
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID)))
|
||||||
|
|
||||||
|
ctx := s.GetCtx()
|
||||||
|
|
||||||
|
gomock.InOrder(
|
||||||
|
s.store.
|
||||||
|
EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig).
|
||||||
|
Return(nil, sql.ErrNoRows),
|
||||||
|
|
||||||
|
s.store.
|
||||||
|
EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}).
|
||||||
|
Return(nil),
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.NoError(err)
|
||||||
|
s.Require().NotNil(client)
|
||||||
|
|
||||||
|
s.Equal("es256k", client.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES384() {
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES384() {
|
||||||
assertion := NewAssertion("es384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
assertion := NewAssertion("es384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES384, assertion)
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES384, assertion)
|
||||||
|
|
||||||
token, err := assertionJWT.SignedString(MustParseECPrivateKey(exampleECP384PrivateKey))
|
token, err := assertionJWT.SignedString(keyECDSAP384)
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotEqual("", token)
|
s.Require().NotEqual("", token)
|
||||||
|
@ -650,12 +1131,46 @@ func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKe
|
||||||
s.Nil(client)
|
s.Nil(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysES384() {
|
||||||
|
assertion := NewAssertion("es384k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES384, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "es384"
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyECDSAP384)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID)))
|
||||||
|
|
||||||
|
ctx := s.GetCtx()
|
||||||
|
|
||||||
|
gomock.InOrder(
|
||||||
|
s.store.
|
||||||
|
EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig).
|
||||||
|
Return(nil, sql.ErrNoRows),
|
||||||
|
|
||||||
|
s.store.
|
||||||
|
EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}).
|
||||||
|
Return(nil),
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.NoError(err)
|
||||||
|
s.Require().NotNil(client)
|
||||||
|
|
||||||
|
s.Equal("es384k", client.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES512() {
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES512() {
|
||||||
assertion := NewAssertion("es512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
assertion := NewAssertion("es512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES512, assertion)
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES512, assertion)
|
||||||
|
|
||||||
token, err := assertionJWT.SignedString(MustParseECPrivateKey(exampleECP521PrivateKey))
|
token, err := assertionJWT.SignedString(keyECDSAP521)
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotEqual("", token)
|
s.Require().NotEqual("", token)
|
||||||
|
@ -668,6 +1183,40 @@ func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKe
|
||||||
s.Nil(client)
|
s.Nil(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysES512() {
|
||||||
|
assertion := NewAssertion("es512k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES512, assertion)
|
||||||
|
assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "es512"
|
||||||
|
|
||||||
|
token, err := assertionJWT.SignedString(keyECDSAP521)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual("", token)
|
||||||
|
|
||||||
|
r := s.GetAssertionRequest(token)
|
||||||
|
|
||||||
|
sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID)))
|
||||||
|
|
||||||
|
ctx := s.GetCtx()
|
||||||
|
|
||||||
|
gomock.InOrder(
|
||||||
|
s.store.
|
||||||
|
EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig).
|
||||||
|
Return(nil, sql.ErrNoRows),
|
||||||
|
|
||||||
|
s.store.
|
||||||
|
EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}).
|
||||||
|
Return(nil),
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm)
|
||||||
|
|
||||||
|
s.NoError(err)
|
||||||
|
s.Require().NotNil(client)
|
||||||
|
|
||||||
|
s.Equal("es512k", client.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnJTIKnown() {
|
func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnJTIKnown() {
|
||||||
assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0))
|
||||||
|
|
||||||
|
@ -1384,126 +1933,3 @@ func NewAssertion(clientID string, tokenURL *url.URL, iat, exp time.Time) Regist
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RFC6749ErrorTest struct {
|
|
||||||
*fosite.RFC6749Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err *RFC6749ErrorTest) Error() string {
|
|
||||||
return err.WithExposeDebug(true).GetDescription()
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrorToRFC6749ErrorTest(err error) (rfc error) {
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ferr := fosite.ErrorToRFC6749Error(err)
|
|
||||||
|
|
||||||
return &RFC6749ErrorTest{ferr}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MustDecodeSecret(value string) *schema.PasswordDigest {
|
|
||||||
if secret, err := schema.DecodePasswordDigest(value); err != nil {
|
|
||||||
panic(err)
|
|
||||||
} else {
|
|
||||||
return secret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MustParseRequestURI(input string) *url.URL {
|
|
||||||
if requestURI, err := url.ParseRequestURI(input); err != nil {
|
|
||||||
panic(err)
|
|
||||||
} else {
|
|
||||||
return requestURI
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
|
||||||
block, _ := pem.Decode([]byte(data))
|
|
||||||
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
|
|
||||||
panic("not pem encoded")
|
|
||||||
}
|
|
||||||
|
|
||||||
if block.Type != "RSA PRIVATE KEY" {
|
|
||||||
panic("not private key")
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
func MustParseECPrivateKey(data string) *ecdsa.PrivateKey {
|
|
||||||
block, _ := pem.Decode([]byte(data))
|
|
||||||
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
|
|
||||||
panic("not pem encoded")
|
|
||||||
}
|
|
||||||
|
|
||||||
if block.Type != "EC PRIVATE KEY" {
|
|
||||||
panic("not private key")
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := x509.ParseECPrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
const exampleRSAPrivateKey = `
|
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIEpQIBAAKCAQEA60Vuz1N1wUHiCDIlbz8gE0dWPCmHEWnXKchEEISqIJ6j5Eah
|
|
||||||
Q/GwX3WK0UV5ATRvWhg6o7/WfrLYcAsi4w79TgMjJHLWIY/jzAS3quEtzOLlLSWZ
|
|
||||||
9FR9SomQm3T/ETOS8IvSGrksIj0WgX35jB1NnbqSTRnYx7Cg/TBJjmiaqd0b9G/8
|
|
||||||
LlReaihwGf8tvPgnteWIdon3EI2MKDBkaesRjpL98Cz7VvD7dajseAlUh9jQWVge
|
|
||||||
sN8qnm8pNPFAYsgxf//Jf0RfsND6H70zKKybDmyct4T4o/8qjivw4ly0XkArDCUj
|
|
||||||
Qx2KUF7nN+Bo9wwnNppjdnsOPUbus8o1a9vY1QIDAQABAoIBAQDl1SBY3PlN36SF
|
|
||||||
yScUtCALdUbi4taVxkVxBbioQlFIKHGGkRD9JN/dgSApK6r36FdXNhAi40cQ4nnZ
|
|
||||||
iqd8FKqTSTFNa/mPM9ee+ITMI8nwOz8SiYcKTndPF2/yzapXDYDgCFcpz/czQ2X2
|
|
||||||
/i+IFyA5k4dUVomVGhFLBZ71xW5BvGUBMUH0XkeR5+c4gLvgR209BlpBHlkX4tUQ
|
|
||||||
+RQoxbKpkntl0mjqf91zcOe4LJVsXZFyN+NVSzLEbGC3lVSSiyjVQH3s7ExnTaHi
|
|
||||||
PpwSoXzu5QJj5xRit/1B3/LEGpIlPGFrkhMzBDTN+HYV/VLbCHJzjg5GVJawA82E
|
|
||||||
h2BY6YWJAoGBAPmGaZL5ggnTVR2XVBLDKbwL/sesqiPZk45B+I5eObHl+v236JH9
|
|
||||||
RPMjdE10jOR1TzfQdmE2/RboKhiVn+osS+2W6VXSo7sMsSM1bLBPYhnwrNIqzrX8
|
|
||||||
Vgi2bCl2S8ZhVo2R8c5WUaD0Gpxs6hwPIMOQWWwxDlsbg/UoLrhD3X4XAoGBAPFg
|
|
||||||
VSvaWQdDVAqjM42ObhZtWxeLfEAcxRQDMQq7btrTwBZSrtP3S3Egu66cp/4PT4VD
|
|
||||||
Hc8tYyT2rNETiqT6b2Rm1MgeoJ8wRqte6ZXSQVVQUOd42VG04O3aaleAGhXjEkM2
|
|
||||||
avctRdKHDhQdIt+riPgaNj4FdYpmQ5zIrcZtBr/zAoGBAOBXzBX7xMHmwxEe3NUd
|
|
||||||
qSlMM579C9+9oF/3ymzeJMtgtcBmGHEhoFtmVgvJrV8+ZaIOCFExam2tASQnaqbV
|
|
||||||
etK7q0ChaNok+CJqxzThupcN/6PaHw4aOJQOx8KjfE95dqNEQ367txqaPk7D0dy2
|
|
||||||
cUPDRdLzbC/X1lWV8iNzyPGzAoGBAN4R2epRpYz4Fa7/vWNkAcaib6c2zmaR0YN6
|
|
||||||
+Di+ftvW6yfehDhBkWgQTHv2ZtxoK6oYOKmuQUP1qsNkbi8gtTEzJlrDStWKbcom
|
|
||||||
tVMAsNkT3otHdPEmL7bFNwcvtVAjrF6oBztHrLBnTr2UnMwZnhdczkC7dwuQ0G3D
|
|
||||||
d5VSI16fAoGAY7eeVDkic73GbZmtZibuodvPJ/z85RIBOrzf3ColO4jGI6Ej/EnD
|
|
||||||
rMEe/mRC27CJzS9L9Jc0Kt66mGSvodDGl0nBsXGNfPog0cGwweCVN0Eo2VJZbRTT
|
|
||||||
UoU05/Pvu2h3/E8gGTBY0/WPSo06YUsICjVDWNuOIa/7IY7SyE6Xxn0=
|
|
||||||
-----END RSA PRIVATE KEY-----`
|
|
||||||
|
|
||||||
const exampleECP256PrivateKey = `
|
|
||||||
-----BEGIN EC PRIVATE KEY-----
|
|
||||||
MHcCAQEEID1fSsJ8qyEqj2DVkrshaNiXqaSDX7qViASRkyGGJFbEoAoGCCqGSM49
|
|
||||||
AwEHoUQDQgAENnBG+bBJIaIa+bRlHaLiXD86RAy+Ef9CVdAfpPGoNRfkOTcrrIV7
|
|
||||||
2wv3Y5e0he63Tn9iVAFYRFexK1mjFw7TfA==
|
|
||||||
-----END EC PRIVATE KEY-----`
|
|
||||||
|
|
||||||
const exampleECP384PrivateKey = `
|
|
||||||
-----BEGIN EC PRIVATE KEY-----
|
|
||||||
MIGkAgEBBDBPoOfapxtgZ8XNE7Wwdlw+9oDc6x4m57MITZyWzN62jkFUAYsvPJDF
|
|
||||||
9+g+e8CT5yqgBwYFK4EEACKhZANiAAQ2uZ0HIIxIavyjGyX13tIZVOaRB4+D64dF
|
|
||||||
s3DXDrpXcuDTSohw9xBW5sLDqRVu2LkBsCUFXtEJUHgC+O7wToNw8nh+KdDrcu/J
|
|
||||||
miNqbvEHuvlSlHWyx9HH8kAEuu1+SZg=
|
|
||||||
-----END EC PRIVATE KEY-----`
|
|
||||||
|
|
||||||
const exampleECP521PrivateKey = `
|
|
||||||
-----BEGIN EC PRIVATE KEY-----
|
|
||||||
MIHcAgEBBEIBT07AnitDd1Z01bl5W5VW8/vTWyu7w3MSqEmCeKcM19p/TAJAeS8L
|
|
||||||
6UOig2fTUeuMeA2PoOUjI2Bid927VsWcxE2gBwYFK4EEACOhgYkDgYYABAGnV9mu
|
|
||||||
xY0E7/k8b+glOOMaN0+Qt70H9OmSz6tC8tU3EayRwFlNPch9TlvEpbCS3MsDE9dN
|
|
||||||
78EpFx45MUqzzdZcOgAu+EUC9Zas1YVK+WMo0GFy+XtFq3kxubOclBb52M/63mcd
|
|
||||||
zZnA8aAu9iTK9YPfcw1YWTJliNdKUoxmGVV5Ca1W4w==
|
|
||||||
-----END EC PRIVATE KEY-----`
|
|
|
@ -46,12 +46,22 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client Client) {
|
||||||
base.ResponseModes = append(base.ResponseModes, fosite.ResponseModeType(mode))
|
base.ResponseModes = append(base.ResponseModes, fosite.ResponseModeType(mode))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.TokenEndpointAuthMethod != "" && config.TokenEndpointAuthMethod != "auto" {
|
if config.TokenEndpointAuthMethod != "" || config.TokenEndpointAuthSigningAlg != "" ||
|
||||||
client = &FullClient{
|
len(config.PublicKeys.Values) != 0 || config.PublicKeys.URI != nil || config.RequestObjectSigningAlg != "" {
|
||||||
|
full := &FullClient{
|
||||||
BaseClient: base,
|
BaseClient: base,
|
||||||
TokenEndpointAuthMethod: config.TokenEndpointAuthMethod,
|
TokenEndpointAuthMethod: config.TokenEndpointAuthMethod,
|
||||||
TokenEndpointAuthSigningAlgorithm: config.TokenEndpointAuthSigningAlg,
|
TokenEndpointAuthSigningAlgorithm: config.TokenEndpointAuthSigningAlg,
|
||||||
|
RequestObjectSigningAlgorithm: config.RequestObjectSigningAlg,
|
||||||
|
|
||||||
|
JSONWebKeys: NewPublicJSONWebKeySetFromSchemaJWK(config.PublicKeys.Values),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.PublicKeys.URI != nil {
|
||||||
|
full.JSONWebKeysURI = config.PublicKeys.URI.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
client = full
|
||||||
} else {
|
} else {
|
||||||
client = base
|
client = base
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package oidc
|
package oidc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -14,30 +14,31 @@ import (
|
||||||
"github.com/authelia/authelia/v4/internal/authorization"
|
"github.com/authelia/authelia/v4/internal/authorization"
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
"github.com/authelia/authelia/v4/internal/model"
|
"github.com/authelia/authelia/v4/internal/model"
|
||||||
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewClient(t *testing.T) {
|
func TestNewClient(t *testing.T) {
|
||||||
config := schema.OpenIDConnectClientConfiguration{}
|
config := schema.OpenIDConnectClientConfiguration{}
|
||||||
client := NewClient(config)
|
client := oidc.NewClient(config)
|
||||||
assert.Equal(t, "", client.GetID())
|
assert.Equal(t, "", client.GetID())
|
||||||
assert.Equal(t, "", client.GetDescription())
|
assert.Equal(t, "", client.GetDescription())
|
||||||
assert.Len(t, client.GetResponseModes(), 0)
|
assert.Len(t, client.GetResponseModes(), 0)
|
||||||
assert.Len(t, client.GetResponseTypes(), 1)
|
assert.Len(t, client.GetResponseTypes(), 1)
|
||||||
assert.Equal(t, "", client.GetSectorIdentifier())
|
assert.Equal(t, "", client.GetSectorIdentifier())
|
||||||
|
|
||||||
bclient, ok := client.(*BaseClient)
|
bclient, ok := client.(*oidc.BaseClient)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, "", bclient.UserinfoSigningAlg)
|
assert.Equal(t, "", bclient.UserinfoSigningAlg)
|
||||||
assert.Equal(t, SigningAlgNone, client.GetUserinfoSigningAlg())
|
assert.Equal(t, oidc.SigningAlgNone, client.GetUserinfoSigningAlg())
|
||||||
|
|
||||||
_, ok = client.(*FullClient)
|
_, ok = client.(*oidc.FullClient)
|
||||||
assert.False(t, ok)
|
assert.False(t, ok)
|
||||||
|
|
||||||
config = schema.OpenIDConnectClientConfiguration{
|
config = schema.OpenIDConnectClientConfiguration{
|
||||||
ID: myclient,
|
ID: myclient,
|
||||||
Description: myclientdesc,
|
Description: myclientdesc,
|
||||||
Policy: twofactor,
|
Policy: twofactor,
|
||||||
Secret: MustDecodeSecret(badsecret),
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
RedirectURIs: []string{examplecom},
|
RedirectURIs: []string{examplecom},
|
||||||
Scopes: schema.DefaultOpenIDConnectClientConfiguration.Scopes,
|
Scopes: schema.DefaultOpenIDConnectClientConfiguration.Scopes,
|
||||||
ResponseTypes: schema.DefaultOpenIDConnectClientConfiguration.ResponseTypes,
|
ResponseTypes: schema.DefaultOpenIDConnectClientConfiguration.ResponseTypes,
|
||||||
|
@ -45,44 +46,42 @@ func TestNewClient(t *testing.T) {
|
||||||
ResponseModes: schema.DefaultOpenIDConnectClientConfiguration.ResponseModes,
|
ResponseModes: schema.DefaultOpenIDConnectClientConfiguration.ResponseModes,
|
||||||
}
|
}
|
||||||
|
|
||||||
client = NewClient(config)
|
client = oidc.NewClient(config)
|
||||||
assert.Equal(t, myclient, client.GetID())
|
assert.Equal(t, myclient, client.GetID())
|
||||||
require.Len(t, client.GetResponseModes(), 1)
|
require.Len(t, client.GetResponseModes(), 1)
|
||||||
assert.Equal(t, fosite.ResponseModeFormPost, client.GetResponseModes()[0])
|
assert.Equal(t, fosite.ResponseModeFormPost, client.GetResponseModes()[0])
|
||||||
assert.Equal(t, authorization.TwoFactor, client.GetAuthorizationPolicy())
|
assert.Equal(t, authorization.TwoFactor, client.GetAuthorizationPolicy())
|
||||||
|
|
||||||
config = schema.OpenIDConnectClientConfiguration{
|
config = schema.OpenIDConnectClientConfiguration{
|
||||||
TokenEndpointAuthMethod: ClientAuthMethodClientSecretPost,
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretPost,
|
||||||
}
|
}
|
||||||
|
|
||||||
client = NewClient(config)
|
client = oidc.NewClient(config)
|
||||||
|
|
||||||
fclient, ok := client.(*FullClient)
|
fclient, ok := client.(*oidc.FullClient)
|
||||||
|
|
||||||
var niljwks *jose.JSONWebKeySet
|
|
||||||
|
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
assert.Equal(t, "", fclient.UserinfoSigningAlg)
|
assert.Equal(t, "", fclient.UserinfoSigningAlg)
|
||||||
assert.Equal(t, SigningAlgNone, client.GetUserinfoSigningAlg())
|
assert.Equal(t, oidc.SigningAlgNone, client.GetUserinfoSigningAlg())
|
||||||
assert.Equal(t, SigningAlgNone, fclient.UserinfoSigningAlg)
|
assert.Equal(t, oidc.SigningAlgNone, fclient.UserinfoSigningAlg)
|
||||||
|
|
||||||
assert.Equal(t, "", fclient.IDTokenSigningAlg)
|
assert.Equal(t, "", fclient.IDTokenSigningAlg)
|
||||||
assert.Equal(t, SigningAlgRSAUsingSHA256, client.GetIDTokenSigningAlg())
|
assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, client.GetIDTokenSigningAlg())
|
||||||
assert.Equal(t, SigningAlgRSAUsingSHA256, fclient.IDTokenSigningAlg)
|
assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, fclient.IDTokenSigningAlg)
|
||||||
|
|
||||||
assert.Equal(t, ClientAuthMethodClientSecretPost, fclient.TokenEndpointAuthMethod)
|
assert.Equal(t, oidc.ClientAuthMethodClientSecretPost, fclient.TokenEndpointAuthMethod)
|
||||||
assert.Equal(t, ClientAuthMethodClientSecretPost, fclient.GetTokenEndpointAuthMethod())
|
assert.Equal(t, oidc.ClientAuthMethodClientSecretPost, fclient.GetTokenEndpointAuthMethod())
|
||||||
|
|
||||||
assert.Equal(t, "", fclient.TokenEndpointAuthSigningAlgorithm)
|
assert.Equal(t, "", fclient.TokenEndpointAuthSigningAlgorithm)
|
||||||
assert.Equal(t, SigningAlgRSAUsingSHA256, fclient.GetTokenEndpointAuthSigningAlgorithm())
|
assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, fclient.GetTokenEndpointAuthSigningAlgorithm())
|
||||||
assert.Equal(t, SigningAlgRSAUsingSHA256, fclient.TokenEndpointAuthSigningAlgorithm)
|
assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, fclient.TokenEndpointAuthSigningAlgorithm)
|
||||||
|
|
||||||
assert.Equal(t, "", fclient.RequestObjectSigningAlgorithm)
|
assert.Equal(t, "", fclient.RequestObjectSigningAlgorithm)
|
||||||
assert.Equal(t, "", fclient.GetRequestObjectSigningAlgorithm())
|
assert.Equal(t, "", fclient.GetRequestObjectSigningAlgorithm())
|
||||||
|
|
||||||
fclient.RequestObjectSigningAlgorithm = SigningAlgRSAUsingSHA256
|
fclient.RequestObjectSigningAlgorithm = oidc.SigningAlgRSAUsingSHA256
|
||||||
assert.Equal(t, SigningAlgRSAUsingSHA256, fclient.GetRequestObjectSigningAlgorithm())
|
assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, fclient.GetRequestObjectSigningAlgorithm())
|
||||||
|
|
||||||
assert.Equal(t, "", fclient.JSONWebKeysURI)
|
assert.Equal(t, "", fclient.JSONWebKeysURI)
|
||||||
assert.Equal(t, "", fclient.GetJSONWebKeysURI())
|
assert.Equal(t, "", fclient.GetJSONWebKeysURI())
|
||||||
|
@ -90,22 +89,24 @@ func TestNewClient(t *testing.T) {
|
||||||
fclient.JSONWebKeysURI = "https://example.com"
|
fclient.JSONWebKeysURI = "https://example.com"
|
||||||
assert.Equal(t, "https://example.com", fclient.GetJSONWebKeysURI())
|
assert.Equal(t, "https://example.com", fclient.GetJSONWebKeysURI())
|
||||||
|
|
||||||
|
var niljwks *jose.JSONWebKeySet
|
||||||
|
|
||||||
assert.Equal(t, niljwks, fclient.JSONWebKeys)
|
assert.Equal(t, niljwks, fclient.JSONWebKeys)
|
||||||
assert.Equal(t, niljwks, fclient.GetJSONWebKeys())
|
assert.Equal(t, niljwks, fclient.GetJSONWebKeys())
|
||||||
|
|
||||||
assert.Equal(t, ClientConsentMode(0), fclient.Consent.Mode)
|
assert.Equal(t, oidc.ClientConsentMode(0), fclient.Consent.Mode)
|
||||||
assert.Equal(t, time.Second*0, fclient.Consent.Duration)
|
assert.Equal(t, time.Second*0, fclient.Consent.Duration)
|
||||||
assert.Equal(t, ClientConsent{Mode: ClientConsentModeExplicit}, fclient.GetConsentPolicy())
|
assert.Equal(t, oidc.ClientConsent{Mode: oidc.ClientConsentModeExplicit}, fclient.GetConsentPolicy())
|
||||||
|
|
||||||
fclient.TokenEndpointAuthMethod = ""
|
fclient.TokenEndpointAuthMethod = ""
|
||||||
fclient.Public = false
|
fclient.Public = false
|
||||||
assert.Equal(t, ClientAuthMethodClientSecretBasic, fclient.GetTokenEndpointAuthMethod())
|
assert.Equal(t, oidc.ClientAuthMethodClientSecretBasic, fclient.GetTokenEndpointAuthMethod())
|
||||||
assert.Equal(t, ClientAuthMethodClientSecretBasic, fclient.TokenEndpointAuthMethod)
|
assert.Equal(t, oidc.ClientAuthMethodClientSecretBasic, fclient.TokenEndpointAuthMethod)
|
||||||
|
|
||||||
fclient.TokenEndpointAuthMethod = ""
|
fclient.TokenEndpointAuthMethod = ""
|
||||||
fclient.Public = true
|
fclient.Public = true
|
||||||
assert.Equal(t, ClientAuthMethodNone, fclient.GetTokenEndpointAuthMethod())
|
assert.Equal(t, oidc.ClientAuthMethodNone, fclient.GetTokenEndpointAuthMethod())
|
||||||
assert.Equal(t, ClientAuthMethodNone, fclient.TokenEndpointAuthMethod)
|
assert.Equal(t, oidc.ClientAuthMethodNone, fclient.TokenEndpointAuthMethod)
|
||||||
|
|
||||||
assert.Equal(t, []string(nil), fclient.RequestURIs)
|
assert.Equal(t, []string(nil), fclient.RequestURIs)
|
||||||
assert.Equal(t, []string(nil), fclient.GetRequestURIs())
|
assert.Equal(t, []string(nil), fclient.GetRequestURIs())
|
||||||
|
@ -114,13 +115,13 @@ func TestNewClient(t *testing.T) {
|
||||||
func TestBaseClient_ValidatePARPolicy(t *testing.T) {
|
func TestBaseClient_ValidatePARPolicy(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
client *BaseClient
|
client *oidc.BaseClient
|
||||||
have *fosite.Request
|
have *fosite.Request
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"ShouldNotEnforcePAR",
|
"ShouldNotEnforcePAR",
|
||||||
&BaseClient{
|
&oidc.BaseClient{
|
||||||
EnforcePAR: false,
|
EnforcePAR: false,
|
||||||
},
|
},
|
||||||
&fosite.Request{},
|
&fosite.Request{},
|
||||||
|
@ -128,36 +129,36 @@ func TestBaseClient_ValidatePARPolicy(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ShouldEnforcePARAndErrorWithoutCorrectRequestURI",
|
"ShouldEnforcePARAndErrorWithoutCorrectRequestURI",
|
||||||
&BaseClient{
|
&oidc.BaseClient{
|
||||||
EnforcePAR: true,
|
EnforcePAR: true,
|
||||||
},
|
},
|
||||||
&fosite.Request{
|
&fosite.Request{
|
||||||
Form: map[string][]string{
|
Form: map[string][]string{
|
||||||
FormParameterRequestURI: {"https://google.com"},
|
oidc.FormParameterRequestURI: {"https://google.com"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"invalid_request",
|
"invalid_request",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ShouldEnforcePARAndErrorWithEmptyRequestURI",
|
"ShouldEnforcePARAndErrorWithEmptyRequestURI",
|
||||||
&BaseClient{
|
&oidc.BaseClient{
|
||||||
EnforcePAR: true,
|
EnforcePAR: true,
|
||||||
},
|
},
|
||||||
&fosite.Request{
|
&fosite.Request{
|
||||||
Form: map[string][]string{
|
Form: map[string][]string{
|
||||||
FormParameterRequestURI: {""},
|
oidc.FormParameterRequestURI: {""},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"invalid_request",
|
"invalid_request",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ShouldEnforcePARAndNotErrorWithCorrectRequestURI",
|
"ShouldEnforcePARAndNotErrorWithCorrectRequestURI",
|
||||||
&BaseClient{
|
&oidc.BaseClient{
|
||||||
EnforcePAR: true,
|
EnforcePAR: true,
|
||||||
},
|
},
|
||||||
&fosite.Request{
|
&fosite.Request{
|
||||||
Form: map[string][]string{
|
Form: map[string][]string{
|
||||||
FormParameterRequestURI: {urnPARPrefix + "abc"},
|
oidc.FormParameterRequestURI: {oidc.RedirectURIPrefixPushedAuthorizationRequestURN + "abc"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"",
|
"",
|
||||||
|
@ -166,7 +167,7 @@ func TestBaseClient_ValidatePARPolicy(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
err := tc.client.ValidatePARPolicy(tc.have, urnPARPrefix)
|
err := tc.client.ValidatePARPolicy(tc.have, oidc.RedirectURIPrefixPushedAuthorizationRequestURN)
|
||||||
|
|
||||||
switch tc.expected {
|
switch tc.expected {
|
||||||
case "":
|
case "":
|
||||||
|
@ -179,7 +180,7 @@ func TestBaseClient_ValidatePARPolicy(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsAuthenticationLevelSufficient(t *testing.T) {
|
func TestIsAuthenticationLevelSufficient(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
c.Policy = authorization.Bypass
|
c.Policy = authorization.Bypass
|
||||||
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated))
|
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated))
|
||||||
|
@ -203,7 +204,7 @@ func TestIsAuthenticationLevelSufficient(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetConsentResponseBody(t *testing.T) {
|
func TestClient_GetConsentResponseBody(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
consentRequestBody := c.GetConsentResponseBody(nil)
|
consentRequestBody := c.GetConsentResponseBody(nil)
|
||||||
assert.Equal(t, "", consentRequestBody.ClientID)
|
assert.Equal(t, "", consentRequestBody.ClientID)
|
||||||
|
@ -216,10 +217,10 @@ func TestClient_GetConsentResponseBody(t *testing.T) {
|
||||||
|
|
||||||
consent := &model.OAuth2ConsentSession{
|
consent := &model.OAuth2ConsentSession{
|
||||||
RequestedAudience: []string{examplecom},
|
RequestedAudience: []string{examplecom},
|
||||||
RequestedScopes: []string{ScopeOpenID, ScopeGroups},
|
RequestedScopes: []string{oidc.ScopeOpenID, oidc.ScopeGroups},
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedScopes := []string{ScopeOpenID, ScopeGroups}
|
expectedScopes := []string{oidc.ScopeOpenID, oidc.ScopeGroups}
|
||||||
expectedAudiences := []string{examplecom}
|
expectedAudiences := []string{examplecom}
|
||||||
|
|
||||||
consentRequestBody = c.GetConsentResponseBody(consent)
|
consentRequestBody = c.GetConsentResponseBody(consent)
|
||||||
|
@ -230,7 +231,7 @@ func TestClient_GetConsentResponseBody(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetAudience(t *testing.T) {
|
func TestClient_GetAudience(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
audience := c.GetAudience()
|
audience := c.GetAudience()
|
||||||
assert.Len(t, audience, 0)
|
assert.Len(t, audience, 0)
|
||||||
|
@ -243,24 +244,24 @@ func TestClient_GetAudience(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetScopes(t *testing.T) {
|
func TestClient_GetScopes(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
scopes := c.GetScopes()
|
scopes := c.GetScopes()
|
||||||
assert.Len(t, scopes, 0)
|
assert.Len(t, scopes, 0)
|
||||||
|
|
||||||
c.Scopes = []string{ScopeOpenID}
|
c.Scopes = []string{oidc.ScopeOpenID}
|
||||||
|
|
||||||
scopes = c.GetScopes()
|
scopes = c.GetScopes()
|
||||||
require.Len(t, scopes, 1)
|
require.Len(t, scopes, 1)
|
||||||
assert.Equal(t, ScopeOpenID, scopes[0])
|
assert.Equal(t, oidc.ScopeOpenID, scopes[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetGrantTypes(t *testing.T) {
|
func TestClient_GetGrantTypes(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
grantTypes := c.GetGrantTypes()
|
grantTypes := c.GetGrantTypes()
|
||||||
require.Len(t, grantTypes, 1)
|
require.Len(t, grantTypes, 1)
|
||||||
assert.Equal(t, GrantTypeAuthorizationCode, grantTypes[0])
|
assert.Equal(t, oidc.GrantTypeAuthorizationCode, grantTypes[0])
|
||||||
|
|
||||||
c.GrantTypes = []string{"device_code"}
|
c.GrantTypes = []string{"device_code"}
|
||||||
|
|
||||||
|
@ -270,30 +271,30 @@ func TestClient_GetGrantTypes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_Hashing(t *testing.T) {
|
func TestClient_Hashing(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
hashedSecret := c.GetHashedSecret()
|
hashedSecret := c.GetHashedSecret()
|
||||||
assert.Equal(t, []byte(nil), hashedSecret)
|
assert.Equal(t, []byte(nil), hashedSecret)
|
||||||
|
|
||||||
c.Secret = MustDecodeSecret(badsecret)
|
c.Secret = tOpenIDConnectPlainTextClientSecret
|
||||||
|
|
||||||
assert.True(t, c.Secret.MatchBytes([]byte("a_bad_secret")))
|
assert.True(t, c.Secret.MatchBytes([]byte("client-secret")))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetHashedSecret(t *testing.T) {
|
func TestClient_GetHashedSecret(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
hashedSecret := c.GetHashedSecret()
|
hashedSecret := c.GetHashedSecret()
|
||||||
assert.Equal(t, []byte(nil), hashedSecret)
|
assert.Equal(t, []byte(nil), hashedSecret)
|
||||||
|
|
||||||
c.Secret = MustDecodeSecret(badsecret)
|
c.Secret = tOpenIDConnectPlainTextClientSecret
|
||||||
|
|
||||||
hashedSecret = c.GetHashedSecret()
|
hashedSecret = c.GetHashedSecret()
|
||||||
assert.Equal(t, []byte(badsecret), hashedSecret)
|
assert.Equal(t, []byte("$plaintext$client-secret"), hashedSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetID(t *testing.T) {
|
func TestClient_GetID(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
id := c.GetID()
|
id := c.GetID()
|
||||||
assert.Equal(t, "", id)
|
assert.Equal(t, "", id)
|
||||||
|
@ -305,7 +306,7 @@ func TestClient_GetID(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetRedirectURIs(t *testing.T) {
|
func TestClient_GetRedirectURIs(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
redirectURIs := c.GetRedirectURIs()
|
redirectURIs := c.GetRedirectURIs()
|
||||||
require.Len(t, redirectURIs, 0)
|
require.Len(t, redirectURIs, 0)
|
||||||
|
@ -318,7 +319,7 @@ func TestClient_GetRedirectURIs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetResponseModes(t *testing.T) {
|
func TestClient_GetResponseModes(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
responseModes := c.GetResponseModes()
|
responseModes := c.GetResponseModes()
|
||||||
require.Len(t, responseModes, 0)
|
require.Len(t, responseModes, 0)
|
||||||
|
@ -337,18 +338,18 @@ func TestClient_GetResponseModes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetResponseTypes(t *testing.T) {
|
func TestClient_GetResponseTypes(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
responseTypes := c.GetResponseTypes()
|
responseTypes := c.GetResponseTypes()
|
||||||
require.Len(t, responseTypes, 1)
|
require.Len(t, responseTypes, 1)
|
||||||
assert.Equal(t, ResponseTypeAuthorizationCodeFlow, responseTypes[0])
|
assert.Equal(t, oidc.ResponseTypeAuthorizationCodeFlow, responseTypes[0])
|
||||||
|
|
||||||
c.ResponseTypes = []string{ResponseTypeAuthorizationCodeFlow, ResponseTypeImplicitFlowIDToken}
|
c.ResponseTypes = []string{oidc.ResponseTypeAuthorizationCodeFlow, oidc.ResponseTypeImplicitFlowIDToken}
|
||||||
|
|
||||||
responseTypes = c.GetResponseTypes()
|
responseTypes = c.GetResponseTypes()
|
||||||
require.Len(t, responseTypes, 2)
|
require.Len(t, responseTypes, 2)
|
||||||
assert.Equal(t, ResponseTypeAuthorizationCodeFlow, responseTypes[0])
|
assert.Equal(t, oidc.ResponseTypeAuthorizationCodeFlow, responseTypes[0])
|
||||||
assert.Equal(t, ResponseTypeImplicitFlowIDToken, responseTypes[1])
|
assert.Equal(t, oidc.ResponseTypeImplicitFlowIDToken, responseTypes[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewClientPKCE(t *testing.T) {
|
func TestNewClientPKCE(t *testing.T) {
|
||||||
|
@ -423,7 +424,7 @@ func TestNewClientPKCE(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
client := NewClient(tc.have)
|
client := oidc.NewClient(tc.have)
|
||||||
|
|
||||||
assert.Equal(t, tc.expectedEnforcePKCE, client.GetPKCEEnforcement())
|
assert.Equal(t, tc.expectedEnforcePKCE, client.GetPKCEEnforcement())
|
||||||
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.GetPKCEChallengeMethodEnforcement())
|
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.GetPKCEChallengeMethodEnforcement())
|
||||||
|
@ -473,14 +474,14 @@ func TestNewClientPAR(t *testing.T) {
|
||||||
"ShouldEnforcePARAndErrorOnNonPARRequest",
|
"ShouldEnforcePARAndErrorOnNonPARRequest",
|
||||||
schema.OpenIDConnectClientConfiguration{EnforcePAR: true},
|
schema.OpenIDConnectClientConfiguration{EnforcePAR: true},
|
||||||
true,
|
true,
|
||||||
&fosite.Request{Form: map[string][]string{FormParameterRequestURI: {"https://example.com"}}},
|
&fosite.Request{Form: map[string][]string{oidc.FormParameterRequestURI: {"https://example.com"}}},
|
||||||
"invalid_request",
|
"invalid_request",
|
||||||
"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Pushed Authorization Requests are enforced for this client but no such request was sent. The request_uri parameter 'https://example.com' is malformed."},
|
"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Pushed Authorization Requests are enforced for this client but no such request was sent. The request_uri parameter 'https://example.com' is malformed."},
|
||||||
{
|
{
|
||||||
"ShouldEnforcePARAndNotErrorOnPARRequest",
|
"ShouldEnforcePARAndNotErrorOnPARRequest",
|
||||||
schema.OpenIDConnectClientConfiguration{EnforcePAR: true},
|
schema.OpenIDConnectClientConfiguration{EnforcePAR: true},
|
||||||
true,
|
true,
|
||||||
&fosite.Request{Form: map[string][]string{FormParameterRequestURI: {fmt.Sprintf("%sabc", urnPARPrefix)}}},
|
&fosite.Request{Form: map[string][]string{oidc.FormParameterRequestURI: {fmt.Sprintf("%sabc", oidc.RedirectURIPrefixPushedAuthorizationRequestURN)}}},
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
|
@ -488,12 +489,12 @@ func TestNewClientPAR(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
client := NewClient(tc.have)
|
client := oidc.NewClient(tc.have)
|
||||||
|
|
||||||
assert.Equal(t, tc.expected, client.GetPAREnforcement())
|
assert.Equal(t, tc.expected, client.GetPAREnforcement())
|
||||||
|
|
||||||
if tc.r != nil {
|
if tc.r != nil {
|
||||||
err := client.ValidatePARPolicy(tc.r, urnPARPrefix)
|
err := client.ValidatePARPolicy(tc.r, oidc.RedirectURIPrefixPushedAuthorizationRequestURN)
|
||||||
|
|
||||||
if tc.err != "" {
|
if tc.err != "" {
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
|
@ -518,25 +519,25 @@ func TestNewClientResponseModes(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"ShouldEnforceResponseModePolicyAndAllowDefaultModeQuery",
|
"ShouldEnforceResponseModePolicyAndAllowDefaultModeQuery",
|
||||||
schema.OpenIDConnectClientConfiguration{ResponseModes: []string{ResponseModeQuery}},
|
schema.OpenIDConnectClientConfiguration{ResponseModes: []string{oidc.ResponseModeQuery}},
|
||||||
[]fosite.ResponseModeType{fosite.ResponseModeQuery},
|
[]fosite.ResponseModeType{fosite.ResponseModeQuery},
|
||||||
&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeDefault, Request: fosite.Request{Form: map[string][]string{FormParameterResponseMode: nil}}},
|
&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeDefault, Request: fosite.Request{Form: map[string][]string{oidc.FormParameterResponseMode: nil}}},
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ShouldEnforceResponseModePolicyAndFailOnDefaultMode",
|
"ShouldEnforceResponseModePolicyAndFailOnDefaultMode",
|
||||||
schema.OpenIDConnectClientConfiguration{ResponseModes: []string{ResponseModeFormPost}},
|
schema.OpenIDConnectClientConfiguration{ResponseModes: []string{oidc.ResponseModeFormPost}},
|
||||||
[]fosite.ResponseModeType{fosite.ResponseModeFormPost},
|
[]fosite.ResponseModeType{fosite.ResponseModeFormPost},
|
||||||
&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeDefault, Request: fosite.Request{Form: map[string][]string{FormParameterResponseMode: nil}}},
|
&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeDefault, Request: fosite.Request{Form: map[string][]string{oidc.FormParameterResponseMode: nil}}},
|
||||||
"unsupported_response_mode",
|
"unsupported_response_mode",
|
||||||
"The authorization server does not support obtaining a response using this response mode. The request omitted the response_mode making the default response_mode 'query' based on the other authorization request parameters but registered OAuth 2.0 client doesn't support this response_mode",
|
"The authorization server does not support obtaining a response using this response mode. The request omitted the response_mode making the default response_mode 'query' based on the other authorization request parameters but registered OAuth 2.0 client doesn't support this response_mode",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ShouldNotEnforceConfiguredResponseMode",
|
"ShouldNotEnforceConfiguredResponseMode",
|
||||||
schema.OpenIDConnectClientConfiguration{ResponseModes: []string{ResponseModeFormPost}},
|
schema.OpenIDConnectClientConfiguration{ResponseModes: []string{oidc.ResponseModeFormPost}},
|
||||||
[]fosite.ResponseModeType{fosite.ResponseModeFormPost},
|
[]fosite.ResponseModeType{fosite.ResponseModeFormPost},
|
||||||
&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeQuery, Request: fosite.Request{Form: map[string][]string{FormParameterResponseMode: {ResponseModeQuery}}}},
|
&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeQuery, Request: fosite.Request{Form: map[string][]string{oidc.FormParameterResponseMode: {oidc.ResponseModeQuery}}}},
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
|
@ -544,7 +545,7 @@ func TestNewClientResponseModes(t *testing.T) {
|
||||||
"ShouldNotEnforceUnconfiguredResponseMode",
|
"ShouldNotEnforceUnconfiguredResponseMode",
|
||||||
schema.OpenIDConnectClientConfiguration{ResponseModes: []string{}},
|
schema.OpenIDConnectClientConfiguration{ResponseModes: []string{}},
|
||||||
[]fosite.ResponseModeType{},
|
[]fosite.ResponseModeType{},
|
||||||
&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeDefault, Request: fosite.Request{Form: map[string][]string{FormParameterResponseMode: {ResponseModeQuery}}}},
|
&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeDefault, Request: fosite.Request{Form: map[string][]string{oidc.FormParameterResponseMode: {oidc.ResponseModeQuery}}}},
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
|
@ -552,7 +553,7 @@ func TestNewClientResponseModes(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
client := NewClient(tc.have)
|
client := oidc.NewClient(tc.have)
|
||||||
|
|
||||||
assert.Equal(t, tc.expected, client.GetResponseModes())
|
assert.Equal(t, tc.expected, client.GetResponseModes())
|
||||||
|
|
||||||
|
@ -572,10 +573,48 @@ func TestNewClientResponseModes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_IsPublic(t *testing.T) {
|
func TestClient_IsPublic(t *testing.T) {
|
||||||
c := &FullClient{BaseClient: &BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
assert.False(t, c.IsPublic())
|
assert.False(t, c.IsPublic())
|
||||||
|
|
||||||
c.Public = true
|
c.Public = true
|
||||||
assert.True(t, c.IsPublic())
|
assert.True(t, c.IsPublic())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewClient_JSONWebKeySetURI(t *testing.T) {
|
||||||
|
var (
|
||||||
|
client oidc.Client
|
||||||
|
clientf *oidc.FullClient
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
|
||||||
|
client = oidc.NewClient(schema.OpenIDConnectClientConfiguration{
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretPost,
|
||||||
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
|
URI: MustParseRequestURI("https://google.com"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NotNil(t, client)
|
||||||
|
|
||||||
|
clientf, ok = client.(*oidc.FullClient)
|
||||||
|
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
assert.Equal(t, "https://google.com", clientf.GetJSONWebKeysURI())
|
||||||
|
|
||||||
|
client = oidc.NewClient(schema.OpenIDConnectClientConfiguration{
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretPost,
|
||||||
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
|
URI: nil,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NotNil(t, client)
|
||||||
|
|
||||||
|
clientf, ok = client.(*oidc.FullClient)
|
||||||
|
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
assert.Equal(t, "", clientf.GetJSONWebKeysURI())
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"hash"
|
"hash"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/go-retryablehttp"
|
"github.com/hashicorp/go-retryablehttp"
|
||||||
|
@ -43,7 +42,7 @@ func NewConfig(config *schema.OpenIDConnectConfiguration, templates *templates.P
|
||||||
PAR: PARConfig{
|
PAR: PARConfig{
|
||||||
Enforced: config.PAR.Enforce,
|
Enforced: config.PAR.Enforce,
|
||||||
ContextLifespan: config.PAR.ContextLifespan,
|
ContextLifespan: config.PAR.ContextLifespan,
|
||||||
URIPrefix: urnPARPrefix,
|
URIPrefix: RedirectURIPrefixPushedAuthorizationRequestURN,
|
||||||
},
|
},
|
||||||
Templates: templates,
|
Templates: templates,
|
||||||
}
|
}
|
||||||
|
@ -91,7 +90,6 @@ type Config struct {
|
||||||
RefreshTokenScopes []string
|
RefreshTokenScopes []string
|
||||||
|
|
||||||
HTTPClient *retryablehttp.Client
|
HTTPClient *retryablehttp.Client
|
||||||
FormPostHTMLTemplate *template.Template
|
|
||||||
MessageCatalog i18n.MessageCatalog
|
MessageCatalog i18n.MessageCatalog
|
||||||
|
|
||||||
Templates *templates.Provider
|
Templates *templates.Provider
|
||||||
|
@ -386,8 +384,8 @@ func (c *Config) GetDisableRefreshTokenValidation(ctx context.Context) (disable
|
||||||
|
|
||||||
// GetAuthorizeCodeLifespan returns the authorization code lifespan.
|
// GetAuthorizeCodeLifespan returns the authorization code lifespan.
|
||||||
func (c *Config) GetAuthorizeCodeLifespan(ctx context.Context) (lifespan time.Duration) {
|
func (c *Config) GetAuthorizeCodeLifespan(ctx context.Context) (lifespan time.Duration) {
|
||||||
if c.Lifespans.AuthorizeCode <= 0 {
|
if c.Lifespans.AuthorizeCode.Seconds() <= 0 {
|
||||||
c.Lifespans.AccessToken = lifespanAuthorizeCodeDefault
|
c.Lifespans.AuthorizeCode = lifespanAuthorizeCodeDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Lifespans.AuthorizeCode
|
return c.Lifespans.AuthorizeCode
|
||||||
|
@ -395,8 +393,8 @@ func (c *Config) GetAuthorizeCodeLifespan(ctx context.Context) (lifespan time.Du
|
||||||
|
|
||||||
// GetRefreshTokenLifespan returns the refresh token lifespan.
|
// GetRefreshTokenLifespan returns the refresh token lifespan.
|
||||||
func (c *Config) GetRefreshTokenLifespan(ctx context.Context) (lifespan time.Duration) {
|
func (c *Config) GetRefreshTokenLifespan(ctx context.Context) (lifespan time.Duration) {
|
||||||
if c.Lifespans.RefreshToken <= 0 {
|
if c.Lifespans.RefreshToken.Seconds() <= 0 {
|
||||||
c.Lifespans.AccessToken = lifespanRefreshTokenDefault
|
c.Lifespans.RefreshToken = lifespanRefreshTokenDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Lifespans.RefreshToken
|
return c.Lifespans.RefreshToken
|
||||||
|
@ -404,8 +402,8 @@ func (c *Config) GetRefreshTokenLifespan(ctx context.Context) (lifespan time.Dur
|
||||||
|
|
||||||
// GetIDTokenLifespan returns the ID token lifespan.
|
// GetIDTokenLifespan returns the ID token lifespan.
|
||||||
func (c *Config) GetIDTokenLifespan(ctx context.Context) (lifespan time.Duration) {
|
func (c *Config) GetIDTokenLifespan(ctx context.Context) (lifespan time.Duration) {
|
||||||
if c.Lifespans.IDToken <= 0 {
|
if c.Lifespans.IDToken.Seconds() <= 0 {
|
||||||
c.Lifespans.AccessToken = lifespanTokenDefault
|
c.Lifespans.IDToken = lifespanTokenDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Lifespans.IDToken
|
return c.Lifespans.IDToken
|
||||||
|
@ -413,7 +411,7 @@ func (c *Config) GetIDTokenLifespan(ctx context.Context) (lifespan time.Duration
|
||||||
|
|
||||||
// GetAccessTokenLifespan returns the access token lifespan.
|
// GetAccessTokenLifespan returns the access token lifespan.
|
||||||
func (c *Config) GetAccessTokenLifespan(ctx context.Context) (lifespan time.Duration) {
|
func (c *Config) GetAccessTokenLifespan(ctx context.Context) (lifespan time.Duration) {
|
||||||
if c.Lifespans.AccessToken <= 0 {
|
if c.Lifespans.AccessToken.Seconds() <= 0 {
|
||||||
c.Lifespans.AccessToken = lifespanTokenDefault
|
c.Lifespans.AccessToken = lifespanTokenDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,15 +526,13 @@ 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 {
|
if octx, ok := ctx.(OpenIDConnectContext); ok {
|
||||||
tokenURI, err := ctx.IssuerURL()
|
switch issuerURL, err := octx.IssuerURL(); err {
|
||||||
if err != nil {
|
case nil:
|
||||||
|
return issuerURL.JoinPath(EndpointPathToken).String()
|
||||||
|
default:
|
||||||
return c.TokenURL
|
return c.TokenURL
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenURI.Path = path.Join(tokenURI.Path, EndpointPathToken)
|
|
||||||
|
|
||||||
return tokenURI.String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.TokenURL
|
return c.TokenURL
|
||||||
|
@ -592,7 +588,7 @@ func (c *Config) GetResponseModeHandlerExtension(ctx context.Context) (handler f
|
||||||
// usually 'urn:ietf:params:oauth:request_uri:'.
|
// usually 'urn:ietf:params:oauth:request_uri:'.
|
||||||
func (c *Config) GetPushedAuthorizeRequestURIPrefix(ctx context.Context) string {
|
func (c *Config) GetPushedAuthorizeRequestURIPrefix(ctx context.Context) string {
|
||||||
if c.PAR.URIPrefix == "" {
|
if c.PAR.URIPrefix == "" {
|
||||||
c.PAR.URIPrefix = urnPARPrefix
|
c.PAR.URIPrefix = RedirectURIPrefixPushedAuthorizationRequestURN
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.PAR.URIPrefix
|
return c.PAR.URIPrefix
|
||||||
|
@ -607,7 +603,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.Seconds() == 0 {
|
if c.PAR.ContextLifespan.Seconds() <= 0 {
|
||||||
c.PAR.ContextLifespan = lifespanPARContextDefault
|
c.PAR.ContextLifespan = lifespanPARContextDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,229 @@
|
||||||
|
package oidc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ory/fosite/token/jwt"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
|
"github.com/authelia/authelia/v4/internal/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfig_GetAllowedPrompts(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
config := &oidc.Config{}
|
||||||
|
|
||||||
|
assert.Equal(t, []string(nil), config.AllowedPrompts)
|
||||||
|
assert.Equal(t, []string{oidc.PromptNone, oidc.PromptLogin, oidc.PromptConsent}, config.GetAllowedPrompts(ctx))
|
||||||
|
assert.Equal(t, []string{oidc.PromptNone, oidc.PromptLogin, oidc.PromptConsent}, config.AllowedPrompts)
|
||||||
|
|
||||||
|
config.AllowedPrompts = []string{oidc.PromptNone}
|
||||||
|
assert.Equal(t, []string{oidc.PromptNone}, config.AllowedPrompts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_PKCE(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
config := &oidc.Config{}
|
||||||
|
|
||||||
|
assert.False(t, config.GetEnforcePKCE(ctx))
|
||||||
|
assert.False(t, config.GetEnforcePKCEForPublicClients(ctx))
|
||||||
|
|
||||||
|
config.ProofKeyCodeExchange.Enforce = true
|
||||||
|
assert.True(t, config.GetEnforcePKCE(ctx))
|
||||||
|
assert.True(t, config.GetEnforcePKCEForPublicClients(ctx))
|
||||||
|
|
||||||
|
config.ProofKeyCodeExchange.Enforce = false
|
||||||
|
|
||||||
|
assert.False(t, config.GetEnforcePKCEForPublicClients(ctx))
|
||||||
|
|
||||||
|
config.ProofKeyCodeExchange.EnforcePublicClients = true
|
||||||
|
|
||||||
|
assert.True(t, config.GetEnforcePKCEForPublicClients(ctx))
|
||||||
|
|
||||||
|
assert.False(t, config.GetEnablePKCEPlainChallengeMethod(ctx))
|
||||||
|
config.ProofKeyCodeExchange.AllowPlainChallengeMethod = true
|
||||||
|
|
||||||
|
assert.True(t, config.GetEnablePKCEPlainChallengeMethod(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_GrantTypeJWTBearer(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
config := &oidc.Config{}
|
||||||
|
assert.False(t, config.GetGrantTypeJWTBearerIDOptional(ctx))
|
||||||
|
assert.False(t, config.GetGrantTypeJWTBearerCanSkipClientAuth(ctx))
|
||||||
|
assert.False(t, config.GetGrantTypeJWTBearerIssuedDateOptional(ctx))
|
||||||
|
|
||||||
|
config.GrantTypeJWTBearer.OptionalJTIClaim = true
|
||||||
|
assert.True(t, config.GetGrantTypeJWTBearerIDOptional(ctx))
|
||||||
|
assert.False(t, config.GetGrantTypeJWTBearerCanSkipClientAuth(ctx))
|
||||||
|
assert.False(t, config.GetGrantTypeJWTBearerIssuedDateOptional(ctx))
|
||||||
|
|
||||||
|
config.GrantTypeJWTBearer.OptionalClientAuth = true
|
||||||
|
assert.True(t, config.GetGrantTypeJWTBearerIDOptional(ctx))
|
||||||
|
assert.True(t, config.GetGrantTypeJWTBearerCanSkipClientAuth(ctx))
|
||||||
|
assert.False(t, config.GetGrantTypeJWTBearerIssuedDateOptional(ctx))
|
||||||
|
|
||||||
|
config.GrantTypeJWTBearer.OptionalIssuedDate = true
|
||||||
|
assert.True(t, config.GetGrantTypeJWTBearerIDOptional(ctx))
|
||||||
|
assert.True(t, config.GetGrantTypeJWTBearerCanSkipClientAuth(ctx))
|
||||||
|
assert.True(t, config.GetGrantTypeJWTBearerIssuedDateOptional(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_Durations(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
config := &oidc.Config{}
|
||||||
|
assert.Equal(t, time.Duration(0), config.JWTMaxDuration)
|
||||||
|
assert.Equal(t, time.Hour*24, config.GetJWTMaxDuration(ctx))
|
||||||
|
assert.Equal(t, time.Hour*24, config.JWTMaxDuration)
|
||||||
|
|
||||||
|
assert.Equal(t, time.Duration(0), config.Lifespans.IDToken)
|
||||||
|
assert.Equal(t, time.Hour, config.GetIDTokenLifespan(ctx))
|
||||||
|
assert.Equal(t, time.Hour, config.Lifespans.IDToken)
|
||||||
|
|
||||||
|
assert.Equal(t, time.Duration(0), config.Lifespans.AccessToken)
|
||||||
|
assert.Equal(t, time.Hour, config.GetAccessTokenLifespan(ctx))
|
||||||
|
assert.Equal(t, time.Hour, config.Lifespans.AccessToken)
|
||||||
|
|
||||||
|
assert.Equal(t, time.Duration(0), config.Lifespans.RefreshToken)
|
||||||
|
assert.Equal(t, time.Hour*24*30, config.GetRefreshTokenLifespan(ctx))
|
||||||
|
assert.Equal(t, time.Hour*24*30, config.Lifespans.RefreshToken)
|
||||||
|
|
||||||
|
assert.Equal(t, time.Duration(0), config.Lifespans.AuthorizeCode)
|
||||||
|
assert.Equal(t, time.Minute*15, config.GetAuthorizeCodeLifespan(ctx))
|
||||||
|
assert.Equal(t, time.Minute*15, config.Lifespans.AuthorizeCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_GetTokenEntropy(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
config := &oidc.Config{}
|
||||||
|
|
||||||
|
assert.Equal(t, 0, config.TokenEntropy)
|
||||||
|
assert.Equal(t, 32, config.GetTokenEntropy(ctx))
|
||||||
|
assert.Equal(t, 32, config.TokenEntropy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_Misc(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
config := &oidc.Config{}
|
||||||
|
|
||||||
|
assert.False(t, config.DisableRefreshTokenValidation)
|
||||||
|
assert.False(t, config.GetDisableRefreshTokenValidation(ctx))
|
||||||
|
|
||||||
|
assert.Equal(t, "", config.Issuers.AccessToken)
|
||||||
|
assert.Equal(t, "", config.GetAccessTokenIssuer(ctx))
|
||||||
|
|
||||||
|
assert.Equal(t, "", config.Issuers.IDToken)
|
||||||
|
assert.Equal(t, "", config.GetIDTokenIssuer(ctx))
|
||||||
|
|
||||||
|
assert.Equal(t, jwt.JWTScopeFieldUnset, config.JWTScopeField)
|
||||||
|
assert.Equal(t, jwt.JWTScopeFieldList, config.GetJWTScopeField(ctx))
|
||||||
|
assert.Equal(t, jwt.JWTScopeFieldList, config.JWTScopeField)
|
||||||
|
|
||||||
|
assert.Equal(t, []string(nil), config.SanitationWhiteList)
|
||||||
|
assert.Equal(t, []string(nil), config.GetSanitationWhiteList(ctx))
|
||||||
|
assert.Equal(t, []string(nil), config.SanitationWhiteList)
|
||||||
|
|
||||||
|
assert.False(t, config.OmitRedirectScopeParameter)
|
||||||
|
assert.False(t, config.GetOmitRedirectScopeParam(ctx))
|
||||||
|
|
||||||
|
assert.NotNil(t, config.GetRedirectSecureChecker(ctx))
|
||||||
|
assert.NotNil(t, config.GetHTTPClient(ctx))
|
||||||
|
|
||||||
|
assert.Nil(t, config.Strategy.Scope)
|
||||||
|
assert.NotNil(t, config.GetScopeStrategy(ctx))
|
||||||
|
assert.NotNil(t, config.Strategy.Scope)
|
||||||
|
|
||||||
|
assert.Nil(t, config.Strategy.Audience)
|
||||||
|
assert.NotNil(t, config.GetAudienceStrategy(ctx))
|
||||||
|
assert.NotNil(t, config.Strategy.Audience)
|
||||||
|
|
||||||
|
assert.Equal(t, []string(nil), config.RefreshTokenScopes)
|
||||||
|
assert.Equal(t, []string{oidc.ScopeOffline, oidc.ScopeOfflineAccess}, config.GetRefreshTokenScopes(ctx))
|
||||||
|
assert.Equal(t, []string{oidc.ScopeOffline, oidc.ScopeOfflineAccess}, config.RefreshTokenScopes)
|
||||||
|
|
||||||
|
assert.Equal(t, 0, config.MinParameterEntropy)
|
||||||
|
assert.Equal(t, 8, config.GetMinParameterEntropy(ctx))
|
||||||
|
assert.Equal(t, 8, config.MinParameterEntropy)
|
||||||
|
|
||||||
|
assert.False(t, config.SendDebugMessagesToClients)
|
||||||
|
assert.False(t, config.GetSendDebugMessagesToClients(ctx))
|
||||||
|
|
||||||
|
config.SendDebugMessagesToClients = true
|
||||||
|
|
||||||
|
assert.True(t, config.GetSendDebugMessagesToClients(ctx))
|
||||||
|
|
||||||
|
assert.Nil(t, config.Strategy.JWKSFetcher)
|
||||||
|
assert.NotNil(t, config.GetJWKSFetcherStrategy(ctx))
|
||||||
|
assert.NotNil(t, config.Strategy.JWKSFetcher)
|
||||||
|
|
||||||
|
assert.Nil(t, config.Strategy.ClientAuthentication)
|
||||||
|
assert.Nil(t, config.GetClientAuthenticationStrategy(ctx))
|
||||||
|
|
||||||
|
assert.Nil(t, config.MessageCatalog)
|
||||||
|
assert.Nil(t, config.GetMessageCatalog(ctx))
|
||||||
|
|
||||||
|
assert.Nil(t, config.Templates)
|
||||||
|
assert.Nil(t, config.GetFormPostHTMLTemplate(ctx))
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
config.Templates, err = templates.New(templates.Config{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NotNil(t, config.GetFormPostHTMLTemplate(ctx))
|
||||||
|
assert.NotNil(t, config.Templates)
|
||||||
|
|
||||||
|
assert.False(t, config.GetUseLegacyErrorFormat(ctx))
|
||||||
|
|
||||||
|
assert.Nil(t, config.GetAuthorizeEndpointHandlers(ctx))
|
||||||
|
assert.Nil(t, config.GetTokenEndpointHandlers(ctx))
|
||||||
|
assert.Nil(t, config.GetTokenIntrospectionHandlers(ctx))
|
||||||
|
assert.Nil(t, config.GetRevocationHandlers(ctx))
|
||||||
|
assert.Nil(t, config.GetPushedAuthorizeEndpointHandlers(ctx))
|
||||||
|
assert.Nil(t, config.GetResponseModeHandlerExtension(ctx))
|
||||||
|
|
||||||
|
assert.Equal(t, "", config.GetTokenURL(ctx))
|
||||||
|
|
||||||
|
octx := &MockOpenIDConnectContext{
|
||||||
|
Context: ctx,
|
||||||
|
IssuerURLFunc: func() (issuerURL *url.URL, err error) {
|
||||||
|
return nil, fmt.Errorf("test error")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "", config.GetTokenURL(octx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_PAR(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
config := &oidc.Config{}
|
||||||
|
|
||||||
|
assert.Equal(t, "", config.PAR.URIPrefix)
|
||||||
|
assert.Equal(t, "urn:ietf:params:oauth:request_uri:", config.GetPushedAuthorizeRequestURIPrefix(ctx))
|
||||||
|
assert.Equal(t, "urn:ietf:params:oauth:request_uri:", config.PAR.URIPrefix)
|
||||||
|
|
||||||
|
assert.False(t, config.PAR.Enforced)
|
||||||
|
assert.False(t, config.EnforcePushedAuthorize(ctx))
|
||||||
|
assert.False(t, config.PAR.Enforced)
|
||||||
|
|
||||||
|
config.PAR.Enforced = true
|
||||||
|
|
||||||
|
assert.True(t, config.EnforcePushedAuthorize(ctx))
|
||||||
|
|
||||||
|
assert.Equal(t, time.Duration(0), config.PAR.ContextLifespan)
|
||||||
|
assert.Equal(t, time.Minute*5, config.GetPushedAuthorizeContextLifespan(ctx))
|
||||||
|
assert.Equal(t, time.Minute*5, config.PAR.ContextLifespan)
|
||||||
|
}
|
|
@ -53,7 +53,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
urnPARPrefix = "urn:ietf:params:oauth:request_uri:"
|
RedirectURIPrefixPushedAuthorizationRequestURN = "urn:ietf:params:oauth:request_uri:"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -175,9 +175,9 @@ const (
|
||||||
tokenPrefixOrgAutheliaFmt = "authelia_%s_" //nolint:gosec
|
tokenPrefixOrgAutheliaFmt = "authelia_%s_" //nolint:gosec
|
||||||
tokenPrefixOrgOryFmt = "ory_%s_" //nolint:gosec
|
tokenPrefixOrgOryFmt = "ory_%s_" //nolint:gosec
|
||||||
|
|
||||||
tokenPrefixPartAccessToken = "at"
|
TokenPrefixPartAccessToken = "at"
|
||||||
tokenPrefixPartRefreshToken = "rt"
|
TokenPrefixPartRefreshToken = "rt"
|
||||||
tokenPrefixPartAuthorizeCode = "ac"
|
TokenPrefixPartAuthorizeCode = "ac"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Paths.
|
// Paths.
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package oidc
|
package oidc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -10,6 +9,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ory/fosite"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
"github.com/authelia/authelia/v4/internal/utils"
|
"github.com/authelia/authelia/v4/internal/utils"
|
||||||
)
|
)
|
||||||
|
@ -22,8 +23,12 @@ const (
|
||||||
twofactor = "two_factor"
|
twofactor = "two_factor"
|
||||||
examplecom = "https://example.com"
|
examplecom = "https://example.com"
|
||||||
examplecomsid = "example.com"
|
examplecomsid = "example.com"
|
||||||
badsecret = "$plaintext$a_bad_secret"
|
|
||||||
badhmac = "asbdhaaskmdlkamdklasmdlkams"
|
badhmac = "asbdhaaskmdlkamdklasmdlkams"
|
||||||
|
badTokenString = "badTokenString"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
rs256 = "rs256"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MustDecodeSecret(value string) *schema.PasswordDigest {
|
func MustDecodeSecret(value string) *schema.PasswordDigest {
|
||||||
|
@ -78,28 +83,6 @@ func MustLoadCertificateChain(alg, op string) schema.X509CertificateChain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func MustLoadCertificate(alg, op string) *x509.Certificate {
|
|
||||||
decoded := MustLoadCrypto(alg, op, "crt")
|
|
||||||
|
|
||||||
cert, ok := decoded.(*x509.Certificate)
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Errorf("the key was not a *x509.Certificate, it's a %T", cert))
|
|
||||||
}
|
|
||||||
|
|
||||||
return cert
|
|
||||||
}
|
|
||||||
|
|
||||||
func MustLoadEd15519PrivateKey(curve string, extra ...string) ed25519.PrivateKey {
|
|
||||||
decoded := MustLoadCrypto("ED25519", curve, "pem", extra...)
|
|
||||||
|
|
||||||
key, ok := decoded.(ed25519.PrivateKey)
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Errorf("the key was not a ed25519.PrivateKey, it's a %T", key))
|
|
||||||
}
|
|
||||||
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
func MustLoadECDSAPrivateKey(curve string, extra ...string) *ecdsa.PrivateKey {
|
func MustLoadECDSAPrivateKey(curve string, extra ...string) *ecdsa.PrivateKey {
|
||||||
decoded := MustLoadCrypto("ECDSA", curve, "pem", extra...)
|
decoded := MustLoadCrypto("ECDSA", curve, "pem", extra...)
|
||||||
|
|
||||||
|
@ -133,6 +116,24 @@ func MustLoadRSAPrivateKey(bits string, extra ...string) *rsa.PrivateKey {
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RFC6749ErrorTest struct {
|
||||||
|
*fosite.RFC6749Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *RFC6749ErrorTest) Error() string {
|
||||||
|
return err.WithExposeDebug(true).GetDescription()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorToRFC6749ErrorTest(err error) (rfc error) {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ferr := fosite.ErrorToRFC6749Error(err)
|
||||||
|
|
||||||
|
return &RFC6749ErrorTest{ferr}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tOpenIDConnectPBKDF2ClientSecret, tOpenIDConnectPlainTextClientSecret *schema.PasswordDigest
|
tOpenIDConnectPBKDF2ClientSecret, tOpenIDConnectPlainTextClientSecret *schema.PasswordDigest
|
||||||
|
|
||||||
|
@ -146,8 +147,8 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tOpenIDConnectPBKDF2ClientSecret = MustDecodeSecret("$pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng")
|
tOpenIDConnectPBKDF2ClientSecret = MustDecodeSecret("$pbkdf2-sha512$100000$cfNEo93VkIUIvaXHqetFoQ$O6qFLAlwCMz6.hv9XqUEPnMtrFxODw70T7bmnfTzfNPi3iXbgUEmGiyA6msybOfmj7m3QJS6lLy4DglgJifkKw")
|
||||||
tOpenIDConnectPlainTextClientSecret = MustDecodeSecret("$plaintext$example")
|
tOpenIDConnectPlainTextClientSecret = MustDecodeSecret("$plaintext$client-secret")
|
||||||
|
|
||||||
keyRSA1024 = MustLoadRSAPrivateKey("1024")
|
keyRSA1024 = MustLoadRSAPrivateKey("1024")
|
||||||
keyRSA2048 = MustLoadRSAPrivateKey("2048")
|
keyRSA2048 = MustLoadRSAPrivateKey("2048")
|
||||||
|
|
|
@ -22,22 +22,21 @@ type HMACCoreStrategy struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccessTokenSignature implements oauth2.AccessTokenStrategy.
|
// AccessTokenSignature implements oauth2.AccessTokenStrategy.
|
||||||
func (h *HMACCoreStrategy) AccessTokenSignature(ctx context.Context, token string) string {
|
func (h *HMACCoreStrategy) AccessTokenSignature(ctx context.Context, tokenString string) string {
|
||||||
return h.Enigma.Signature(token)
|
return h.Enigma.Signature(tokenString)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateAccessToken implements oauth2.AccessTokenStrategy.
|
// GenerateAccessToken implements oauth2.AccessTokenStrategy.
|
||||||
func (h *HMACCoreStrategy) GenerateAccessToken(ctx context.Context, _ fosite.Requester) (token string, signature string, err error) {
|
func (h *HMACCoreStrategy) GenerateAccessToken(ctx context.Context, _ fosite.Requester) (tokenString string, sig string, err error) {
|
||||||
token, sig, err := h.Enigma.Generate(ctx)
|
if tokenString, sig, err = h.Enigma.Generate(ctx); err != nil {
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return h.setPrefix(token, tokenPrefixPartAccessToken), sig, nil
|
return h.setPrefix(tokenString, TokenPrefixPartAccessToken), sig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateAccessToken implements oauth2.AccessTokenStrategy.
|
// ValidateAccessToken implements oauth2.AccessTokenStrategy.
|
||||||
func (h *HMACCoreStrategy) ValidateAccessToken(ctx context.Context, r fosite.Requester, token string) (err error) {
|
func (h *HMACCoreStrategy) ValidateAccessToken(ctx context.Context, r fosite.Requester, tokenString string) (err error) {
|
||||||
var exp = r.GetSession().GetExpiresAt(fosite.AccessToken)
|
var exp = r.GetSession().GetExpiresAt(fosite.AccessToken)
|
||||||
if exp.IsZero() && r.GetRequestedAt().Add(h.Config.GetAccessTokenLifespan(ctx)).Before(time.Now().UTC()) {
|
if exp.IsZero() && r.GetRequestedAt().Add(h.Config.GetAccessTokenLifespan(ctx)).Before(time.Now().UTC()) {
|
||||||
return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Access token expired at '%s'.", r.GetRequestedAt().Add(h.Config.GetAccessTokenLifespan(ctx))))
|
return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Access token expired at '%s'.", r.GetRequestedAt().Add(h.Config.GetAccessTokenLifespan(ctx))))
|
||||||
|
@ -47,37 +46,36 @@ func (h *HMACCoreStrategy) ValidateAccessToken(ctx context.Context, r fosite.Req
|
||||||
return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Access token expired at '%s'.", exp))
|
return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Access token expired at '%s'.", exp))
|
||||||
}
|
}
|
||||||
|
|
||||||
return h.Enigma.Validate(ctx, h.trimPrefix(token, tokenPrefixPartAccessToken))
|
return h.Enigma.Validate(ctx, h.trimPrefix(tokenString, TokenPrefixPartAccessToken))
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshTokenSignature implements oauth2.RefreshTokenStrategy.
|
// RefreshTokenSignature implements oauth2.RefreshTokenStrategy.
|
||||||
func (h *HMACCoreStrategy) RefreshTokenSignature(ctx context.Context, token string) string {
|
func (h *HMACCoreStrategy) RefreshTokenSignature(ctx context.Context, tokenString string) string {
|
||||||
return h.Enigma.Signature(token)
|
return h.Enigma.Signature(tokenString)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateRefreshToken implements oauth2.RefreshTokenStrategy.
|
// GenerateRefreshToken implements oauth2.RefreshTokenStrategy.
|
||||||
func (h *HMACCoreStrategy) GenerateRefreshToken(ctx context.Context, _ fosite.Requester) (token string, signature string, err error) {
|
func (h *HMACCoreStrategy) GenerateRefreshToken(ctx context.Context, _ fosite.Requester) (tokenString string, sig string, err error) {
|
||||||
token, sig, err := h.Enigma.Generate(ctx)
|
if tokenString, sig, err = h.Enigma.Generate(ctx); err != nil {
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return h.setPrefix(token, tokenPrefixPartRefreshToken), sig, nil
|
return h.setPrefix(tokenString, TokenPrefixPartRefreshToken), sig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRefreshToken implements oauth2.RefreshTokenStrategy.
|
// ValidateRefreshToken implements oauth2.RefreshTokenStrategy.
|
||||||
func (h *HMACCoreStrategy) ValidateRefreshToken(ctx context.Context, r fosite.Requester, token string) (err error) {
|
func (h *HMACCoreStrategy) ValidateRefreshToken(ctx context.Context, r fosite.Requester, tokenString string) (err error) {
|
||||||
var exp = r.GetSession().GetExpiresAt(fosite.RefreshToken)
|
var exp = r.GetSession().GetExpiresAt(fosite.RefreshToken)
|
||||||
|
|
||||||
if exp.IsZero() {
|
if exp.IsZero() {
|
||||||
return h.Enigma.Validate(ctx, h.trimPrefix(token, tokenPrefixPartRefreshToken))
|
return h.Enigma.Validate(ctx, h.trimPrefix(tokenString, TokenPrefixPartRefreshToken))
|
||||||
}
|
}
|
||||||
|
|
||||||
if exp.Before(time.Now().UTC()) {
|
if exp.Before(time.Now().UTC()) {
|
||||||
return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Refresh token expired at '%s'.", exp))
|
return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Refresh token expired at '%s'.", exp))
|
||||||
}
|
}
|
||||||
|
|
||||||
return h.Enigma.Validate(ctx, h.trimPrefix(token, tokenPrefixPartRefreshToken))
|
return h.Enigma.Validate(ctx, h.trimPrefix(tokenString, TokenPrefixPartRefreshToken))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizeCodeSignature implements oauth2.AuthorizeCodeStrategy.
|
// AuthorizeCodeSignature implements oauth2.AuthorizeCodeStrategy.
|
||||||
|
@ -86,17 +84,16 @@ func (h *HMACCoreStrategy) AuthorizeCodeSignature(ctx context.Context, token str
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateAuthorizeCode implements oauth2.AuthorizeCodeStrategy.
|
// GenerateAuthorizeCode implements oauth2.AuthorizeCodeStrategy.
|
||||||
func (h *HMACCoreStrategy) GenerateAuthorizeCode(ctx context.Context, _ fosite.Requester) (token string, signature string, err error) {
|
func (h *HMACCoreStrategy) GenerateAuthorizeCode(ctx context.Context, _ fosite.Requester) (tokenString string, sig string, err error) {
|
||||||
token, sig, err := h.Enigma.Generate(ctx)
|
if tokenString, sig, err = h.Enigma.Generate(ctx); err != nil {
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return h.setPrefix(token, tokenPrefixPartAuthorizeCode), sig, nil
|
return h.setPrefix(tokenString, TokenPrefixPartAuthorizeCode), sig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateAuthorizeCode implements oauth2.AuthorizeCodeStrategy.
|
// ValidateAuthorizeCode implements oauth2.AuthorizeCodeStrategy.
|
||||||
func (h *HMACCoreStrategy) ValidateAuthorizeCode(ctx context.Context, r fosite.Requester, token string) (err error) {
|
func (h *HMACCoreStrategy) ValidateAuthorizeCode(ctx context.Context, r fosite.Requester, tokenString string) (err error) {
|
||||||
var exp = r.GetSession().GetExpiresAt(fosite.AuthorizeCode)
|
var exp = r.GetSession().GetExpiresAt(fosite.AuthorizeCode)
|
||||||
|
|
||||||
if exp.IsZero() && r.GetRequestedAt().Add(h.Config.GetAuthorizeCodeLifespan(ctx)).Before(time.Now().UTC()) {
|
if exp.IsZero() && r.GetRequestedAt().Add(h.Config.GetAuthorizeCodeLifespan(ctx)).Before(time.Now().UTC()) {
|
||||||
|
@ -107,7 +104,7 @@ func (h *HMACCoreStrategy) ValidateAuthorizeCode(ctx context.Context, r fosite.R
|
||||||
return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Authorize code expired at '%s'.", exp))
|
return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Authorize code expired at '%s'.", exp))
|
||||||
}
|
}
|
||||||
|
|
||||||
return h.Enigma.Validate(ctx, h.trimPrefix(token, tokenPrefixPartAuthorizeCode))
|
return h.Enigma.Validate(ctx, h.trimPrefix(tokenString, TokenPrefixPartAuthorizeCode))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HMACCoreStrategy) getPrefix(part string) string {
|
func (h *HMACCoreStrategy) getPrefix(part string) string {
|
||||||
|
@ -118,14 +115,14 @@ func (h *HMACCoreStrategy) getCustomPrefix(tokenPrefixFmt, part string) string {
|
||||||
return fmt.Sprintf(tokenPrefixFmt, part)
|
return fmt.Sprintf(tokenPrefixFmt, part)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HMACCoreStrategy) setPrefix(token, part string) string {
|
func (h *HMACCoreStrategy) setPrefix(tokenString, part string) string {
|
||||||
return h.getPrefix(part) + token
|
return h.getPrefix(part) + tokenString
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HMACCoreStrategy) trimPrefix(token, part string) string {
|
func (h *HMACCoreStrategy) trimPrefix(tokenString, part string) string {
|
||||||
if strings.HasPrefix(token, h.getCustomPrefix(tokenPrefixOrgOryFmt, part)) {
|
if strings.HasPrefix(tokenString, h.getCustomPrefix(tokenPrefixOrgOryFmt, part)) {
|
||||||
return strings.TrimPrefix(token, h.getCustomPrefix(tokenPrefixOrgOryFmt, part))
|
return strings.TrimPrefix(tokenString, h.getCustomPrefix(tokenPrefixOrgOryFmt, part))
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.TrimPrefix(token, h.getPrefix(part))
|
return strings.TrimPrefix(tokenString, h.getPrefix(part))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
package oidc
|
package oidc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
"github.com/ory/fosite/token/hmac"
|
"github.com/ory/fosite/token/hmac"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHMACStrategy(t *testing.T) {
|
func TestHMACStrategy(t *testing.T) {
|
||||||
goodsecret := []byte("R7VCSUfnKc7Y5zE84q6GstYqfMGjL4wM")
|
goodsecret := []byte("R7VCSUfnKc7Y5zE84q6GstYqfMGjL4wM")
|
||||||
secreta := []byte("a")
|
secreta := []byte("a")
|
||||||
|
|
||||||
config := &Config{
|
config := &oidc.Config{
|
||||||
TokenEntropy: 10,
|
TokenEntropy: 10,
|
||||||
GlobalSecret: secreta,
|
GlobalSecret: secreta,
|
||||||
Lifespans: LifespanConfig{
|
Lifespans: oidc.LifespanConfig{
|
||||||
AccessToken: time.Hour,
|
AccessToken: time.Hour,
|
||||||
RefreshToken: time.Hour,
|
RefreshToken: time.Hour,
|
||||||
AuthorizeCode: time.Minute,
|
AuthorizeCode: time.Minute,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
strategy := &HMACCoreStrategy{
|
strategy := &oidc.HMACCoreStrategy{
|
||||||
Enigma: &hmac.HMACStrategy{Config: config},
|
Enigma: &hmac.HMACStrategy{Config: config},
|
||||||
Config: config,
|
Config: config,
|
||||||
}
|
}
|
||||||
|
@ -87,50 +87,3 @@ func TestHMACStrategy(t *testing.T) {
|
||||||
assert.NoError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AccessToken: time.Now().Add(100 * time.Hour)}}}, token))
|
assert.NoError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AccessToken: time.Now().Add(100 * time.Hour)}}}, token))
|
||||||
assert.EqualError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AccessToken: time.Now().Add(-100 * time.Second)}}}, token), "invalid_token")
|
assert.EqualError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AccessToken: time.Now().Add(-100 * time.Second)}}}, token), "invalid_token")
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,6 +66,7 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration
|
||||||
ClientAuthMethodClientSecretBasic,
|
ClientAuthMethodClientSecretBasic,
|
||||||
ClientAuthMethodClientSecretPost,
|
ClientAuthMethodClientSecretPost,
|
||||||
ClientAuthMethodClientSecretJWT,
|
ClientAuthMethodClientSecretJWT,
|
||||||
|
ClientAuthMethodPrivateKeyJWT,
|
||||||
ClientAuthMethodNone,
|
ClientAuthMethodNone,
|
||||||
},
|
},
|
||||||
TokenEndpointAuthSigningAlgValuesSupported: []string{
|
TokenEndpointAuthSigningAlgValuesSupported: []string{
|
||||||
|
@ -82,6 +83,7 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration
|
||||||
ClientAuthMethodClientSecretBasic,
|
ClientAuthMethodClientSecretBasic,
|
||||||
ClientAuthMethodClientSecretPost,
|
ClientAuthMethodClientSecretPost,
|
||||||
ClientAuthMethodClientSecretJWT,
|
ClientAuthMethodClientSecretJWT,
|
||||||
|
ClientAuthMethodPrivateKeyJWT,
|
||||||
ClientAuthMethodNone,
|
ClientAuthMethodNone,
|
||||||
},
|
},
|
||||||
RevocationEndpointAuthSigningAlgValuesSupported: []string{
|
RevocationEndpointAuthSigningAlgValuesSupported: []string{
|
||||||
|
@ -104,12 +106,12 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration
|
||||||
SigningAlgRSAUsingSHA256,
|
SigningAlgRSAUsingSHA256,
|
||||||
},
|
},
|
||||||
UserinfoSigningAlgValuesSupported: []string{
|
UserinfoSigningAlgValuesSupported: []string{
|
||||||
SigningAlgNone,
|
|
||||||
SigningAlgRSAUsingSHA256,
|
SigningAlgRSAUsingSHA256,
|
||||||
|
SigningAlgNone,
|
||||||
},
|
},
|
||||||
RequestObjectSigningAlgValuesSupported: []string{
|
RequestObjectSigningAlgValuesSupported: []string{
|
||||||
SigningAlgNone,
|
|
||||||
SigningAlgRSAUsingSHA256,
|
SigningAlgRSAUsingSHA256,
|
||||||
|
SigningAlgNone,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OpenIDConnectFrontChannelLogoutDiscoveryOptions: &OpenIDConnectFrontChannelLogoutDiscoveryOptions{},
|
OpenIDConnectFrontChannelLogoutDiscoveryOptions: &OpenIDConnectFrontChannelLogoutDiscoveryOptions{},
|
||||||
|
@ -122,11 +124,7 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
algs := make([]string, len(c.Discovery.RegisteredJWKSigningAlgs))
|
for _, alg := range c.Discovery.ResponseObjectSigningAlgs {
|
||||||
|
|
||||||
copy(algs, c.Discovery.RegisteredJWKSigningAlgs)
|
|
||||||
|
|
||||||
for _, alg := range algs {
|
|
||||||
if !utils.IsStringInSlice(alg, config.IDTokenSigningAlgValuesSupported) {
|
if !utils.IsStringInSlice(alg, config.IDTokenSigningAlgValuesSupported) {
|
||||||
config.IDTokenSigningAlgValuesSupported = append(config.IDTokenSigningAlgValuesSupported, alg)
|
config.IDTokenSigningAlgValuesSupported = append(config.IDTokenSigningAlgValuesSupported, alg)
|
||||||
}
|
}
|
||||||
|
@ -136,6 +134,20 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.OpenIDConnectConfiguration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, alg := range c.Discovery.RequestObjectSigningAlgs {
|
||||||
|
if !utils.IsStringInSlice(alg, config.RequestObjectSigningAlgValuesSupported) {
|
||||||
|
config.RequestObjectSigningAlgValuesSupported = append(config.RequestObjectSigningAlgValuesSupported, alg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.IsStringInSlice(alg, config.RevocationEndpointAuthSigningAlgValuesSupported) {
|
||||||
|
config.RevocationEndpointAuthSigningAlgValuesSupported = append(config.RevocationEndpointAuthSigningAlgValuesSupported, alg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.IsStringInSlice(alg, config.TokenEndpointAuthSigningAlgValuesSupported) {
|
||||||
|
config.TokenEndpointAuthSigningAlgValuesSupported = append(config.TokenEndpointAuthSigningAlgValuesSupported, alg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sort.Sort(SortedSigningAlgs(config.IDTokenSigningAlgValuesSupported))
|
sort.Sort(SortedSigningAlgs(config.IDTokenSigningAlgValuesSupported))
|
||||||
sort.Sort(SortedSigningAlgs(config.UserinfoSigningAlgValuesSupported))
|
sort.Sort(SortedSigningAlgs(config.UserinfoSigningAlgValuesSupported))
|
||||||
sort.Sort(SortedSigningAlgs(config.RequestObjectSigningAlgValuesSupported))
|
sort.Sort(SortedSigningAlgs(config.RequestObjectSigningAlgValuesSupported))
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
package oidc
|
package oidc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
||||||
|
@ -13,91 +16,119 @@ func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
||||||
desc string
|
desc string
|
||||||
pkcePlainChallenge bool
|
pkcePlainChallenge bool
|
||||||
enforcePAR bool
|
enforcePAR bool
|
||||||
clients map[string]Client
|
clients map[string]oidc.Client
|
||||||
discovery schema.OpenIDConnectDiscovery
|
discovery schema.OpenIDConnectDiscovery
|
||||||
|
|
||||||
expectCodeChallengeMethodsSupported, expectSubjectTypesSupported, expectedIDTokenSigAlgsSupported, expectedUserInfoSigAlgsSupported []string
|
expectCodeChallengeMethodsSupported, expectSubjectTypesSupported []string
|
||||||
|
expectedIDTokenSigAlgsSupported, expectedUserInfoSigAlgsSupported []string
|
||||||
|
|
||||||
|
expectedRequestObjectSigAlgsSupported, expectedRevocationSigAlgsSupported, expectedTokenAuthSigAlgsSupported []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "ShouldHaveChallengeMethodsS256ANDSubjectTypesSupportedPublic",
|
desc: "ShouldHaveChallengeMethodsS256ANDSubjectTypesSupportedPublic",
|
||||||
pkcePlainChallenge: false,
|
pkcePlainChallenge: false,
|
||||||
clients: map[string]Client{"a": &BaseClient{}},
|
clients: map[string]oidc.Client{"a": &oidc.BaseClient{}},
|
||||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256},
|
expectCodeChallengeMethodsSupported: []string{oidc.PKCEChallengeMethodSHA256},
|
||||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
expectSubjectTypesSupported: []string{oidc.SubjectTypePublic, oidc.SubjectTypePairwise},
|
||||||
expectedIDTokenSigAlgsSupported: []string{SigningAlgRSAUsingSHA256},
|
expectedIDTokenSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256},
|
||||||
expectedUserInfoSigAlgsSupported: []string{SigningAlgRSAUsingSHA256, SigningAlgNone},
|
expectedUserInfoSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRequestObjectSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRevocationSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
|
expectedTokenAuthSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "ShouldIncludDiscoveryInfo",
|
desc: "ShouldIncludeDiscoveryInfo",
|
||||||
pkcePlainChallenge: false,
|
pkcePlainChallenge: false,
|
||||||
clients: map[string]Client{"a": &BaseClient{}},
|
clients: map[string]oidc.Client{"a": &oidc.BaseClient{}},
|
||||||
discovery: schema.OpenIDConnectDiscovery{
|
discovery: schema.OpenIDConnectDiscovery{
|
||||||
RegisteredJWKSigningAlgs: []string{SigningAlgECDSAUsingP521AndSHA512},
|
ResponseObjectSigningAlgs: []string{oidc.SigningAlgECDSAUsingP521AndSHA512},
|
||||||
|
RequestObjectSigningAlgs: []string{oidc.SigningAlgECDSAUsingP256AndSHA256},
|
||||||
},
|
},
|
||||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256},
|
expectCodeChallengeMethodsSupported: []string{oidc.PKCEChallengeMethodSHA256},
|
||||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
expectSubjectTypesSupported: []string{oidc.SubjectTypePublic, oidc.SubjectTypePairwise},
|
||||||
expectedIDTokenSigAlgsSupported: []string{SigningAlgRSAUsingSHA256, SigningAlgECDSAUsingP521AndSHA512},
|
expectedIDTokenSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgECDSAUsingP521AndSHA512},
|
||||||
expectedUserInfoSigAlgsSupported: []string{SigningAlgRSAUsingSHA256, SigningAlgECDSAUsingP521AndSHA512, SigningAlgNone},
|
expectedUserInfoSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgECDSAUsingP521AndSHA512, oidc.SigningAlgNone},
|
||||||
|
expectedRequestObjectSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgECDSAUsingP256AndSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRevocationSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512, oidc.SigningAlgECDSAUsingP256AndSHA256},
|
||||||
|
expectedTokenAuthSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512, oidc.SigningAlgECDSAUsingP256AndSHA256},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "ShouldHaveChallengeMethodsS256PlainANDSubjectTypesSupportedPublic",
|
desc: "ShouldHaveChallengeMethodsS256PlainANDSubjectTypesSupportedPublic",
|
||||||
pkcePlainChallenge: true,
|
pkcePlainChallenge: true,
|
||||||
clients: map[string]Client{"a": &BaseClient{}},
|
clients: map[string]oidc.Client{"a": &oidc.BaseClient{}},
|
||||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
|
expectCodeChallengeMethodsSupported: []string{oidc.PKCEChallengeMethodSHA256, oidc.PKCEChallengeMethodPlain},
|
||||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
expectSubjectTypesSupported: []string{oidc.SubjectTypePublic, oidc.SubjectTypePairwise},
|
||||||
expectedIDTokenSigAlgsSupported: []string{SigningAlgRSAUsingSHA256},
|
expectedIDTokenSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256},
|
||||||
expectedUserInfoSigAlgsSupported: []string{SigningAlgRSAUsingSHA256, SigningAlgNone},
|
expectedUserInfoSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRequestObjectSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRevocationSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
|
expectedTokenAuthSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "ShouldHaveChallengeMethodsS256ANDSubjectTypesSupportedPublicPairwise",
|
desc: "ShouldHaveChallengeMethodsS256ANDSubjectTypesSupportedPublicPairwise",
|
||||||
pkcePlainChallenge: false,
|
pkcePlainChallenge: false,
|
||||||
clients: map[string]Client{"a": &BaseClient{SectorIdentifier: "yes"}},
|
clients: map[string]oidc.Client{"a": &oidc.BaseClient{SectorIdentifier: "yes"}},
|
||||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256},
|
expectCodeChallengeMethodsSupported: []string{oidc.PKCEChallengeMethodSHA256},
|
||||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
expectSubjectTypesSupported: []string{oidc.SubjectTypePublic, oidc.SubjectTypePairwise},
|
||||||
expectedIDTokenSigAlgsSupported: []string{SigningAlgRSAUsingSHA256},
|
expectedIDTokenSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256},
|
||||||
expectedUserInfoSigAlgsSupported: []string{SigningAlgRSAUsingSHA256, SigningAlgNone},
|
expectedUserInfoSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRequestObjectSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRevocationSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
|
expectedTokenAuthSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "ShouldHaveChallengeMethodsS256PlainANDSubjectTypesSupportedPublicPairwise",
|
desc: "ShouldHaveChallengeMethodsS256PlainANDSubjectTypesSupportedPublicPairwise",
|
||||||
pkcePlainChallenge: true,
|
pkcePlainChallenge: true,
|
||||||
clients: map[string]Client{"a": &BaseClient{SectorIdentifier: "yes"}},
|
clients: map[string]oidc.Client{"a": &oidc.BaseClient{SectorIdentifier: "yes"}},
|
||||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
|
expectCodeChallengeMethodsSupported: []string{oidc.PKCEChallengeMethodSHA256, oidc.PKCEChallengeMethodPlain},
|
||||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
expectSubjectTypesSupported: []string{oidc.SubjectTypePublic, oidc.SubjectTypePairwise},
|
||||||
expectedIDTokenSigAlgsSupported: []string{SigningAlgRSAUsingSHA256},
|
expectedIDTokenSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256},
|
||||||
expectedUserInfoSigAlgsSupported: []string{SigningAlgRSAUsingSHA256, SigningAlgNone},
|
expectedUserInfoSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRequestObjectSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRevocationSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
|
expectedTokenAuthSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "ShouldHaveTokenAuthMethodsNone",
|
desc: "ShouldHaveTokenAuthMethodsNone",
|
||||||
pkcePlainChallenge: true,
|
pkcePlainChallenge: true,
|
||||||
clients: map[string]Client{"a": &BaseClient{SectorIdentifier: "yes"}},
|
clients: map[string]oidc.Client{"a": &oidc.BaseClient{SectorIdentifier: "yes"}},
|
||||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
|
expectCodeChallengeMethodsSupported: []string{oidc.PKCEChallengeMethodSHA256, oidc.PKCEChallengeMethodPlain},
|
||||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
expectSubjectTypesSupported: []string{oidc.SubjectTypePublic, oidc.SubjectTypePairwise},
|
||||||
expectedIDTokenSigAlgsSupported: []string{SigningAlgRSAUsingSHA256},
|
expectedIDTokenSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256},
|
||||||
expectedUserInfoSigAlgsSupported: []string{SigningAlgRSAUsingSHA256, SigningAlgNone},
|
expectedUserInfoSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRequestObjectSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRevocationSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
|
expectedTokenAuthSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "ShouldHaveTokenAuthMethodsNone",
|
desc: "ShouldHaveTokenAuthMethodsNone",
|
||||||
pkcePlainChallenge: true,
|
pkcePlainChallenge: true,
|
||||||
clients: map[string]Client{
|
clients: map[string]oidc.Client{
|
||||||
"a": &BaseClient{SectorIdentifier: "yes"},
|
"a": &oidc.BaseClient{SectorIdentifier: "yes"},
|
||||||
"b": &BaseClient{SectorIdentifier: "yes"},
|
"b": &oidc.BaseClient{SectorIdentifier: "yes"},
|
||||||
},
|
},
|
||||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
|
expectCodeChallengeMethodsSupported: []string{oidc.PKCEChallengeMethodSHA256, oidc.PKCEChallengeMethodPlain},
|
||||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
expectSubjectTypesSupported: []string{oidc.SubjectTypePublic, oidc.SubjectTypePairwise},
|
||||||
expectedIDTokenSigAlgsSupported: []string{SigningAlgRSAUsingSHA256},
|
expectedIDTokenSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256},
|
||||||
expectedUserInfoSigAlgsSupported: []string{SigningAlgRSAUsingSHA256, SigningAlgNone},
|
expectedUserInfoSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRequestObjectSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRevocationSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
|
expectedTokenAuthSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "ShouldHaveTokenAuthMethodsNone",
|
desc: "ShouldHaveTokenAuthMethodsNone",
|
||||||
pkcePlainChallenge: true,
|
pkcePlainChallenge: true,
|
||||||
clients: map[string]Client{
|
clients: map[string]oidc.Client{
|
||||||
"a": &BaseClient{SectorIdentifier: "yes"},
|
"a": &oidc.BaseClient{SectorIdentifier: "yes"},
|
||||||
"b": &BaseClient{SectorIdentifier: "yes"},
|
"b": &oidc.BaseClient{SectorIdentifier: "yes"},
|
||||||
},
|
},
|
||||||
expectCodeChallengeMethodsSupported: []string{PKCEChallengeMethodSHA256, PKCEChallengeMethodPlain},
|
expectCodeChallengeMethodsSupported: []string{oidc.PKCEChallengeMethodSHA256, oidc.PKCEChallengeMethodPlain},
|
||||||
expectSubjectTypesSupported: []string{SubjectTypePublic, SubjectTypePairwise},
|
expectSubjectTypesSupported: []string{oidc.SubjectTypePublic, oidc.SubjectTypePairwise},
|
||||||
expectedIDTokenSigAlgsSupported: []string{SigningAlgRSAUsingSHA256},
|
expectedIDTokenSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256},
|
||||||
expectedUserInfoSigAlgsSupported: []string{SigningAlgRSAUsingSHA256, SigningAlgNone},
|
expectedUserInfoSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRequestObjectSigAlgsSupported: []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgNone},
|
||||||
|
expectedRevocationSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
|
expectedTokenAuthSigAlgsSupported: []string{oidc.SigningAlgHMACUsingSHA256, oidc.SigningAlgHMACUsingSHA384, oidc.SigningAlgHMACUsingSHA512},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +142,7 @@ func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
||||||
Discovery: tc.discovery,
|
Discovery: tc.discovery,
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := NewOpenIDConnectWellKnownConfiguration(&c)
|
actual := oidc.NewOpenIDConnectWellKnownConfiguration(&c)
|
||||||
for _, codeChallengeMethod := range tc.expectCodeChallengeMethodsSupported {
|
for _, codeChallengeMethod := range tc.expectCodeChallengeMethodsSupported {
|
||||||
assert.Contains(t, actual.CodeChallengeMethodsSupported, codeChallengeMethod)
|
assert.Contains(t, actual.CodeChallengeMethodsSupported, codeChallengeMethod)
|
||||||
}
|
}
|
||||||
|
@ -130,6 +161,425 @@ func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, tc.expectedUserInfoSigAlgsSupported, actual.UserinfoSigningAlgValuesSupported)
|
assert.Equal(t, tc.expectedUserInfoSigAlgsSupported, actual.UserinfoSigningAlgValuesSupported)
|
||||||
assert.Equal(t, tc.expectedIDTokenSigAlgsSupported, actual.IDTokenSigningAlgValuesSupported)
|
assert.Equal(t, tc.expectedIDTokenSigAlgsSupported, actual.IDTokenSigningAlgValuesSupported)
|
||||||
|
assert.Equal(t, tc.expectedRequestObjectSigAlgsSupported, actual.RequestObjectSigningAlgValuesSupported)
|
||||||
|
assert.Equal(t, tc.expectedRevocationSigAlgsSupported, actual.RevocationEndpointAuthSigningAlgValuesSupported)
|
||||||
|
assert.Equal(t, tc.expectedTokenAuthSigAlgsSupported, actual.TokenEndpointAuthSigningAlgValuesSupported)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewOpenIDConnectProviderDiscovery(t *testing.T) {
|
||||||
|
provider := oidc.NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
|
IssuerPrivateKey: keyRSA2048,
|
||||||
|
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||||
|
EnablePKCEPlainChallenge: true,
|
||||||
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
{
|
||||||
|
ID: "a-client",
|
||||||
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
|
Policy: onefactor,
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://google.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil, nil)
|
||||||
|
|
||||||
|
a := provider.GetOpenIDConnectWellKnownConfiguration("https://auth.example.com")
|
||||||
|
|
||||||
|
data, err := json.Marshal(&a)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
b := oidc.OpenIDConnectWellKnownConfiguration{}
|
||||||
|
|
||||||
|
assert.NoError(t, json.Unmarshal(data, &b))
|
||||||
|
|
||||||
|
assert.Equal(t, a, b)
|
||||||
|
|
||||||
|
y := provider.GetOAuth2WellKnownConfiguration("https://auth.example.com")
|
||||||
|
|
||||||
|
data, err = json.Marshal(&y)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
z := oidc.OAuth2WellKnownConfiguration{}
|
||||||
|
|
||||||
|
assert.NoError(t, json.Unmarshal(data, &z))
|
||||||
|
|
||||||
|
assert.Equal(t, y, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewOpenIDConnectProvider_GetOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
||||||
|
provider := oidc.NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
|
IssuerPrivateKey: keyRSA2048,
|
||||||
|
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||||
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
{
|
||||||
|
ID: "a-client",
|
||||||
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
|
Policy: onefactor,
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://google.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil, nil)
|
||||||
|
|
||||||
|
require.NotNil(t, provider)
|
||||||
|
|
||||||
|
disco := provider.GetOpenIDConnectWellKnownConfiguration(examplecom)
|
||||||
|
|
||||||
|
assert.Equal(t, examplecom, disco.Issuer)
|
||||||
|
assert.Equal(t, "https://example.com/jwks.json", disco.JWKSURI)
|
||||||
|
assert.Equal(t, "https://example.com/api/oidc/authorization", disco.AuthorizationEndpoint)
|
||||||
|
assert.Equal(t, "https://example.com/api/oidc/token", disco.TokenEndpoint)
|
||||||
|
assert.Equal(t, "https://example.com/api/oidc/userinfo", disco.UserinfoEndpoint)
|
||||||
|
assert.Equal(t, "https://example.com/api/oidc/introspection", disco.IntrospectionEndpoint)
|
||||||
|
assert.Equal(t, "https://example.com/api/oidc/revocation", disco.RevocationEndpoint)
|
||||||
|
assert.Equal(t, "", disco.RegistrationEndpoint)
|
||||||
|
|
||||||
|
assert.Len(t, disco.CodeChallengeMethodsSupported, 1)
|
||||||
|
assert.Contains(t, disco.CodeChallengeMethodsSupported, oidc.PKCEChallengeMethodSHA256)
|
||||||
|
|
||||||
|
assert.Len(t, disco.ScopesSupported, 5)
|
||||||
|
assert.Contains(t, disco.ScopesSupported, oidc.ScopeOpenID)
|
||||||
|
assert.Contains(t, disco.ScopesSupported, oidc.ScopeOfflineAccess)
|
||||||
|
assert.Contains(t, disco.ScopesSupported, oidc.ScopeProfile)
|
||||||
|
assert.Contains(t, disco.ScopesSupported, oidc.ScopeGroups)
|
||||||
|
assert.Contains(t, disco.ScopesSupported, oidc.ScopeEmail)
|
||||||
|
|
||||||
|
assert.Len(t, disco.ResponseModesSupported, 3)
|
||||||
|
assert.Contains(t, disco.ResponseModesSupported, oidc.ResponseModeFormPost)
|
||||||
|
assert.Contains(t, disco.ResponseModesSupported, oidc.ResponseModeQuery)
|
||||||
|
assert.Contains(t, disco.ResponseModesSupported, oidc.ResponseModeFragment)
|
||||||
|
|
||||||
|
assert.Len(t, disco.SubjectTypesSupported, 2)
|
||||||
|
assert.Contains(t, disco.SubjectTypesSupported, oidc.SubjectTypePublic)
|
||||||
|
assert.Contains(t, disco.SubjectTypesSupported, oidc.SubjectTypePairwise)
|
||||||
|
|
||||||
|
assert.Len(t, disco.ResponseTypesSupported, 7)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeAuthorizationCodeFlow)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeImplicitFlowIDToken)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeImplicitFlowToken)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeImplicitFlowBoth)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeHybridFlowIDToken)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeHybridFlowToken)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeHybridFlowBoth)
|
||||||
|
|
||||||
|
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 5)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, oidc.ClientAuthMethodClientSecretBasic)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, oidc.ClientAuthMethodClientSecretPost)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, oidc.ClientAuthMethodClientSecretJWT)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, oidc.ClientAuthMethodPrivateKeyJWT)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, oidc.ClientAuthMethodNone)
|
||||||
|
|
||||||
|
assert.Len(t, disco.RevocationEndpointAuthMethodsSupported, 5)
|
||||||
|
assert.Contains(t, disco.RevocationEndpointAuthMethodsSupported, oidc.ClientAuthMethodClientSecretBasic)
|
||||||
|
assert.Contains(t, disco.RevocationEndpointAuthMethodsSupported, oidc.ClientAuthMethodClientSecretPost)
|
||||||
|
assert.Contains(t, disco.RevocationEndpointAuthMethodsSupported, oidc.ClientAuthMethodClientSecretJWT)
|
||||||
|
assert.Contains(t, disco.RevocationEndpointAuthMethodsSupported, oidc.ClientAuthMethodPrivateKeyJWT)
|
||||||
|
assert.Contains(t, disco.RevocationEndpointAuthMethodsSupported, oidc.ClientAuthMethodNone)
|
||||||
|
|
||||||
|
assert.Len(t, disco.IntrospectionEndpointAuthMethodsSupported, 2)
|
||||||
|
assert.Contains(t, disco.IntrospectionEndpointAuthMethodsSupported, oidc.ClientAuthMethodClientSecretBasic)
|
||||||
|
assert.Contains(t, disco.IntrospectionEndpointAuthMethodsSupported, oidc.ClientAuthMethodNone)
|
||||||
|
|
||||||
|
assert.Len(t, disco.GrantTypesSupported, 3)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, oidc.GrantTypeAuthorizationCode)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, oidc.GrantTypeRefreshToken)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, oidc.GrantTypeImplicit)
|
||||||
|
|
||||||
|
assert.Len(t, disco.RevocationEndpointAuthSigningAlgValuesSupported, 3)
|
||||||
|
assert.Equal(t, disco.RevocationEndpointAuthSigningAlgValuesSupported[0], oidc.SigningAlgHMACUsingSHA256)
|
||||||
|
assert.Equal(t, disco.RevocationEndpointAuthSigningAlgValuesSupported[1], oidc.SigningAlgHMACUsingSHA384)
|
||||||
|
assert.Equal(t, disco.RevocationEndpointAuthSigningAlgValuesSupported[2], oidc.SigningAlgHMACUsingSHA512)
|
||||||
|
|
||||||
|
assert.Len(t, disco.TokenEndpointAuthSigningAlgValuesSupported, 3)
|
||||||
|
assert.Equal(t, disco.TokenEndpointAuthSigningAlgValuesSupported[0], oidc.SigningAlgHMACUsingSHA256)
|
||||||
|
assert.Equal(t, disco.TokenEndpointAuthSigningAlgValuesSupported[1], oidc.SigningAlgHMACUsingSHA384)
|
||||||
|
assert.Equal(t, disco.TokenEndpointAuthSigningAlgValuesSupported[2], oidc.SigningAlgHMACUsingSHA512)
|
||||||
|
|
||||||
|
assert.Len(t, disco.IDTokenSigningAlgValuesSupported, 1)
|
||||||
|
assert.Contains(t, disco.IDTokenSigningAlgValuesSupported, oidc.SigningAlgRSAUsingSHA256)
|
||||||
|
|
||||||
|
assert.Len(t, disco.UserinfoSigningAlgValuesSupported, 2)
|
||||||
|
assert.Equal(t, disco.UserinfoSigningAlgValuesSupported[0], oidc.SigningAlgRSAUsingSHA256)
|
||||||
|
assert.Equal(t, disco.UserinfoSigningAlgValuesSupported[1], oidc.SigningAlgNone)
|
||||||
|
|
||||||
|
require.Len(t, disco.RequestObjectSigningAlgValuesSupported, 2)
|
||||||
|
assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, disco.RequestObjectSigningAlgValuesSupported[0])
|
||||||
|
assert.Equal(t, oidc.SigningAlgNone, disco.RequestObjectSigningAlgValuesSupported[1])
|
||||||
|
|
||||||
|
assert.Len(t, disco.ClaimsSupported, 18)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimAuthenticationMethodsReference)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimAudience)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimAuthorizedParty)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimClientIdentifier)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimExpirationTime)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimIssuedAt)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimIssuer)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimJWTID)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimRequestedAt)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimSubject)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimAuthenticationTime)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimNonce)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimPreferredEmail)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimEmailVerified)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimEmailAlts)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimGroups)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimPreferredUsername)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimFullName)
|
||||||
|
|
||||||
|
assert.Len(t, disco.PromptValuesSupported, 2)
|
||||||
|
assert.Contains(t, disco.PromptValuesSupported, oidc.PromptConsent)
|
||||||
|
assert.Contains(t, disco.PromptValuesSupported, oidc.PromptNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewOpenIDConnectProvider_GetOAuth2WellKnownConfiguration(t *testing.T) {
|
||||||
|
provider := oidc.NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
|
IssuerPrivateKey: keyRSA2048,
|
||||||
|
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||||
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
{
|
||||||
|
ID: "a-client",
|
||||||
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
|
Policy: onefactor,
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://google.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil, nil)
|
||||||
|
|
||||||
|
require.NotNil(t, provider)
|
||||||
|
|
||||||
|
disco := provider.GetOAuth2WellKnownConfiguration(examplecom)
|
||||||
|
|
||||||
|
assert.Equal(t, examplecom, disco.Issuer)
|
||||||
|
assert.Equal(t, "https://example.com/jwks.json", disco.JWKSURI)
|
||||||
|
assert.Equal(t, "https://example.com/api/oidc/authorization", disco.AuthorizationEndpoint)
|
||||||
|
assert.Equal(t, "https://example.com/api/oidc/token", disco.TokenEndpoint)
|
||||||
|
assert.Equal(t, "https://example.com/api/oidc/introspection", disco.IntrospectionEndpoint)
|
||||||
|
assert.Equal(t, "https://example.com/api/oidc/revocation", disco.RevocationEndpoint)
|
||||||
|
assert.Equal(t, "", disco.RegistrationEndpoint)
|
||||||
|
|
||||||
|
require.Len(t, disco.CodeChallengeMethodsSupported, 1)
|
||||||
|
assert.Equal(t, "S256", disco.CodeChallengeMethodsSupported[0])
|
||||||
|
|
||||||
|
assert.Len(t, disco.ScopesSupported, 5)
|
||||||
|
assert.Contains(t, disco.ScopesSupported, oidc.ScopeOpenID)
|
||||||
|
assert.Contains(t, disco.ScopesSupported, oidc.ScopeOfflineAccess)
|
||||||
|
assert.Contains(t, disco.ScopesSupported, oidc.ScopeProfile)
|
||||||
|
assert.Contains(t, disco.ScopesSupported, oidc.ScopeGroups)
|
||||||
|
assert.Contains(t, disco.ScopesSupported, oidc.ScopeEmail)
|
||||||
|
|
||||||
|
assert.Len(t, disco.ResponseModesSupported, 3)
|
||||||
|
assert.Contains(t, disco.ResponseModesSupported, oidc.ResponseModeFormPost)
|
||||||
|
assert.Contains(t, disco.ResponseModesSupported, oidc.ResponseModeQuery)
|
||||||
|
assert.Contains(t, disco.ResponseModesSupported, oidc.ResponseModeFragment)
|
||||||
|
|
||||||
|
assert.Len(t, disco.SubjectTypesSupported, 2)
|
||||||
|
assert.Contains(t, disco.SubjectTypesSupported, oidc.SubjectTypePublic)
|
||||||
|
assert.Contains(t, disco.SubjectTypesSupported, oidc.SubjectTypePairwise)
|
||||||
|
|
||||||
|
assert.Len(t, disco.ResponseTypesSupported, 7)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeAuthorizationCodeFlow)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeImplicitFlowIDToken)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeImplicitFlowToken)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeImplicitFlowBoth)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeHybridFlowIDToken)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeHybridFlowToken)
|
||||||
|
assert.Contains(t, disco.ResponseTypesSupported, oidc.ResponseTypeHybridFlowBoth)
|
||||||
|
|
||||||
|
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 5)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, oidc.ClientAuthMethodClientSecretBasic)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, oidc.ClientAuthMethodClientSecretPost)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, oidc.ClientAuthMethodClientSecretJWT)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, oidc.ClientAuthMethodPrivateKeyJWT)
|
||||||
|
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, oidc.ClientAuthMethodNone)
|
||||||
|
|
||||||
|
assert.Len(t, disco.GrantTypesSupported, 3)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, oidc.GrantTypeAuthorizationCode)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, oidc.GrantTypeRefreshToken)
|
||||||
|
assert.Contains(t, disco.GrantTypesSupported, oidc.GrantTypeImplicit)
|
||||||
|
|
||||||
|
assert.Len(t, disco.ClaimsSupported, 18)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimAuthenticationMethodsReference)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimAudience)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimAuthorizedParty)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimClientIdentifier)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimExpirationTime)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimIssuedAt)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimIssuer)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimJWTID)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimRequestedAt)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimSubject)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimAuthenticationTime)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimNonce)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimPreferredEmail)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimEmailVerified)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimEmailAlts)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimGroups)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimPreferredUsername)
|
||||||
|
assert.Contains(t, disco.ClaimsSupported, oidc.ClaimFullName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewOpenIDConnectProvider_GetOpenIDConnectWellKnownConfigurationWithPlainPKCE(t *testing.T) {
|
||||||
|
provider := oidc.NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
|
IssuerPrivateKey: keyRSA2048,
|
||||||
|
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||||
|
EnablePKCEPlainChallenge: true,
|
||||||
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
{
|
||||||
|
ID: "a-client",
|
||||||
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
|
Policy: onefactor,
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://google.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil, nil)
|
||||||
|
|
||||||
|
require.NotNil(t, provider)
|
||||||
|
|
||||||
|
disco := provider.GetOpenIDConnectWellKnownConfiguration(examplecom)
|
||||||
|
|
||||||
|
require.Len(t, disco.CodeChallengeMethodsSupported, 2)
|
||||||
|
assert.Equal(t, oidc.PKCEChallengeMethodSHA256, disco.CodeChallengeMethodsSupported[0])
|
||||||
|
assert.Equal(t, oidc.PKCEChallengeMethodPlain, disco.CodeChallengeMethodsSupported[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewOpenIDConnectWellKnownConfiguration_Copy(t *testing.T) {
|
||||||
|
config := &oidc.OpenIDConnectWellKnownConfiguration{
|
||||||
|
OAuth2WellKnownConfiguration: oidc.OAuth2WellKnownConfiguration{
|
||||||
|
CommonDiscoveryOptions: oidc.CommonDiscoveryOptions{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
JWKSURI: "https://example.com/jwks.json",
|
||||||
|
AuthorizationEndpoint: "",
|
||||||
|
TokenEndpoint: "",
|
||||||
|
SubjectTypesSupported: nil,
|
||||||
|
ResponseTypesSupported: nil,
|
||||||
|
GrantTypesSupported: nil,
|
||||||
|
ResponseModesSupported: nil,
|
||||||
|
ScopesSupported: nil,
|
||||||
|
ClaimsSupported: nil,
|
||||||
|
UILocalesSupported: nil,
|
||||||
|
TokenEndpointAuthMethodsSupported: nil,
|
||||||
|
TokenEndpointAuthSigningAlgValuesSupported: nil,
|
||||||
|
ServiceDocumentation: "",
|
||||||
|
OPPolicyURI: "",
|
||||||
|
OPTOSURI: "",
|
||||||
|
SignedMetadata: "",
|
||||||
|
},
|
||||||
|
OAuth2DiscoveryOptions: oidc.OAuth2DiscoveryOptions{
|
||||||
|
IntrospectionEndpoint: "",
|
||||||
|
RevocationEndpoint: "",
|
||||||
|
RegistrationEndpoint: "",
|
||||||
|
IntrospectionEndpointAuthMethodsSupported: nil,
|
||||||
|
RevocationEndpointAuthMethodsSupported: nil,
|
||||||
|
RevocationEndpointAuthSigningAlgValuesSupported: nil,
|
||||||
|
IntrospectionEndpointAuthSigningAlgValuesSupported: nil,
|
||||||
|
CodeChallengeMethodsSupported: nil,
|
||||||
|
},
|
||||||
|
OAuth2DeviceAuthorizationGrantDiscoveryOptions: &oidc.OAuth2DeviceAuthorizationGrantDiscoveryOptions{
|
||||||
|
DeviceAuthorizationEndpoint: "",
|
||||||
|
},
|
||||||
|
OAuth2MutualTLSClientAuthenticationDiscoveryOptions: &oidc.OAuth2MutualTLSClientAuthenticationDiscoveryOptions{
|
||||||
|
TLSClientCertificateBoundAccessTokens: false,
|
||||||
|
MutualTLSEndpointAliases: oidc.OAuth2MutualTLSClientAuthenticationAliasesDiscoveryOptions{
|
||||||
|
AuthorizationEndpoint: "",
|
||||||
|
TokenEndpoint: "",
|
||||||
|
IntrospectionEndpoint: "",
|
||||||
|
RevocationEndpoint: "",
|
||||||
|
EndSessionEndpoint: "",
|
||||||
|
UserinfoEndpoint: "",
|
||||||
|
BackChannelAuthenticationEndpoint: "",
|
||||||
|
FederationRegistrationEndpoint: "",
|
||||||
|
PushedAuthorizationRequestEndpoint: "",
|
||||||
|
RegistrationEndpoint: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OAuth2IssuerIdentificationDiscoveryOptions: &oidc.OAuth2IssuerIdentificationDiscoveryOptions{
|
||||||
|
AuthorizationResponseIssuerParameterSupported: false,
|
||||||
|
},
|
||||||
|
OAuth2JWTIntrospectionResponseDiscoveryOptions: &oidc.OAuth2JWTIntrospectionResponseDiscoveryOptions{
|
||||||
|
IntrospectionSigningAlgValuesSupported: nil,
|
||||||
|
IntrospectionEncryptionAlgValuesSupported: nil,
|
||||||
|
IntrospectionEncryptionEncValuesSupported: nil,
|
||||||
|
},
|
||||||
|
OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions: &oidc.OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions{
|
||||||
|
RequireSignedRequestObject: false,
|
||||||
|
},
|
||||||
|
OAuth2PushedAuthorizationDiscoveryOptions: &oidc.OAuth2PushedAuthorizationDiscoveryOptions{
|
||||||
|
PushedAuthorizationRequestEndpoint: "",
|
||||||
|
RequirePushedAuthorizationRequests: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OpenIDConnectDiscoveryOptions: oidc.OpenIDConnectDiscoveryOptions{
|
||||||
|
UserinfoEndpoint: "",
|
||||||
|
IDTokenSigningAlgValuesSupported: nil,
|
||||||
|
UserinfoSigningAlgValuesSupported: nil,
|
||||||
|
RequestObjectSigningAlgValuesSupported: nil,
|
||||||
|
IDTokenEncryptionAlgValuesSupported: nil,
|
||||||
|
UserinfoEncryptionAlgValuesSupported: nil,
|
||||||
|
RequestObjectEncryptionAlgValuesSupported: nil,
|
||||||
|
IDTokenEncryptionEncValuesSupported: nil,
|
||||||
|
UserinfoEncryptionEncValuesSupported: nil,
|
||||||
|
RequestObjectEncryptionEncValuesSupported: nil,
|
||||||
|
ACRValuesSupported: nil,
|
||||||
|
DisplayValuesSupported: nil,
|
||||||
|
ClaimTypesSupported: nil,
|
||||||
|
ClaimLocalesSupported: nil,
|
||||||
|
RequestParameterSupported: false,
|
||||||
|
RequestURIParameterSupported: false,
|
||||||
|
RequireRequestURIRegistration: false,
|
||||||
|
ClaimsParameterSupported: false,
|
||||||
|
},
|
||||||
|
OpenIDConnectFrontChannelLogoutDiscoveryOptions: &oidc.OpenIDConnectFrontChannelLogoutDiscoveryOptions{
|
||||||
|
FrontChannelLogoutSupported: false,
|
||||||
|
FrontChannelLogoutSessionSupported: false,
|
||||||
|
},
|
||||||
|
OpenIDConnectBackChannelLogoutDiscoveryOptions: &oidc.OpenIDConnectBackChannelLogoutDiscoveryOptions{
|
||||||
|
BackChannelLogoutSupported: false,
|
||||||
|
BackChannelLogoutSessionSupported: false,
|
||||||
|
},
|
||||||
|
OpenIDConnectSessionManagementDiscoveryOptions: &oidc.OpenIDConnectSessionManagementDiscoveryOptions{
|
||||||
|
CheckSessionIFrame: "",
|
||||||
|
},
|
||||||
|
OpenIDConnectRPInitiatedLogoutDiscoveryOptions: &oidc.OpenIDConnectRPInitiatedLogoutDiscoveryOptions{
|
||||||
|
EndSessionEndpoint: "",
|
||||||
|
},
|
||||||
|
OpenIDConnectPromptCreateDiscoveryOptions: &oidc.OpenIDConnectPromptCreateDiscoveryOptions{
|
||||||
|
PromptValuesSupported: nil,
|
||||||
|
},
|
||||||
|
OpenIDConnectClientInitiatedBackChannelAuthFlowDiscoveryOptions: &oidc.OpenIDConnectClientInitiatedBackChannelAuthFlowDiscoveryOptions{
|
||||||
|
BackChannelAuthenticationEndpoint: "",
|
||||||
|
BackChannelTokenDeliveryModesSupported: nil,
|
||||||
|
BackChannelAuthRequestSigningAlgValuesSupported: nil,
|
||||||
|
BackChannelUserCodeParameterSupported: false,
|
||||||
|
},
|
||||||
|
OpenIDConnectJWTSecuredAuthorizationResponseModeDiscoveryOptions: &oidc.OpenIDConnectJWTSecuredAuthorizationResponseModeDiscoveryOptions{
|
||||||
|
AuthorizationSigningAlgValuesSupported: nil,
|
||||||
|
AuthorizationEncryptionAlgValuesSupported: nil,
|
||||||
|
AuthorizationEncryptionEncValuesSupported: nil,
|
||||||
|
},
|
||||||
|
OpenIDFederationDiscoveryOptions: &oidc.OpenIDFederationDiscoveryOptions{
|
||||||
|
FederationRegistrationEndpoint: "",
|
||||||
|
ClientRegistrationTypesSupported: nil,
|
||||||
|
RequestAuthenticationMethodsSupported: nil,
|
||||||
|
RequestAuthenticationSigningAlgValuesSupproted: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
x := config.Copy()
|
||||||
|
|
||||||
|
assert.Equal(t, config, &x)
|
||||||
|
|
||||||
|
y := config.OAuth2WellKnownConfiguration.Copy()
|
||||||
|
|
||||||
|
assert.Equal(t, config.OAuth2WellKnownConfiguration, y)
|
||||||
|
}
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
package oidc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/go-crypt/crypt"
|
|
||||||
"github.com/go-crypt/crypt/algorithm"
|
|
||||||
"github.com/go-crypt/crypt/algorithm/plaintext"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewHasher returns a new Hasher.
|
|
||||||
func NewHasher() (hasher *Hasher, err error) {
|
|
||||||
hasher = &Hasher{}
|
|
||||||
|
|
||||||
if hasher.decoder, err = crypt.NewDefaultDecoder(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = plaintext.RegisterDecoderPlainText(hasher.decoder); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return hasher, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hasher implements the fosite.Hasher interface and adaptively compares hashes.
|
|
||||||
type Hasher struct {
|
|
||||||
decoder algorithm.DecoderRegister
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare compares the hash with the data and returns an error if they don't match.
|
|
||||||
func (h Hasher) Compare(_ context.Context, hash, data []byte) (err error) {
|
|
||||||
var digest algorithm.Digest
|
|
||||||
|
|
||||||
if digest, err = h.decoder.Decode(string(hash)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if digest.MatchBytes(data) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errPasswordsDoNotMatch
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash creates a new hash from data.
|
|
||||||
func (h Hasher) Hash(_ context.Context, data []byte) (hash []byte, err error) {
|
|
||||||
return data, nil
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
package oidc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
|
|
||||||
hasher, err := NewHasher()
|
|
||||||
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
a := []byte("$plaintext$abc")
|
|
||||||
b := []byte("abc")
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
assert.NoError(t, hasher.Compare(ctx, a, b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShouldNotRaiseErrorOnEqualPasswordsPlainTextWithSeparator(t *testing.T) {
|
|
||||||
hasher, err := NewHasher()
|
|
||||||
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
a := []byte("$plaintext$abc$123")
|
|
||||||
b := []byte("abc$123")
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
assert.NoError(t, hasher.Compare(ctx, a, b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
|
|
||||||
hasher, err := NewHasher()
|
|
||||||
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
a := []byte("$plaintext$abc")
|
|
||||||
b := []byte("abcd")
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
assert.Equal(t, errPasswordsDoNotMatch, hasher.Compare(ctx, a, b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShouldHashPassword(t *testing.T) {
|
|
||||||
hasher := Hasher{}
|
|
||||||
|
|
||||||
data := []byte("abc")
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
hash, err := hasher.Hash(ctx, data)
|
|
||||||
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, data, hash)
|
|
||||||
}
|
|
|
@ -26,7 +26,7 @@ func NewKeyManager(config *schema.OpenIDConnectConfiguration) (manager *KeyManag
|
||||||
algs: map[string]*JWK{},
|
algs: map[string]*JWK{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, sjwk := range config.IssuerJWKS {
|
for _, sjwk := range config.IssuerPrivateKeys {
|
||||||
jwk := NewJWK(sjwk)
|
jwk := NewJWK(sjwk)
|
||||||
|
|
||||||
manager.kids[sjwk.KeyID] = jwk
|
manager.kids[sjwk.KeyID] = jwk
|
||||||
|
@ -47,7 +47,13 @@ type KeyManager struct {
|
||||||
algs map[string]*JWK
|
algs map[string]*JWK
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *KeyManager) GetKIDFromAlgStrict(ctx context.Context, alg string) (kid string, err error) {
|
// GetKeyID returns the default key id.
|
||||||
|
func (m *KeyManager) GetKeyID(ctx context.Context) string {
|
||||||
|
return m.kid
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKeyIDFromAlgStrict returns the key id given an alg or an error if it doesn't exist.
|
||||||
|
func (m *KeyManager) GetKeyIDFromAlgStrict(ctx context.Context, alg string) (kid string, err error) {
|
||||||
if jwks, ok := m.algs[alg]; ok {
|
if jwks, ok := m.algs[alg]; ok {
|
||||||
return jwks.kid, nil
|
return jwks.kid, nil
|
||||||
}
|
}
|
||||||
|
@ -55,7 +61,8 @@ func (m *KeyManager) GetKIDFromAlgStrict(ctx context.Context, alg string) (kid s
|
||||||
return "", fmt.Errorf("alg not found")
|
return "", fmt.Errorf("alg not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *KeyManager) GetKIDFromAlg(ctx context.Context, alg string) string {
|
// GetKeyIDFromAlg returns the key id given an alg or the default if it doesn't exist.
|
||||||
|
func (m *KeyManager) GetKeyIDFromAlg(ctx context.Context, alg string) string {
|
||||||
if jwks, ok := m.algs[alg]; ok {
|
if jwks, ok := m.algs[alg]; ok {
|
||||||
return jwks.kid
|
return jwks.kid
|
||||||
}
|
}
|
||||||
|
@ -63,6 +70,7 @@ func (m *KeyManager) GetKIDFromAlg(ctx context.Context, alg string) string {
|
||||||
return m.kid
|
return m.kid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetByAlg returns the JWK given an alg or nil if it doesn't exist.
|
||||||
func (m *KeyManager) GetByAlg(ctx context.Context, alg string) *JWK {
|
func (m *KeyManager) GetByAlg(ctx context.Context, alg string) *JWK {
|
||||||
if jwk, ok := m.algs[alg]; ok {
|
if jwk, ok := m.algs[alg]; ok {
|
||||||
return jwk
|
return jwk
|
||||||
|
@ -71,6 +79,7 @@ func (m *KeyManager) GetByAlg(ctx context.Context, alg string) *JWK {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetByKID returns the JWK given an key id or nil if it doesn't exist. If given a blank string it returns the default.
|
||||||
func (m *KeyManager) GetByKID(ctx context.Context, kid string) *JWK {
|
func (m *KeyManager) GetByKID(ctx context.Context, kid string) *JWK {
|
||||||
if kid == "" {
|
if kid == "" {
|
||||||
return m.kids[m.kid]
|
return m.kids[m.kid]
|
||||||
|
@ -83,6 +92,7 @@ func (m *KeyManager) GetByKID(ctx context.Context, kid string) *JWK {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetByHeader returns the JWK a JWT header with the appropriate kid value or returns an error.
|
||||||
func (m *KeyManager) GetByHeader(ctx context.Context, header fjwt.Mapper) (jwk *JWK, err error) {
|
func (m *KeyManager) GetByHeader(ctx context.Context, header fjwt.Mapper) (jwk *JWK, err error) {
|
||||||
var (
|
var (
|
||||||
kid string
|
kid string
|
||||||
|
@ -104,6 +114,7 @@ func (m *KeyManager) GetByHeader(ctx context.Context, header fjwt.Mapper) (jwk *
|
||||||
return jwk, nil
|
return jwk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetByTokenString does an invalidated decode of a token to get the header, then calls GetByHeader.
|
||||||
func (m *KeyManager) GetByTokenString(ctx context.Context, tokenString string) (jwk *JWK, err error) {
|
func (m *KeyManager) GetByTokenString(ctx context.Context, tokenString string) (jwk *JWK, err error) {
|
||||||
var (
|
var (
|
||||||
token *jwt.Token
|
token *jwt.Token
|
||||||
|
@ -116,6 +127,7 @@ func (m *KeyManager) GetByTokenString(ctx context.Context, tokenString string) (
|
||||||
return m.GetByHeader(ctx, &fjwt.Headers{Extra: token.Header})
|
return m.GetByHeader(ctx, &fjwt.Headers{Extra: token.Header})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set returns the *jose.JSONWebKeySet.
|
||||||
func (m *KeyManager) Set(ctx context.Context) *jose.JSONWebKeySet {
|
func (m *KeyManager) Set(ctx context.Context) *jose.JSONWebKeySet {
|
||||||
keys := make([]jose.JSONWebKey, 0, len(m.kids))
|
keys := make([]jose.JSONWebKey, 0, len(m.kids))
|
||||||
|
|
||||||
|
@ -130,6 +142,7 @@ func (m *KeyManager) Set(ctx context.Context) *jose.JSONWebKeySet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate implements the fosite jwt.Signer interface and automatically maps the underlying keys based on the JWK Header kid.
|
||||||
func (m *KeyManager) Generate(ctx context.Context, claims fjwt.MapClaims, header fjwt.Mapper) (tokenString string, sig string, err error) {
|
func (m *KeyManager) Generate(ctx context.Context, claims fjwt.MapClaims, header fjwt.Mapper) (tokenString string, sig string, err error) {
|
||||||
var jwk *JWK
|
var jwk *JWK
|
||||||
|
|
||||||
|
@ -140,6 +153,7 @@ func (m *KeyManager) Generate(ctx context.Context, claims fjwt.MapClaims, header
|
||||||
return jwk.Strategy().Generate(ctx, claims, header)
|
return jwk.Strategy().Generate(ctx, claims, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate implements the fosite jwt.Signer interface and automatically maps the underlying keys based on the JWK Header kid.
|
||||||
func (m *KeyManager) Validate(ctx context.Context, tokenString string) (sig string, err error) {
|
func (m *KeyManager) Validate(ctx context.Context, tokenString string) (sig string, err error) {
|
||||||
var jwk *JWK
|
var jwk *JWK
|
||||||
|
|
||||||
|
@ -150,10 +164,12 @@ func (m *KeyManager) Validate(ctx context.Context, tokenString string) (sig stri
|
||||||
return jwk.Strategy().Validate(ctx, tokenString)
|
return jwk.Strategy().Validate(ctx, tokenString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hash implements the fosite jwt.Signer interface.
|
||||||
func (m *KeyManager) Hash(ctx context.Context, in []byte) (sum []byte, err error) {
|
func (m *KeyManager) Hash(ctx context.Context, in []byte) (sum []byte, err error) {
|
||||||
return m.GetByKID(ctx, "").Strategy().Hash(ctx, in)
|
return m.GetByKID(ctx, "").Strategy().Hash(ctx, in)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decode implements the fosite jwt.Signer interface and automatically maps the underlying keys based on the JWK Header kid.
|
||||||
func (m *KeyManager) Decode(ctx context.Context, tokenString string) (token *fjwt.Token, err error) {
|
func (m *KeyManager) Decode(ctx context.Context, tokenString string) (token *fjwt.Token, err error) {
|
||||||
var jwk *JWK
|
var jwk *JWK
|
||||||
|
|
||||||
|
@ -164,14 +180,58 @@ func (m *KeyManager) Decode(ctx context.Context, tokenString string) (token *fjw
|
||||||
return jwk.Strategy().Decode(ctx, tokenString)
|
return jwk.Strategy().Decode(ctx, tokenString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSignature implements the fosite jwt.Signer interface.
|
||||||
func (m *KeyManager) GetSignature(ctx context.Context, tokenString string) (sig string, err error) {
|
func (m *KeyManager) GetSignature(ctx context.Context, tokenString string) (sig string, err error) {
|
||||||
return getTokenSignature(tokenString)
|
return getTokenSignature(tokenString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSigningMethodLength implements the fosite jwt.Signer interface.
|
||||||
func (m *KeyManager) GetSigningMethodLength(ctx context.Context) (size int) {
|
func (m *KeyManager) GetSigningMethodLength(ctx context.Context) (size int) {
|
||||||
return m.GetByKID(ctx, "").Strategy().GetSigningMethodLength(ctx)
|
return m.GetByKID(ctx, "").Strategy().GetSigningMethodLength(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPublicJSONWebKeySetFromSchemaJWK creates a *jose.JSONWebKeySet from a slice of schema.JWK.
|
||||||
|
func NewPublicJSONWebKeySetFromSchemaJWK(sjwks []schema.JWK) (jwks *jose.JSONWebKeySet) {
|
||||||
|
n := len(sjwks)
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]jose.JSONWebKey, n)
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
jwk := jose.JSONWebKey{
|
||||||
|
KeyID: sjwks[i].KeyID,
|
||||||
|
Algorithm: sjwks[i].Algorithm,
|
||||||
|
Use: sjwks[i].Use,
|
||||||
|
Certificates: sjwks[i].CertificateChain.Certificates(),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch key := sjwks[i].Key.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
jwk.Key = key
|
||||||
|
case rsa.PublicKey:
|
||||||
|
jwk.Key = &key
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
jwk.Key = key.PublicKey
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
jwk.Key = key
|
||||||
|
case ecdsa.PublicKey:
|
||||||
|
jwk.Key = &key
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
jwk.Key = key.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
keys[i] = jwk
|
||||||
|
}
|
||||||
|
|
||||||
|
return &jose.JSONWebKeySet{
|
||||||
|
Keys: keys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewJWK creates a *JWK f rom a schema.JWK.
|
||||||
func NewJWK(s schema.JWK) (jwk *JWK) {
|
func NewJWK(s schema.JWK) (jwk *JWK) {
|
||||||
jwk = &JWK{
|
jwk = &JWK{
|
||||||
kid: s.KeyID,
|
kid: s.KeyID,
|
||||||
|
@ -198,6 +258,7 @@ func NewJWK(s schema.JWK) (jwk *JWK) {
|
||||||
return jwk
|
return jwk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JWK is a representation layer over the *jose.JSONWebKey for convenience.
|
||||||
type JWK struct {
|
type JWK struct {
|
||||||
kid string
|
kid string
|
||||||
use string
|
use string
|
||||||
|
@ -210,16 +271,24 @@ type JWK struct {
|
||||||
thumbprint []byte
|
thumbprint []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSigningMethod returns the jwt.SigningMethod for this *JWK.
|
||||||
|
func (j *JWK) GetSigningMethod() jwt.SigningMethod {
|
||||||
|
return j.alg
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrivateKey returns the Private Key for this *JWK.
|
||||||
func (j *JWK) GetPrivateKey(ctx context.Context) (any, error) {
|
func (j *JWK) GetPrivateKey(ctx context.Context) (any, error) {
|
||||||
return j.PrivateJWK(), nil
|
return j.PrivateJWK(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KeyID returns the Key ID for this *JWK.
|
||||||
func (j *JWK) KeyID() string {
|
func (j *JWK) KeyID() string {
|
||||||
return j.kid
|
return j.kid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JWK) PrivateJWK() (jwk *jose.JSONWebKey) {
|
// DirectJWK directly returns the *JWK as a jose.JSONWebKey with the private key if appropriate.
|
||||||
return &jose.JSONWebKey{
|
func (j *JWK) DirectJWK() (jwk jose.JSONWebKey) {
|
||||||
|
return jose.JSONWebKey{
|
||||||
Key: j.key,
|
Key: j.key,
|
||||||
KeyID: j.kid,
|
KeyID: j.kid,
|
||||||
Algorithm: j.alg.Alg(),
|
Algorithm: j.alg.Alg(),
|
||||||
|
@ -230,10 +299,23 @@ func (j *JWK) PrivateJWK() (jwk *jose.JSONWebKey) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JWK) JWK() (jwk jose.JSONWebKey) {
|
// PrivateJWK directly returns the *JWK as a *jose.JSONWebKey with the private key if appropriate.
|
||||||
return j.PrivateJWK().Public()
|
func (j *JWK) PrivateJWK() (jwk *jose.JSONWebKey) {
|
||||||
|
value := j.DirectJWK()
|
||||||
|
|
||||||
|
return &value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JWK directly returns the *JWK as a jose.JSONWebKey specifically without the private key.
|
||||||
|
func (j *JWK) JWK() (jwk jose.JSONWebKey) {
|
||||||
|
if jwk = j.DirectJWK(); jwk.IsPublic() {
|
||||||
|
return jwk
|
||||||
|
}
|
||||||
|
|
||||||
|
return jwk.Public()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strategy returns the fosite jwt.Signer.
|
||||||
func (j *JWK) Strategy() (strategy fjwt.Signer) {
|
func (j *JWK) Strategy() (strategy fjwt.Signer) {
|
||||||
return &Signer{
|
return &Signer{
|
||||||
hash: j.hash,
|
hash: j.hash,
|
||||||
|
@ -250,6 +332,7 @@ type Signer struct {
|
||||||
GetPrivateKey fjwt.GetPrivateKeyFunc
|
GetPrivateKey fjwt.GetPrivateKeyFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPublicKey returns the PublicKey for this Signer.
|
||||||
func (j *Signer) GetPublicKey(ctx context.Context) (key crypto.PublicKey, err error) {
|
func (j *Signer) GetPublicKey(ctx context.Context) (key crypto.PublicKey, err error) {
|
||||||
var k any
|
var k any
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package oidc
|
package oidc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -12,6 +13,7 @@ import (
|
||||||
"gopkg.in/square/go-jose.v2"
|
"gopkg.in/square/go-jose.v2"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestKeyManager(t *testing.T) {
|
func TestKeyManager(t *testing.T) {
|
||||||
|
@ -19,81 +21,81 @@ func TestKeyManager(t *testing.T) {
|
||||||
Discovery: schema.OpenIDConnectDiscovery{
|
Discovery: schema.OpenIDConnectDiscovery{
|
||||||
DefaultKeyID: "kid-RS256-sig",
|
DefaultKeyID: "kid-RS256-sig",
|
||||||
},
|
},
|
||||||
IssuerJWKS: []schema.JWK{
|
IssuerPrivateKeys: []schema.JWK{
|
||||||
{
|
{
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAUsingSHA256,
|
Algorithm: oidc.SigningAlgRSAUsingSHA256,
|
||||||
Key: keyRSA2048,
|
Key: keyRSA2048,
|
||||||
CertificateChain: certRSA2048,
|
CertificateChain: certRSA2048,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAUsingSHA384,
|
Algorithm: oidc.SigningAlgRSAUsingSHA384,
|
||||||
Key: keyRSA2048,
|
Key: keyRSA2048,
|
||||||
CertificateChain: certRSA2048,
|
CertificateChain: certRSA2048,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAUsingSHA512,
|
Algorithm: oidc.SigningAlgRSAUsingSHA512,
|
||||||
Key: keyRSA4096,
|
Key: keyRSA4096,
|
||||||
CertificateChain: certRSA4096,
|
CertificateChain: certRSA4096,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAPSSUsingSHA256,
|
Algorithm: oidc.SigningAlgRSAPSSUsingSHA256,
|
||||||
Key: keyRSA2048,
|
Key: keyRSA2048,
|
||||||
CertificateChain: certRSA2048,
|
CertificateChain: certRSA2048,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAPSSUsingSHA384,
|
Algorithm: oidc.SigningAlgRSAPSSUsingSHA384,
|
||||||
Key: keyRSA2048,
|
Key: keyRSA2048,
|
||||||
CertificateChain: certRSA2048,
|
CertificateChain: certRSA2048,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAPSSUsingSHA512,
|
Algorithm: oidc.SigningAlgRSAPSSUsingSHA512,
|
||||||
Key: keyRSA4096,
|
Key: keyRSA4096,
|
||||||
CertificateChain: certRSA4096,
|
CertificateChain: certRSA4096,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgECDSAUsingP256AndSHA256,
|
Algorithm: oidc.SigningAlgECDSAUsingP256AndSHA256,
|
||||||
Key: keyECDSAP256,
|
Key: keyECDSAP256,
|
||||||
CertificateChain: certECDSAP256,
|
CertificateChain: certECDSAP256,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgECDSAUsingP384AndSHA384,
|
Algorithm: oidc.SigningAlgECDSAUsingP384AndSHA384,
|
||||||
Key: keyECDSAP384,
|
Key: keyECDSAP384,
|
||||||
CertificateChain: certECDSAP384,
|
CertificateChain: certECDSAP384,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgECDSAUsingP521AndSHA512,
|
Algorithm: oidc.SigningAlgECDSAUsingP521AndSHA512,
|
||||||
Key: keyECDSAP521,
|
Key: keyECDSAP521,
|
||||||
CertificateChain: certECDSAP521,
|
CertificateChain: certECDSAP521,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, key := range config.IssuerJWKS {
|
for i, key := range config.IssuerPrivateKeys {
|
||||||
config.IssuerJWKS[i].KeyID = fmt.Sprintf("kid-%s-%s", key.Algorithm, key.Use)
|
config.IssuerPrivateKeys[i].KeyID = fmt.Sprintf("kid-%s-%s", key.Algorithm, key.Use)
|
||||||
}
|
}
|
||||||
|
|
||||||
manager := NewKeyManager(config)
|
manager := oidc.NewKeyManager(config)
|
||||||
|
|
||||||
assert.NotNil(t, manager)
|
assert.NotNil(t, manager)
|
||||||
|
|
||||||
assert.Len(t, manager.kids, len(config.IssuerJWKS))
|
|
||||||
assert.Len(t, manager.algs, len(config.IssuerJWKS))
|
|
||||||
|
|
||||||
assert.Equal(t, "kid-RS256-sig", manager.kid)
|
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
assert.Equal(t, "kid-RS256-sig", manager.GetKeyID(ctx))
|
||||||
|
|
||||||
var (
|
var (
|
||||||
jwk *JWK
|
jwk *oidc.JWK
|
||||||
|
tokenString, sig string
|
||||||
|
sum []byte
|
||||||
|
token *fjwt.Token
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -107,7 +109,7 @@ func TestKeyManager(t *testing.T) {
|
||||||
assert.NotNil(t, jwk)
|
assert.NotNil(t, jwk)
|
||||||
assert.Equal(t, config.Discovery.DefaultKeyID, jwk.KeyID())
|
assert.Equal(t, config.Discovery.DefaultKeyID, jwk.KeyID())
|
||||||
|
|
||||||
jwk, err = manager.GetByHeader(ctx, &fjwt.Headers{Extra: map[string]any{JWTHeaderKeyIdentifier: "notalg"}})
|
jwk, err = manager.GetByHeader(ctx, &fjwt.Headers{Extra: map[string]any{oidc.JWTHeaderKeyIdentifier: "notalg"}})
|
||||||
assert.EqualError(t, err, "jwt header 'kid' with value 'notalg' does not match a managed jwk")
|
assert.EqualError(t, err, "jwt header 'kid' with value 'notalg' does not match a managed jwk")
|
||||||
assert.Nil(t, jwk)
|
assert.Nil(t, jwk)
|
||||||
|
|
||||||
|
@ -119,17 +121,17 @@ func TestKeyManager(t *testing.T) {
|
||||||
assert.EqualError(t, err, "jwt header was nil")
|
assert.EqualError(t, err, "jwt header was nil")
|
||||||
assert.Nil(t, jwk)
|
assert.Nil(t, jwk)
|
||||||
|
|
||||||
kid, err := manager.GetKIDFromAlgStrict(ctx, "notalg")
|
kid, err := manager.GetKeyIDFromAlgStrict(ctx, "notalg")
|
||||||
assert.EqualError(t, err, "alg not found")
|
assert.EqualError(t, err, "alg not found")
|
||||||
assert.Equal(t, "", kid)
|
assert.Equal(t, "", kid)
|
||||||
|
|
||||||
kid = manager.GetKIDFromAlg(ctx, "notalg")
|
kid = manager.GetKeyIDFromAlg(ctx, "notalg")
|
||||||
assert.Equal(t, config.Discovery.DefaultKeyID, kid)
|
assert.Equal(t, config.Discovery.DefaultKeyID, kid)
|
||||||
|
|
||||||
set := manager.Set(ctx)
|
set := manager.Set(ctx)
|
||||||
|
|
||||||
assert.NotNil(t, set)
|
assert.NotNil(t, set)
|
||||||
assert.Len(t, set.Keys, len(config.IssuerJWKS))
|
assert.Len(t, set.Keys, len(config.IssuerPrivateKeys))
|
||||||
|
|
||||||
data, err := json.Marshal(&set)
|
data, err := json.Marshal(&set)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -139,9 +141,32 @@ func TestKeyManager(t *testing.T) {
|
||||||
assert.NoError(t, json.Unmarshal(data, &out))
|
assert.NoError(t, json.Unmarshal(data, &out))
|
||||||
assert.Equal(t, *set, out)
|
assert.Equal(t, *set, out)
|
||||||
|
|
||||||
for _, alg := range []string{SigningAlgRSAUsingSHA256, SigningAlgRSAUsingSHA384, SigningAlgRSAPSSUsingSHA512, SigningAlgRSAPSSUsingSHA256, SigningAlgRSAPSSUsingSHA384, SigningAlgRSAPSSUsingSHA512, SigningAlgECDSAUsingP256AndSHA256, SigningAlgECDSAUsingP384AndSHA384, SigningAlgECDSAUsingP521AndSHA512} {
|
jwk, err = manager.GetByTokenString(ctx, badTokenString)
|
||||||
|
assert.EqualError(t, err, "token contains an invalid number of segments")
|
||||||
|
assert.Nil(t, jwk)
|
||||||
|
|
||||||
|
tokenString, sig, err = manager.Generate(ctx, nil, nil)
|
||||||
|
assert.EqualError(t, err, "error getting jwk from header: jwt header was nil")
|
||||||
|
assert.Equal(t, "", tokenString)
|
||||||
|
assert.Equal(t, "", sig)
|
||||||
|
|
||||||
|
sig, err = manager.Validate(ctx, badTokenString)
|
||||||
|
assert.EqualError(t, err, "error getting jwk from token string: token contains an invalid number of segments")
|
||||||
|
assert.Equal(t, "", sig)
|
||||||
|
|
||||||
|
token, err = manager.Decode(ctx, badTokenString)
|
||||||
|
assert.EqualError(t, err, "error getting jwk from token string: token contains an invalid number of segments")
|
||||||
|
assert.Nil(t, token)
|
||||||
|
|
||||||
|
sum, err = manager.Hash(ctx, []byte("abc"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", fmt.Sprintf("%x", sum))
|
||||||
|
|
||||||
|
assert.Equal(t, crypto.SHA256.Size(), manager.GetSigningMethodLength(ctx))
|
||||||
|
|
||||||
|
for _, alg := range []string{oidc.SigningAlgRSAUsingSHA256, oidc.SigningAlgRSAUsingSHA384, oidc.SigningAlgRSAPSSUsingSHA512, oidc.SigningAlgRSAPSSUsingSHA256, oidc.SigningAlgRSAPSSUsingSHA384, oidc.SigningAlgRSAPSSUsingSHA512, oidc.SigningAlgECDSAUsingP256AndSHA256, oidc.SigningAlgECDSAUsingP384AndSHA384, oidc.SigningAlgECDSAUsingP521AndSHA512} {
|
||||||
t.Run(alg, func(t *testing.T) {
|
t.Run(alg, func(t *testing.T) {
|
||||||
expectedKID := fmt.Sprintf("kid-%s-%s", alg, KeyUseSignature)
|
expectedKID := fmt.Sprintf("kid-%s-%s", alg, oidc.KeyUseSignature)
|
||||||
|
|
||||||
t.Run("ShouldGetCorrectKey", func(t *testing.T) {
|
t.Run("ShouldGetCorrectKey", func(t *testing.T) {
|
||||||
jwk = manager.GetByKID(ctx, expectedKID)
|
jwk = manager.GetByKID(ctx, expectedKID)
|
||||||
|
@ -151,17 +176,17 @@ func TestKeyManager(t *testing.T) {
|
||||||
jwk = manager.GetByAlg(ctx, alg)
|
jwk = manager.GetByAlg(ctx, alg)
|
||||||
assert.NotNil(t, jwk)
|
assert.NotNil(t, jwk)
|
||||||
|
|
||||||
assert.Equal(t, alg, jwk.alg.Alg())
|
assert.Equal(t, alg, jwk.GetSigningMethod().Alg())
|
||||||
assert.Equal(t, expectedKID, jwk.KeyID())
|
assert.Equal(t, expectedKID, jwk.KeyID())
|
||||||
|
|
||||||
kid, err = manager.GetKIDFromAlgStrict(ctx, alg)
|
kid, err = manager.GetKeyIDFromAlgStrict(ctx, alg)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expectedKID, kid)
|
assert.Equal(t, expectedKID, kid)
|
||||||
|
|
||||||
kid = manager.GetKIDFromAlg(ctx, alg)
|
kid = manager.GetKeyIDFromAlg(ctx, alg)
|
||||||
assert.Equal(t, expectedKID, kid)
|
assert.Equal(t, expectedKID, kid)
|
||||||
|
|
||||||
jwk, err = manager.GetByHeader(ctx, &fjwt.Headers{Extra: map[string]any{JWTHeaderKeyIdentifier: expectedKID}})
|
jwk, err = manager.GetByHeader(ctx, &fjwt.Headers{Extra: map[string]any{oidc.JWTHeaderKeyIdentifier: expectedKID}})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, jwk)
|
assert.NotNil(t, jwk)
|
||||||
|
|
||||||
|
@ -169,10 +194,9 @@ func TestKeyManager(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ShouldUseCorrectSigner", func(t *testing.T) {
|
t.Run("ShouldUseCorrectSigner", func(t *testing.T) {
|
||||||
var tokenString, sig, sigb string
|
var sigb string
|
||||||
var token *fjwt.Token
|
|
||||||
|
|
||||||
tokenString, sig, err = manager.Generate(ctx, fjwt.MapClaims{}, &fjwt.Headers{Extra: map[string]any{JWTHeaderKeyIdentifier: expectedKID}})
|
tokenString, sig, err = manager.Generate(ctx, fjwt.MapClaims{}, &fjwt.Headers{Extra: map[string]any{oidc.JWTHeaderKeyIdentifier: expectedKID}})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sigb, err = manager.GetSignature(ctx, tokenString)
|
sigb, err = manager.GetSignature(ctx, tokenString)
|
||||||
|
@ -185,10 +209,9 @@ func TestKeyManager(t *testing.T) {
|
||||||
|
|
||||||
token, err = manager.Decode(ctx, tokenString)
|
token, err = manager.Decode(ctx, tokenString)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expectedKID, token.Header[JWTHeaderKeyIdentifier])
|
assert.Equal(t, expectedKID, token.Header[oidc.JWTHeaderKeyIdentifier])
|
||||||
|
|
||||||
jwk, err = manager.GetByTokenString(ctx, tokenString)
|
jwk, err = manager.GetByTokenString(ctx, tokenString)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sigb, err = jwk.Strategy().Validate(ctx, tokenString)
|
sigb, err = jwk.Strategy().Validate(ctx, tokenString)
|
||||||
|
@ -206,8 +229,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa2048-rs256",
|
KeyID: "rsa2048-rs256",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAUsingSHA256,
|
Algorithm: oidc.SigningAlgRSAUsingSHA256,
|
||||||
Key: keyRSA2048,
|
Key: keyRSA2048,
|
||||||
CertificateChain: certRSA2048,
|
CertificateChain: certRSA2048,
|
||||||
},
|
},
|
||||||
|
@ -215,8 +238,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa2048-rs384",
|
KeyID: "rsa2048-rs384",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAUsingSHA384,
|
Algorithm: oidc.SigningAlgRSAUsingSHA384,
|
||||||
Key: keyRSA2048,
|
Key: keyRSA2048,
|
||||||
CertificateChain: certRSA2048,
|
CertificateChain: certRSA2048,
|
||||||
},
|
},
|
||||||
|
@ -224,8 +247,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa2048-rs512",
|
KeyID: "rsa2048-rs512",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAUsingSHA512,
|
Algorithm: oidc.SigningAlgRSAUsingSHA512,
|
||||||
Key: keyRSA2048,
|
Key: keyRSA2048,
|
||||||
CertificateChain: certRSA2048,
|
CertificateChain: certRSA2048,
|
||||||
},
|
},
|
||||||
|
@ -233,8 +256,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa4096-rs256",
|
KeyID: "rsa4096-rs256",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAUsingSHA256,
|
Algorithm: oidc.SigningAlgRSAUsingSHA256,
|
||||||
Key: keyRSA4096,
|
Key: keyRSA4096,
|
||||||
CertificateChain: certRSA4096,
|
CertificateChain: certRSA4096,
|
||||||
},
|
},
|
||||||
|
@ -242,8 +265,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa4096-rs384",
|
KeyID: "rsa4096-rs384",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAUsingSHA384,
|
Algorithm: oidc.SigningAlgRSAUsingSHA384,
|
||||||
Key: keyRSA4096,
|
Key: keyRSA4096,
|
||||||
CertificateChain: certRSA4096,
|
CertificateChain: certRSA4096,
|
||||||
},
|
},
|
||||||
|
@ -251,8 +274,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa4096-rs512",
|
KeyID: "rsa4096-rs512",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAUsingSHA512,
|
Algorithm: oidc.SigningAlgRSAUsingSHA512,
|
||||||
Key: keyRSA4096,
|
Key: keyRSA4096,
|
||||||
CertificateChain: certRSA4096,
|
CertificateChain: certRSA4096,
|
||||||
},
|
},
|
||||||
|
@ -260,8 +283,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa2048-rs256",
|
KeyID: "rsa2048-rs256",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAPSSUsingSHA256,
|
Algorithm: oidc.SigningAlgRSAPSSUsingSHA256,
|
||||||
Key: keyRSA2048,
|
Key: keyRSA2048,
|
||||||
CertificateChain: certRSA2048,
|
CertificateChain: certRSA2048,
|
||||||
},
|
},
|
||||||
|
@ -269,8 +292,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa2048-ps384",
|
KeyID: "rsa2048-ps384",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAPSSUsingSHA384,
|
Algorithm: oidc.SigningAlgRSAPSSUsingSHA384,
|
||||||
Key: keyRSA2048,
|
Key: keyRSA2048,
|
||||||
CertificateChain: certRSA2048,
|
CertificateChain: certRSA2048,
|
||||||
},
|
},
|
||||||
|
@ -278,8 +301,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa2048-ps512",
|
KeyID: "rsa2048-ps512",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAPSSUsingSHA512,
|
Algorithm: oidc.SigningAlgRSAPSSUsingSHA512,
|
||||||
Key: keyRSA2048,
|
Key: keyRSA2048,
|
||||||
CertificateChain: certRSA2048,
|
CertificateChain: certRSA2048,
|
||||||
},
|
},
|
||||||
|
@ -287,8 +310,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa4096-ps256",
|
KeyID: "rsa4096-ps256",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAPSSUsingSHA256,
|
Algorithm: oidc.SigningAlgRSAPSSUsingSHA256,
|
||||||
Key: keyRSA4096,
|
Key: keyRSA4096,
|
||||||
CertificateChain: certRSA4096,
|
CertificateChain: certRSA4096,
|
||||||
},
|
},
|
||||||
|
@ -296,8 +319,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa4096-ps384",
|
KeyID: "rsa4096-ps384",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAPSSUsingSHA384,
|
Algorithm: oidc.SigningAlgRSAPSSUsingSHA384,
|
||||||
Key: keyRSA4096,
|
Key: keyRSA4096,
|
||||||
CertificateChain: certRSA4096,
|
CertificateChain: certRSA4096,
|
||||||
},
|
},
|
||||||
|
@ -305,8 +328,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "rsa4096-ps512",
|
KeyID: "rsa4096-ps512",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgRSAPSSUsingSHA512,
|
Algorithm: oidc.SigningAlgRSAPSSUsingSHA512,
|
||||||
Key: keyRSA4096,
|
Key: keyRSA4096,
|
||||||
CertificateChain: certRSA4096,
|
CertificateChain: certRSA4096,
|
||||||
},
|
},
|
||||||
|
@ -314,8 +337,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "ecdsaP256",
|
KeyID: "ecdsaP256",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgECDSAUsingP256AndSHA256,
|
Algorithm: oidc.SigningAlgECDSAUsingP256AndSHA256,
|
||||||
Key: keyECDSAP256,
|
Key: keyECDSAP256,
|
||||||
CertificateChain: certECDSAP256,
|
CertificateChain: certECDSAP256,
|
||||||
},
|
},
|
||||||
|
@ -323,8 +346,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "ecdsaP384",
|
KeyID: "ecdsaP384",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgECDSAUsingP384AndSHA384,
|
Algorithm: oidc.SigningAlgECDSAUsingP384AndSHA384,
|
||||||
Key: keyECDSAP384,
|
Key: keyECDSAP384,
|
||||||
CertificateChain: certECDSAP384,
|
CertificateChain: certECDSAP384,
|
||||||
},
|
},
|
||||||
|
@ -332,8 +355,8 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
{
|
{
|
||||||
schema.JWK{
|
schema.JWK{
|
||||||
KeyID: "ecdsaP521",
|
KeyID: "ecdsaP521",
|
||||||
Use: KeyUseSignature,
|
Use: oidc.KeyUseSignature,
|
||||||
Algorithm: SigningAlgECDSAUsingP521AndSHA512,
|
Algorithm: oidc.SigningAlgECDSAUsingP521AndSHA512,
|
||||||
Key: keyECDSAP521,
|
Key: keyECDSAP521,
|
||||||
CertificateChain: certECDSAP521,
|
CertificateChain: certECDSAP521,
|
||||||
},
|
},
|
||||||
|
@ -344,24 +367,28 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
t.Run(tc.have.KeyID, func(t *testing.T) {
|
t.Run(tc.have.KeyID, func(t *testing.T) {
|
||||||
t.Run("Generating", func(t *testing.T) {
|
t.Run("Generating", func(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
jwk *JWK
|
jwk *oidc.JWK
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
jwk = NewJWK(tc.have)
|
jwk = oidc.NewJWK(tc.have)
|
||||||
|
|
||||||
signer := jwk.Strategy()
|
signer := jwk.Strategy()
|
||||||
|
|
||||||
claims := fjwt.MapClaims{}
|
claims := fjwt.MapClaims{}
|
||||||
header := &fjwt.Headers{
|
header := &fjwt.Headers{
|
||||||
Extra: map[string]any{
|
Extra: map[string]any{
|
||||||
"kid": jwk.kid,
|
oidc.JWTHeaderKeyIdentifier: jwk.KeyID(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenString, sig, err := signer.Generate(ctx, claims, header)
|
tokenString, sig, err := signer.Generate(ctx, nil, nil)
|
||||||
|
assert.EqualError(t, err, "either claims or header is nil")
|
||||||
|
assert.Equal(t, "", tokenString)
|
||||||
|
assert.Equal(t, "", sig)
|
||||||
|
|
||||||
|
tokenString, sig, err = signer.Generate(ctx, claims, header)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEqual(t, "", tokenString)
|
assert.NotEqual(t, "", tokenString)
|
||||||
assert.NotEqual(t, "", sig)
|
assert.NotEqual(t, "", sig)
|
||||||
|
@ -376,7 +403,7 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
fmt.Println(tokenString)
|
fmt.Println(tokenString)
|
||||||
|
|
||||||
assert.True(t, token.Valid())
|
assert.True(t, token.Valid())
|
||||||
assert.Equal(t, jwk.alg.Alg(), string(token.Method))
|
assert.Equal(t, jwk.GetSigningMethod().Alg(), string(token.Method))
|
||||||
|
|
||||||
sigv, err := signer.Validate(ctx, tokenString)
|
sigv, err := signer.Validate(ctx, tokenString)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -385,19 +412,19 @@ func TestJWKFunctionality(t *testing.T) {
|
||||||
|
|
||||||
t.Run("Marshalling", func(t *testing.T) {
|
t.Run("Marshalling", func(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
jwk *JWK
|
jwk *oidc.JWK
|
||||||
out jose.JSONWebKey
|
out jose.JSONWebKey
|
||||||
data []byte
|
data []byte
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
jwk = NewJWK(tc.have)
|
jwk = oidc.NewJWK(tc.have)
|
||||||
|
|
||||||
strategy := jwk.Strategy()
|
strategy := jwk.Strategy()
|
||||||
|
|
||||||
assert.NotNil(t, strategy)
|
assert.NotNil(t, strategy)
|
||||||
|
|
||||||
signer, ok := strategy.(*Signer)
|
signer, ok := strategy.(*oidc.Signer)
|
||||||
|
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package oidc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateToken(t *testing.T) {
|
||||||
|
sig, err := validateToken("none", nil)
|
||||||
|
assert.Equal(t, "", sig)
|
||||||
|
assert.EqualError(t, err, "square/go-jose: compact JWS format must have three parts")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetTokenSignature(t *testing.T) {
|
||||||
|
sig, err := getTokenSignature("abc.123")
|
||||||
|
assert.Equal(t, "", sig)
|
||||||
|
assert.EqualError(t, err, "header, body and signature must all be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssign(t *testing.T) {
|
||||||
|
a := map[string]any{
|
||||||
|
"a": "valuea",
|
||||||
|
"c": "valuea",
|
||||||
|
}
|
||||||
|
|
||||||
|
b := map[string]any{
|
||||||
|
"b": "valueb",
|
||||||
|
"c": "valueb",
|
||||||
|
}
|
||||||
|
|
||||||
|
c := assign(a, b)
|
||||||
|
|
||||||
|
assert.Equal(t, "valuea", c["a"])
|
||||||
|
assert.Equal(t, "valueb", c["b"])
|
||||||
|
assert.Equal(t, "valuea", c["c"])
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package oidc
|
package oidc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -9,16 +8,17 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_NotConfigured(t *testing.T) {
|
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_NotConfigured(t *testing.T) {
|
||||||
provider := NewOpenIDConnectProvider(nil, nil, nil)
|
provider := oidc.NewOpenIDConnectProvider(nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, provider)
|
assert.Nil(t, provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing.T) {
|
func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing.T) {
|
||||||
provider := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
provider := oidc.NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
IssuerPrivateKey: keyRSA2048,
|
IssuerPrivateKey: keyRSA2048,
|
||||||
EnablePKCEPlainChallenge: true,
|
EnablePKCEPlainChallenge: true,
|
||||||
|
@ -26,7 +26,7 @@ func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: myclient,
|
ID: myclient,
|
||||||
Secret: MustDecodeSecret(badsecret),
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
SectorIdentifier: url.URL{Host: examplecomsid},
|
SectorIdentifier: url.URL{Host: examplecomsid},
|
||||||
Policy: onefactor,
|
Policy: onefactor,
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -41,23 +41,23 @@ func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing
|
||||||
disco := provider.GetOpenIDConnectWellKnownConfiguration(examplecom)
|
disco := provider.GetOpenIDConnectWellKnownConfiguration(examplecom)
|
||||||
|
|
||||||
assert.Len(t, disco.SubjectTypesSupported, 2)
|
assert.Len(t, disco.SubjectTypesSupported, 2)
|
||||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
assert.Contains(t, disco.SubjectTypesSupported, oidc.SubjectTypePublic)
|
||||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePairwise)
|
assert.Contains(t, disco.SubjectTypesSupported, oidc.SubjectTypePairwise)
|
||||||
|
|
||||||
assert.Len(t, disco.CodeChallengeMethodsSupported, 2)
|
assert.Len(t, disco.CodeChallengeMethodsSupported, 2)
|
||||||
assert.Contains(t, disco.CodeChallengeMethodsSupported, PKCEChallengeMethodSHA256)
|
assert.Contains(t, disco.CodeChallengeMethodsSupported, oidc.PKCEChallengeMethodSHA256)
|
||||||
assert.Contains(t, disco.CodeChallengeMethodsSupported, PKCEChallengeMethodSHA256)
|
assert.Contains(t, disco.CodeChallengeMethodsSupported, oidc.PKCEChallengeMethodSHA256)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *testing.T) {
|
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *testing.T) {
|
||||||
provider := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
provider := oidc.NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
IssuerPrivateKey: keyRSA2048,
|
IssuerPrivateKey: keyRSA2048,
|
||||||
HMACSecret: badhmac,
|
HMACSecret: badhmac,
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
Policy: onefactor,
|
Policy: onefactor,
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
|
@ -66,16 +66,16 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *tes
|
||||||
{
|
{
|
||||||
ID: "b-client",
|
ID: "b-client",
|
||||||
Description: "Normal Description",
|
Description: "Normal Description",
|
||||||
Secret: MustDecodeSecret("$plaintext$b-client-secret"),
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
Policy: twofactor,
|
Policy: twofactor,
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
},
|
},
|
||||||
Scopes: []string{
|
Scopes: []string{
|
||||||
ScopeGroups,
|
oidc.ScopeGroups,
|
||||||
},
|
},
|
||||||
GrantTypes: []string{
|
GrantTypes: []string{
|
||||||
GrantTypeRefreshToken,
|
oidc.GrantTypeRefreshToken,
|
||||||
},
|
},
|
||||||
ResponseTypes: []string{
|
ResponseTypes: []string{
|
||||||
"token",
|
"token",
|
||||||
|
@ -87,285 +87,3 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *tes
|
||||||
|
|
||||||
assert.NotNil(t, provider)
|
assert.NotNil(t, provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
|
||||||
provider := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
|
||||||
IssuerPrivateKey: keyRSA2048,
|
|
||||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
|
||||||
{
|
|
||||||
ID: "a-client",
|
|
||||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
|
||||||
Policy: onefactor,
|
|
||||||
RedirectURIs: []string{
|
|
||||||
"https://google.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil, nil)
|
|
||||||
|
|
||||||
require.NotNil(t, provider)
|
|
||||||
|
|
||||||
disco := provider.GetOpenIDConnectWellKnownConfiguration(examplecom)
|
|
||||||
|
|
||||||
assert.Equal(t, examplecom, disco.Issuer)
|
|
||||||
assert.Equal(t, "https://example.com/jwks.json", disco.JWKSURI)
|
|
||||||
assert.Equal(t, "https://example.com/api/oidc/authorization", disco.AuthorizationEndpoint)
|
|
||||||
assert.Equal(t, "https://example.com/api/oidc/token", disco.TokenEndpoint)
|
|
||||||
assert.Equal(t, "https://example.com/api/oidc/userinfo", disco.UserinfoEndpoint)
|
|
||||||
assert.Equal(t, "https://example.com/api/oidc/introspection", disco.IntrospectionEndpoint)
|
|
||||||
assert.Equal(t, "https://example.com/api/oidc/revocation", disco.RevocationEndpoint)
|
|
||||||
assert.Equal(t, "", disco.RegistrationEndpoint)
|
|
||||||
|
|
||||||
assert.Len(t, disco.CodeChallengeMethodsSupported, 1)
|
|
||||||
assert.Contains(t, disco.CodeChallengeMethodsSupported, PKCEChallengeMethodSHA256)
|
|
||||||
|
|
||||||
assert.Len(t, disco.ScopesSupported, 5)
|
|
||||||
assert.Contains(t, disco.ScopesSupported, ScopeOpenID)
|
|
||||||
assert.Contains(t, disco.ScopesSupported, ScopeOfflineAccess)
|
|
||||||
assert.Contains(t, disco.ScopesSupported, ScopeProfile)
|
|
||||||
assert.Contains(t, disco.ScopesSupported, ScopeGroups)
|
|
||||||
assert.Contains(t, disco.ScopesSupported, ScopeEmail)
|
|
||||||
|
|
||||||
assert.Len(t, disco.ResponseModesSupported, 3)
|
|
||||||
assert.Contains(t, disco.ResponseModesSupported, ResponseModeFormPost)
|
|
||||||
assert.Contains(t, disco.ResponseModesSupported, ResponseModeQuery)
|
|
||||||
assert.Contains(t, disco.ResponseModesSupported, ResponseModeFragment)
|
|
||||||
|
|
||||||
assert.Len(t, disco.SubjectTypesSupported, 2)
|
|
||||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
|
||||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePairwise)
|
|
||||||
|
|
||||||
assert.Len(t, disco.ResponseTypesSupported, 7)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowIDToken)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowToken)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowBoth)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowIDToken)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth)
|
|
||||||
|
|
||||||
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 4)
|
|
||||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
|
|
||||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost)
|
|
||||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretJWT)
|
|
||||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone)
|
|
||||||
|
|
||||||
assert.Len(t, disco.RevocationEndpointAuthMethodsSupported, 4)
|
|
||||||
assert.Contains(t, disco.RevocationEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
|
|
||||||
assert.Contains(t, disco.RevocationEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost)
|
|
||||||
assert.Contains(t, disco.RevocationEndpointAuthMethodsSupported, ClientAuthMethodClientSecretJWT)
|
|
||||||
assert.Contains(t, disco.RevocationEndpointAuthMethodsSupported, ClientAuthMethodNone)
|
|
||||||
|
|
||||||
assert.Len(t, disco.IntrospectionEndpointAuthMethodsSupported, 2)
|
|
||||||
assert.Contains(t, disco.IntrospectionEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
|
|
||||||
assert.Contains(t, disco.IntrospectionEndpointAuthMethodsSupported, 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.RevocationEndpointAuthSigningAlgValuesSupported, 3)
|
|
||||||
assert.Equal(t, disco.RevocationEndpointAuthSigningAlgValuesSupported[0], SigningAlgHMACUsingSHA256)
|
|
||||||
assert.Equal(t, disco.RevocationEndpointAuthSigningAlgValuesSupported[1], SigningAlgHMACUsingSHA384)
|
|
||||||
assert.Equal(t, disco.RevocationEndpointAuthSigningAlgValuesSupported[2], SigningAlgHMACUsingSHA512)
|
|
||||||
|
|
||||||
assert.Len(t, disco.TokenEndpointAuthSigningAlgValuesSupported, 3)
|
|
||||||
assert.Equal(t, disco.TokenEndpointAuthSigningAlgValuesSupported[0], SigningAlgHMACUsingSHA256)
|
|
||||||
assert.Equal(t, disco.TokenEndpointAuthSigningAlgValuesSupported[1], SigningAlgHMACUsingSHA384)
|
|
||||||
assert.Equal(t, disco.TokenEndpointAuthSigningAlgValuesSupported[2], SigningAlgHMACUsingSHA512)
|
|
||||||
|
|
||||||
assert.Len(t, disco.IDTokenSigningAlgValuesSupported, 1)
|
|
||||||
assert.Contains(t, disco.IDTokenSigningAlgValuesSupported, SigningAlgRSAUsingSHA256)
|
|
||||||
|
|
||||||
assert.Len(t, disco.UserinfoSigningAlgValuesSupported, 2)
|
|
||||||
assert.Equal(t, disco.UserinfoSigningAlgValuesSupported[0], SigningAlgRSAUsingSHA256)
|
|
||||||
assert.Equal(t, disco.UserinfoSigningAlgValuesSupported[1], SigningAlgNone)
|
|
||||||
|
|
||||||
require.Len(t, disco.RequestObjectSigningAlgValuesSupported, 2)
|
|
||||||
assert.Equal(t, SigningAlgRSAUsingSHA256, disco.RequestObjectSigningAlgValuesSupported[0])
|
|
||||||
assert.Equal(t, SigningAlgNone, disco.RequestObjectSigningAlgValuesSupported[1])
|
|
||||||
|
|
||||||
assert.Len(t, disco.ClaimsSupported, 18)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimAuthenticationMethodsReference)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimAudience)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimAuthorizedParty)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimClientIdentifier)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimExpirationTime)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimIssuedAt)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimIssuer)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimJWTID)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimRequestedAt)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimSubject)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimAuthenticationTime)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimNonce)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimPreferredEmail)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimEmailVerified)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimEmailAlts)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimGroups)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimPreferredUsername)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimFullName)
|
|
||||||
|
|
||||||
assert.Len(t, disco.PromptValuesSupported, 2)
|
|
||||||
assert.Contains(t, disco.PromptValuesSupported, PromptConsent)
|
|
||||||
assert.Contains(t, disco.PromptValuesSupported, PromptNone)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfiguration(t *testing.T) {
|
|
||||||
provider := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
|
||||||
IssuerPrivateKey: keyRSA2048,
|
|
||||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
|
||||||
{
|
|
||||||
ID: "a-client",
|
|
||||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
|
||||||
Policy: onefactor,
|
|
||||||
RedirectURIs: []string{
|
|
||||||
"https://google.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil, nil)
|
|
||||||
|
|
||||||
require.NotNil(t, provider)
|
|
||||||
|
|
||||||
disco := provider.GetOAuth2WellKnownConfiguration(examplecom)
|
|
||||||
|
|
||||||
assert.Equal(t, examplecom, disco.Issuer)
|
|
||||||
assert.Equal(t, "https://example.com/jwks.json", disco.JWKSURI)
|
|
||||||
assert.Equal(t, "https://example.com/api/oidc/authorization", disco.AuthorizationEndpoint)
|
|
||||||
assert.Equal(t, "https://example.com/api/oidc/token", disco.TokenEndpoint)
|
|
||||||
assert.Equal(t, "https://example.com/api/oidc/introspection", disco.IntrospectionEndpoint)
|
|
||||||
assert.Equal(t, "https://example.com/api/oidc/revocation", disco.RevocationEndpoint)
|
|
||||||
assert.Equal(t, "", disco.RegistrationEndpoint)
|
|
||||||
|
|
||||||
require.Len(t, disco.CodeChallengeMethodsSupported, 1)
|
|
||||||
assert.Equal(t, "S256", disco.CodeChallengeMethodsSupported[0])
|
|
||||||
|
|
||||||
assert.Len(t, disco.ScopesSupported, 5)
|
|
||||||
assert.Contains(t, disco.ScopesSupported, ScopeOpenID)
|
|
||||||
assert.Contains(t, disco.ScopesSupported, ScopeOfflineAccess)
|
|
||||||
assert.Contains(t, disco.ScopesSupported, ScopeProfile)
|
|
||||||
assert.Contains(t, disco.ScopesSupported, ScopeGroups)
|
|
||||||
assert.Contains(t, disco.ScopesSupported, ScopeEmail)
|
|
||||||
|
|
||||||
assert.Len(t, disco.ResponseModesSupported, 3)
|
|
||||||
assert.Contains(t, disco.ResponseModesSupported, ResponseModeFormPost)
|
|
||||||
assert.Contains(t, disco.ResponseModesSupported, ResponseModeQuery)
|
|
||||||
assert.Contains(t, disco.ResponseModesSupported, ResponseModeFragment)
|
|
||||||
|
|
||||||
assert.Len(t, disco.SubjectTypesSupported, 2)
|
|
||||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePublic)
|
|
||||||
assert.Contains(t, disco.SubjectTypesSupported, SubjectTypePairwise)
|
|
||||||
|
|
||||||
assert.Len(t, disco.ResponseTypesSupported, 7)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeAuthorizationCodeFlow)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowIDToken)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowToken)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeImplicitFlowBoth)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowIDToken)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowToken)
|
|
||||||
assert.Contains(t, disco.ResponseTypesSupported, ResponseTypeHybridFlowBoth)
|
|
||||||
|
|
||||||
assert.Len(t, disco.TokenEndpointAuthMethodsSupported, 4)
|
|
||||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretBasic)
|
|
||||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretPost)
|
|
||||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodClientSecretJWT)
|
|
||||||
assert.Contains(t, disco.TokenEndpointAuthMethodsSupported, ClientAuthMethodNone)
|
|
||||||
|
|
||||||
assert.Len(t, disco.GrantTypesSupported, 3)
|
|
||||||
assert.Contains(t, disco.GrantTypesSupported, GrantTypeAuthorizationCode)
|
|
||||||
assert.Contains(t, disco.GrantTypesSupported, GrantTypeRefreshToken)
|
|
||||||
assert.Contains(t, disco.GrantTypesSupported, GrantTypeImplicit)
|
|
||||||
|
|
||||||
assert.Len(t, disco.ClaimsSupported, 18)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimAuthenticationMethodsReference)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimAudience)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimAuthorizedParty)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimClientIdentifier)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimExpirationTime)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimIssuedAt)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimIssuer)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimJWTID)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimRequestedAt)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimSubject)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimAuthenticationTime)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimNonce)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimPreferredEmail)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimEmailVerified)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimEmailAlts)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimGroups)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimPreferredUsername)
|
|
||||||
assert.Contains(t, disco.ClaimsSupported, ClaimFullName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnownConfigurationWithPlainPKCE(t *testing.T) {
|
|
||||||
provider := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
|
||||||
IssuerPrivateKey: keyRSA2048,
|
|
||||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
|
||||||
EnablePKCEPlainChallenge: true,
|
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
|
||||||
{
|
|
||||||
ID: "a-client",
|
|
||||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
|
||||||
Policy: onefactor,
|
|
||||||
RedirectURIs: []string{
|
|
||||||
"https://google.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil, nil)
|
|
||||||
|
|
||||||
require.NotNil(t, provider)
|
|
||||||
|
|
||||||
disco := provider.GetOpenIDConnectWellKnownConfiguration(examplecom)
|
|
||||||
|
|
||||||
require.Len(t, disco.CodeChallengeMethodsSupported, 2)
|
|
||||||
assert.Equal(t, PKCEChallengeMethodSHA256, disco.CodeChallengeMethodsSupported[0])
|
|
||||||
assert.Equal(t, PKCEChallengeMethodPlain, disco.CodeChallengeMethodsSupported[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewOpenIDConnectProviderDiscovery(t *testing.T) {
|
|
||||||
provider := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
|
||||||
IssuerPrivateKey: keyRSA2048,
|
|
||||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
|
||||||
EnablePKCEPlainChallenge: true,
|
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
|
||||||
{
|
|
||||||
ID: "a-client",
|
|
||||||
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
|
||||||
Policy: onefactor,
|
|
||||||
RedirectURIs: []string{
|
|
||||||
"https://google.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil, nil)
|
|
||||||
|
|
||||||
a := provider.GetOpenIDConnectWellKnownConfiguration("https://auth.example.com")
|
|
||||||
|
|
||||||
data, err := json.Marshal(&a)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
b := OpenIDConnectWellKnownConfiguration{}
|
|
||||||
|
|
||||||
assert.NoError(t, json.Unmarshal(data, &b))
|
|
||||||
|
|
||||||
assert.Equal(t, a, b)
|
|
||||||
|
|
||||||
y := provider.GetOAuth2WellKnownConfiguration("https://auth.example.com")
|
|
||||||
|
|
||||||
data, err = json.Marshal(&y)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
z := OAuth2WellKnownConfiguration{}
|
|
||||||
|
|
||||||
assert.NoError(t, json.Unmarshal(data, &z))
|
|
||||||
|
|
||||||
assert.Equal(t, y, z)
|
|
||||||
}
|
|
||||||
|
|
|
@ -235,7 +235,7 @@ func (s *Store) CreatePKCERequestSession(ctx context.Context, signature string,
|
||||||
// DeletePKCERequestSession marks the authorization request for a given PKCE request as deleted.
|
// DeletePKCERequestSession marks the authorization request for a given PKCE request as deleted.
|
||||||
// This implements a portion of pkce.PKCERequestStorage.
|
// This implements a portion of pkce.PKCERequestStorage.
|
||||||
func (s *Store) DeletePKCERequestSession(ctx context.Context, signature string) (err error) {
|
func (s *Store) DeletePKCERequestSession(ctx context.Context, signature string) (err error) {
|
||||||
return s.revokeSessionBySignature(ctx, storage.OAuth2SessionTypeAccessToken, signature)
|
return s.revokeSessionBySignature(ctx, storage.OAuth2SessionTypePKCEChallenge, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPKCERequestSession gets the authorization request for a given PKCE request.
|
// GetPKCERequestSession gets the authorization request for a given PKCE request.
|
||||||
|
@ -254,7 +254,7 @@ func (s *Store) CreateOpenIDConnectSession(ctx context.Context, authorizeCode st
|
||||||
// DeleteOpenIDConnectSession just implements the method required by fosite even though it's unused.
|
// DeleteOpenIDConnectSession just implements the method required by fosite even though it's unused.
|
||||||
// This implements a portion of openid.OpenIDConnectRequestStorage.
|
// This implements a portion of openid.OpenIDConnectRequestStorage.
|
||||||
func (s *Store) DeleteOpenIDConnectSession(ctx context.Context, authorizeCode string) (err error) {
|
func (s *Store) DeleteOpenIDConnectSession(ctx context.Context, authorizeCode string) (err error) {
|
||||||
return s.revokeSessionBySignature(ctx, storage.OAuth2SessionTypeAccessToken, authorizeCode)
|
return s.revokeSessionBySignature(ctx, storage.OAuth2SessionTypeOpenIDConnect, authorizeCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOpenIDConnectSession returns error:
|
// GetOpenIDConnectSession returns error:
|
||||||
|
|
|
@ -1,19 +1,30 @@
|
||||||
package oidc
|
package oidc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/authorization"
|
"github.com/authelia/authelia/v4/internal/authorization"
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
"github.com/authelia/authelia/v4/internal/mocks"
|
||||||
|
"github.com/authelia/authelia/v4/internal/model"
|
||||||
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
|
"github.com/authelia/authelia/v4/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
||||||
s := NewStore(&schema.OpenIDConnectConfiguration{
|
s := oidc.NewStore(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
IssuerPrivateKey: keyRSA2048,
|
IssuerPrivateKey: keyRSA2048,
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
@ -21,15 +32,15 @@ func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
||||||
ID: myclient,
|
ID: myclient,
|
||||||
Description: myclientdesc,
|
Description: myclientdesc,
|
||||||
Policy: onefactor,
|
Policy: onefactor,
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeProfile},
|
||||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "myotherclient",
|
ID: "myotherclient",
|
||||||
Description: myclientdesc,
|
Description: myclientdesc,
|
||||||
Policy: twofactor,
|
Policy: twofactor,
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeProfile},
|
||||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
@ -45,7 +56,7 @@ func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
||||||
s := NewStore(&schema.OpenIDConnectConfiguration{
|
s := oidc.NewStore(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
IssuerPrivateKey: keyRSA2048,
|
IssuerPrivateKey: keyRSA2048,
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
@ -53,8 +64,8 @@ func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
||||||
ID: myclient,
|
ID: myclient,
|
||||||
Description: myclientdesc,
|
Description: myclientdesc,
|
||||||
Policy: onefactor,
|
Policy: onefactor,
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeProfile},
|
||||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
@ -76,11 +87,11 @@ func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
|
||||||
ID: id,
|
ID: id,
|
||||||
Description: myclientdesc,
|
Description: myclientdesc,
|
||||||
Policy: onefactor,
|
Policy: onefactor,
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeProfile},
|
||||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
}
|
}
|
||||||
|
|
||||||
s := NewStore(&schema.OpenIDConnectConfiguration{
|
s := oidc.NewStore(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
IssuerPrivateKey: keyRSA2048,
|
IssuerPrivateKey: keyRSA2048,
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{c1},
|
Clients: []schema.OpenIDConnectClientConfiguration{c1},
|
||||||
|
@ -92,11 +103,11 @@ func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
|
||||||
assert.Equal(t, id, client.GetID())
|
assert.Equal(t, id, client.GetID())
|
||||||
assert.Equal(t, myclientdesc, client.GetDescription())
|
assert.Equal(t, myclientdesc, client.GetDescription())
|
||||||
assert.Equal(t, fosite.Arguments(c1.Scopes), client.GetScopes())
|
assert.Equal(t, fosite.Arguments(c1.Scopes), client.GetScopes())
|
||||||
assert.Equal(t, fosite.Arguments([]string{GrantTypeAuthorizationCode}), client.GetGrantTypes())
|
assert.Equal(t, fosite.Arguments([]string{oidc.GrantTypeAuthorizationCode}), client.GetGrantTypes())
|
||||||
assert.Equal(t, fosite.Arguments([]string{ResponseTypeAuthorizationCodeFlow}), client.GetResponseTypes())
|
assert.Equal(t, fosite.Arguments([]string{oidc.ResponseTypeAuthorizationCodeFlow}), client.GetResponseTypes())
|
||||||
assert.Equal(t, []string(nil), client.GetRedirectURIs())
|
assert.Equal(t, []string(nil), client.GetRedirectURIs())
|
||||||
assert.Equal(t, authorization.OneFactor, client.GetAuthorizationPolicy())
|
assert.Equal(t, authorization.OneFactor, client.GetAuthorizationPolicy())
|
||||||
assert.Equal(t, "$plaintext$mysecret", client.GetSecret().Encode())
|
assert.Equal(t, "$plaintext$client-secret", client.GetSecret().Encode())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
||||||
|
@ -104,11 +115,11 @@ func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
||||||
ID: myclient,
|
ID: myclient,
|
||||||
Description: myclientdesc,
|
Description: myclientdesc,
|
||||||
Policy: onefactor,
|
Policy: onefactor,
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeProfile},
|
||||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
}
|
}
|
||||||
|
|
||||||
s := NewStore(&schema.OpenIDConnectConfiguration{
|
s := oidc.NewStore(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
IssuerPrivateKey: keyRSA2048,
|
IssuerPrivateKey: keyRSA2048,
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{c1},
|
Clients: []schema.OpenIDConnectClientConfiguration{c1},
|
||||||
|
@ -120,7 +131,7 @@ func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
|
func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
|
||||||
s := NewStore(&schema.OpenIDConnectConfiguration{
|
s := oidc.NewStore(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
IssuerPrivateKey: keyRSA2048,
|
IssuerPrivateKey: keyRSA2048,
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
@ -128,8 +139,8 @@ func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
|
||||||
ID: myclient,
|
ID: myclient,
|
||||||
Description: myclientdesc,
|
Description: myclientdesc,
|
||||||
Policy: onefactor,
|
Policy: onefactor,
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeProfile},
|
||||||
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
Secret: tOpenIDConnectPlainTextClientSecret,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
@ -140,3 +151,516 @@ func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
|
||||||
assert.True(t, validClient)
|
assert.True(t, validClient)
|
||||||
assert.False(t, invalidClient)
|
assert.False(t, invalidClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStoreSuite(t *testing.T) {
|
||||||
|
suite.Run(t, &StoreSuite{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type StoreSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
mock *mocks.MockStorage
|
||||||
|
store *oidc.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StoreSuite) SetupTest() {
|
||||||
|
s.ctx = context.Background()
|
||||||
|
s.ctrl = gomock.NewController(s.T())
|
||||||
|
s.mock = mocks.NewMockStorage(s.ctrl)
|
||||||
|
s.store = oidc.NewStore(&schema.OpenIDConnectConfiguration{
|
||||||
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
{
|
||||||
|
ID: "hs256",
|
||||||
|
Secret: tOpenIDConnectPBKDF2ClientSecret,
|
||||||
|
Policy: authorization.OneFactor.String(),
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://client.example.com",
|
||||||
|
},
|
||||||
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT,
|
||||||
|
TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA256,
|
||||||
|
},
|
||||||
|
}}, s.mock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StoreSuite) TestGetSubject() {
|
||||||
|
s.T().Run("GenerateNew", func(t *testing.T) {
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
LoadUserOpaqueIdentifierBySignature(s.ctx, "openid", "", "john").
|
||||||
|
Return(nil, nil)
|
||||||
|
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
SaveUserOpaqueIdentifier(s.ctx, gomock.Any()).
|
||||||
|
Return(nil)
|
||||||
|
|
||||||
|
opaqueID, err := s.store.GetSubject(s.ctx, "", "john")
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEqual(t, uint32(0), opaqueID)
|
||||||
|
})
|
||||||
|
|
||||||
|
s.T().Run("ReturnDatabaseErrorOnLoad", func(t *testing.T) {
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
LoadUserOpaqueIdentifierBySignature(s.ctx, "openid", "", "john").
|
||||||
|
Return(nil, fmt.Errorf("failed to load"))
|
||||||
|
|
||||||
|
opaqueID, err := s.store.GetSubject(s.ctx, "", "john")
|
||||||
|
|
||||||
|
assert.EqualError(t, err, "failed to load")
|
||||||
|
assert.Equal(t, uint32(0), opaqueID.ID())
|
||||||
|
})
|
||||||
|
|
||||||
|
s.T().Run("ReturnDatabaseErrorOnSave", func(t *testing.T) {
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
LoadUserOpaqueIdentifierBySignature(s.ctx, "openid", "", "john").
|
||||||
|
Return(nil, nil)
|
||||||
|
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
SaveUserOpaqueIdentifier(s.ctx, gomock.Any()).
|
||||||
|
Return(fmt.Errorf("failed to save"))
|
||||||
|
|
||||||
|
opaqueID, err := s.store.GetSubject(s.ctx, "", "john")
|
||||||
|
|
||||||
|
assert.EqualError(t, err, "failed to save")
|
||||||
|
assert.Equal(t, uint32(0), opaqueID.ID())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StoreSuite) TestTx() {
|
||||||
|
gomock.InOrder(
|
||||||
|
s.mock.EXPECT().BeginTX(s.ctx).Return(s.ctx, nil),
|
||||||
|
s.mock.EXPECT().Commit(s.ctx).Return(nil),
|
||||||
|
s.mock.EXPECT().Rollback(s.ctx).Return(nil),
|
||||||
|
s.mock.EXPECT().BeginTX(s.ctx).Return(nil, fmt.Errorf("failed to begin")),
|
||||||
|
s.mock.EXPECT().Commit(s.ctx).Return(fmt.Errorf("failed to commit")),
|
||||||
|
s.mock.EXPECT().Rollback(s.ctx).Return(fmt.Errorf("failed to rollback")),
|
||||||
|
)
|
||||||
|
|
||||||
|
x, err := s.store.BeginTX(s.ctx)
|
||||||
|
s.Equal(s.ctx, x)
|
||||||
|
s.NoError(err)
|
||||||
|
s.NoError(s.store.Commit(s.ctx))
|
||||||
|
s.NoError(s.store.Rollback(s.ctx))
|
||||||
|
|
||||||
|
x, err = s.store.BeginTX(s.ctx)
|
||||||
|
s.Equal(nil, x)
|
||||||
|
s.EqualError(err, "failed to begin")
|
||||||
|
s.EqualError(s.store.Commit(s.ctx), "failed to commit")
|
||||||
|
s.EqualError(s.store.Rollback(s.ctx), "failed to rollback")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StoreSuite) TestClientAssertionJWTValid() {
|
||||||
|
gomock.InOrder(
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
LoadOAuth2BlacklistedJTI(s.ctx, "3a240379e8286a7a8ff5e99d68567e0e5e34e80168b8feffa89d3d33dea95b63").
|
||||||
|
Return(&model.OAuth2BlacklistedJTI{
|
||||||
|
ID: 1,
|
||||||
|
Signature: "3a240379e8286a7a8ff5e99d68567e0e5e34e80168b8feffa89d3d33dea95b63",
|
||||||
|
ExpiresAt: time.Now().Add(time.Hour),
|
||||||
|
}, nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
LoadOAuth2BlacklistedJTI(s.ctx, "e7f67ad76c80d57d34b19598462817932aec21d2806a08a786a8d4b9dd476068").
|
||||||
|
Return(&model.OAuth2BlacklistedJTI{
|
||||||
|
ID: 1,
|
||||||
|
Signature: "e7f67ad76c80d57d34b19598462817932aec21d2806a08a786a8d4b9dd476068",
|
||||||
|
ExpiresAt: time.Now().Add(-time.Hour),
|
||||||
|
}, nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
LoadOAuth2BlacklistedJTI(s.ctx, "f29ef0d85303a09411b76001c579980f1b1b7fc9deb1fa647875a724f4f231c6").
|
||||||
|
Return(nil, fmt.Errorf("failed to load")),
|
||||||
|
)
|
||||||
|
|
||||||
|
s.EqualError(s.store.ClientAssertionJWTValid(s.ctx, "066ee771-e156-4886-b99f-ee09b0d3edf4"), "jti_known")
|
||||||
|
s.NoError(s.store.ClientAssertionJWTValid(s.ctx, "5dad3ff7-e4f2-41b6-98a3-b73d872076ce"))
|
||||||
|
s.EqualError(s.store.ClientAssertionJWTValid(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7202"), "failed to load")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StoreSuite) TestCreateSessions() {
|
||||||
|
challenge := uuid.Must(uuid.NewRandom())
|
||||||
|
session := &model.OpenIDSession{
|
||||||
|
ChallengeID: challenge,
|
||||||
|
}
|
||||||
|
sessionData, _ := json.Marshal(session)
|
||||||
|
|
||||||
|
gomock.InOrder(
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
SaveOAuth2Session(s.ctx, storage.OAuth2SessionTypeAuthorizeCode, model.OAuth2Session{ChallengeID: challenge, RequestID: "abc", ClientID: "example", Signature: "abc", Active: true, Session: sessionData}).
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
SaveOAuth2Session(s.ctx, storage.OAuth2SessionTypeAuthorizeCode, model.OAuth2Session{ChallengeID: challenge, RequestID: "abc", ClientID: "example", Signature: "abc", Active: true, Session: sessionData}).
|
||||||
|
Return(fmt.Errorf("duplicate key")),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
SaveOAuth2Session(s.ctx, storage.OAuth2SessionTypeAccessToken, model.OAuth2Session{ChallengeID: challenge, RequestID: "abc", ClientID: "example", Signature: "abc", Active: true, Session: sessionData}).
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
SaveOAuth2Session(s.ctx, storage.OAuth2SessionTypeRefreshToken, model.OAuth2Session{ChallengeID: challenge, RequestID: "abc", ClientID: "example", Signature: "abc", Active: true, Session: sessionData}).
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
SaveOAuth2Session(s.ctx, storage.OAuth2SessionTypeOpenIDConnect, model.OAuth2Session{ChallengeID: challenge, RequestID: "abc", ClientID: "example", Signature: "abc", Active: true, Session: sessionData}).
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
SaveOAuth2Session(s.ctx, storage.OAuth2SessionTypePKCEChallenge, model.OAuth2Session{ChallengeID: challenge, RequestID: "abc", ClientID: "example", Signature: "abc", Active: true, Session: sessionData}).
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
SaveOAuth2PARContext(s.ctx, model.OAuth2PARContext{Signature: "abc", RequestID: "abc", ClientID: "example", Session: sessionData}).
|
||||||
|
Return(nil),
|
||||||
|
)
|
||||||
|
|
||||||
|
s.NoError(s.store.CreateAuthorizeCodeSession(s.ctx, "abc", &fosite.Request{
|
||||||
|
ID: "abc",
|
||||||
|
Client: &oidc.BaseClient{
|
||||||
|
ID: "example",
|
||||||
|
},
|
||||||
|
Session: session,
|
||||||
|
}))
|
||||||
|
|
||||||
|
s.EqualError(s.store.CreateAuthorizeCodeSession(s.ctx, "abc", &fosite.Request{
|
||||||
|
ID: "abc",
|
||||||
|
Client: &oidc.BaseClient{
|
||||||
|
ID: "example",
|
||||||
|
},
|
||||||
|
Session: session,
|
||||||
|
}), "duplicate key")
|
||||||
|
|
||||||
|
s.EqualError(s.store.CreateAuthorizeCodeSession(s.ctx, "abc", &fosite.Request{
|
||||||
|
ID: "abc",
|
||||||
|
Client: &oidc.BaseClient{
|
||||||
|
ID: "example",
|
||||||
|
},
|
||||||
|
Session: nil,
|
||||||
|
}), "can't convert type '<nil>' to an *OAuth2Session")
|
||||||
|
|
||||||
|
s.NoError(s.store.CreateAccessTokenSession(s.ctx, "abc", &fosite.Request{
|
||||||
|
ID: "abc",
|
||||||
|
Client: &oidc.BaseClient{
|
||||||
|
ID: "example",
|
||||||
|
},
|
||||||
|
Session: session,
|
||||||
|
}))
|
||||||
|
|
||||||
|
s.NoError(s.store.CreateRefreshTokenSession(s.ctx, "abc", &fosite.Request{
|
||||||
|
ID: "abc",
|
||||||
|
Client: &oidc.BaseClient{
|
||||||
|
ID: "example",
|
||||||
|
},
|
||||||
|
Session: session,
|
||||||
|
}))
|
||||||
|
|
||||||
|
s.NoError(s.store.CreateOpenIDConnectSession(s.ctx, "abc", &fosite.Request{
|
||||||
|
ID: "abc",
|
||||||
|
Client: &oidc.BaseClient{
|
||||||
|
ID: "example",
|
||||||
|
},
|
||||||
|
Session: session,
|
||||||
|
}))
|
||||||
|
|
||||||
|
s.NoError(s.store.CreatePKCERequestSession(s.ctx, "abc", &fosite.Request{
|
||||||
|
ID: "abc",
|
||||||
|
Client: &oidc.BaseClient{
|
||||||
|
ID: "example",
|
||||||
|
},
|
||||||
|
Session: session,
|
||||||
|
}))
|
||||||
|
|
||||||
|
s.NoError(s.store.CreatePARSession(s.ctx, "abc", &fosite.AuthorizeRequest{
|
||||||
|
Request: fosite.Request{
|
||||||
|
ID: "abc",
|
||||||
|
Client: &oidc.BaseClient{
|
||||||
|
ID: "example",
|
||||||
|
},
|
||||||
|
Session: session,
|
||||||
|
}}))
|
||||||
|
|
||||||
|
s.EqualError(s.store.CreatePARSession(s.ctx, "abc", &fosite.AuthorizeRequest{
|
||||||
|
Request: fosite.Request{
|
||||||
|
ID: "abc",
|
||||||
|
Client: &oidc.BaseClient{
|
||||||
|
ID: "example",
|
||||||
|
},
|
||||||
|
Session: nil,
|
||||||
|
}}), "can't convert type '<nil>' to an *OAuth2Session")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StoreSuite) TestRevokeSessions() {
|
||||||
|
gomock.InOrder(
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
DeactivateOAuth2Session(s.ctx, storage.OAuth2SessionTypeAuthorizeCode, "abc1").
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
DeactivateOAuth2Session(s.ctx, storage.OAuth2SessionTypeAuthorizeCode, "abc2").
|
||||||
|
Return(fmt.Errorf("not found")),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2Session(s.ctx, storage.OAuth2SessionTypeAccessToken, "at_example1").
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2Session(s.ctx, storage.OAuth2SessionTypeAccessToken, "at_example2").
|
||||||
|
Return(fmt.Errorf("not found")),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2SessionByRequestID(s.ctx, storage.OAuth2SessionTypeAccessToken, "65471ccb-d650-4006-a95f-cb4f4e3d7200").
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2SessionByRequestID(s.ctx, storage.OAuth2SessionTypeAccessToken, "65471ccb-d650-4006-a95f-cb4f4e3d7201").
|
||||||
|
Return(fmt.Errorf("not found")),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2SessionByRequestID(s.ctx, storage.OAuth2SessionTypeAccessToken, "65471ccb-d650-4006-a95f-cb4f4e3d7202").
|
||||||
|
Return(sql.ErrNoRows),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2Session(s.ctx, storage.OAuth2SessionTypeRefreshToken, "rt_example1").
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2Session(s.ctx, storage.OAuth2SessionTypeRefreshToken, "rt_example2").
|
||||||
|
Return(fmt.Errorf("not found")),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
DeactivateOAuth2SessionByRequestID(s.ctx, storage.OAuth2SessionTypeRefreshToken, "65471ccb-d650-4006-a95f-cb4f4e3d7200").
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
DeactivateOAuth2SessionByRequestID(s.ctx, storage.OAuth2SessionTypeRefreshToken, "65471ccb-d650-4006-a95f-cb4f4e3d7201").
|
||||||
|
Return(fmt.Errorf("not found")),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
DeactivateOAuth2SessionByRequestID(s.ctx, storage.OAuth2SessionTypeRefreshToken, "65471ccb-d650-4006-a95f-cb4f4e3d7202").
|
||||||
|
Return(sql.ErrNoRows),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
DeactivateOAuth2SessionByRequestID(s.ctx, storage.OAuth2SessionTypeRefreshToken, "65471ccb-d650-4006-a95f-cb4f4e3d7200").
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
DeactivateOAuth2SessionByRequestID(s.ctx, storage.OAuth2SessionTypeRefreshToken, "65471ccb-d650-4006-a95f-cb4f4e3d7201").
|
||||||
|
Return(fmt.Errorf("not found")),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
DeactivateOAuth2SessionByRequestID(s.ctx, storage.OAuth2SessionTypeRefreshToken, "65471ccb-d650-4006-a95f-cb4f4e3d7202").
|
||||||
|
Return(sql.ErrNoRows),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2Session(s.ctx, storage.OAuth2SessionTypePKCEChallenge, "pkce1").
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2Session(s.ctx, storage.OAuth2SessionTypePKCEChallenge, "pkce2").
|
||||||
|
Return(fmt.Errorf("not found")),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2Session(s.ctx, storage.OAuth2SessionTypeOpenIDConnect, "ac_1").
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2Session(s.ctx, storage.OAuth2SessionTypeOpenIDConnect, "ac_2").
|
||||||
|
Return(fmt.Errorf("not found")),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2PARContext(s.ctx, "urn:par1").
|
||||||
|
Return(nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
RevokeOAuth2PARContext(s.ctx, "urn:par2").
|
||||||
|
Return(fmt.Errorf("not found")),
|
||||||
|
)
|
||||||
|
|
||||||
|
s.NoError(s.store.InvalidateAuthorizeCodeSession(s.ctx, "abc1"))
|
||||||
|
s.EqualError(s.store.InvalidateAuthorizeCodeSession(s.ctx, "abc2"), "not found")
|
||||||
|
|
||||||
|
s.NoError(s.store.DeleteAccessTokenSession(s.ctx, "at_example1"))
|
||||||
|
s.EqualError(s.store.DeleteAccessTokenSession(s.ctx, "at_example2"), "not found")
|
||||||
|
|
||||||
|
s.NoError(s.store.RevokeAccessToken(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7200"))
|
||||||
|
s.EqualError(s.store.RevokeAccessToken(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7201"), "not found")
|
||||||
|
s.EqualError(s.store.RevokeAccessToken(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7202"), "not_found")
|
||||||
|
|
||||||
|
s.NoError(s.store.DeleteRefreshTokenSession(s.ctx, "rt_example1"))
|
||||||
|
s.EqualError(s.store.DeleteRefreshTokenSession(s.ctx, "rt_example2"), "not found")
|
||||||
|
|
||||||
|
s.NoError(s.store.RevokeRefreshToken(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7200"))
|
||||||
|
s.EqualError(s.store.RevokeRefreshToken(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7201"), "not found")
|
||||||
|
s.EqualError(s.store.RevokeRefreshToken(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7202"), "sql: no rows in result set")
|
||||||
|
|
||||||
|
s.NoError(s.store.RevokeRefreshTokenMaybeGracePeriod(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7200", "1"))
|
||||||
|
s.EqualError(s.store.RevokeRefreshTokenMaybeGracePeriod(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7201", "2"), "not found")
|
||||||
|
s.EqualError(s.store.RevokeRefreshTokenMaybeGracePeriod(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7202", "3"), "sql: no rows in result set")
|
||||||
|
|
||||||
|
s.NoError(s.store.DeletePKCERequestSession(s.ctx, "pkce1"))
|
||||||
|
s.EqualError(s.store.DeletePKCERequestSession(s.ctx, "pkce2"), "not found")
|
||||||
|
|
||||||
|
s.NoError(s.store.DeleteOpenIDConnectSession(s.ctx, "ac_1"))
|
||||||
|
s.EqualError(s.store.DeleteOpenIDConnectSession(s.ctx, "ac_2"), "not found")
|
||||||
|
|
||||||
|
s.NoError(s.store.DeletePARSession(s.ctx, "urn:par1"))
|
||||||
|
s.EqualError(s.store.DeletePARSession(s.ctx, "urn:par2"), "not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StoreSuite) TestGetSessions() {
|
||||||
|
challenge := uuid.Must(uuid.NewRandom())
|
||||||
|
session := &model.OpenIDSession{
|
||||||
|
ChallengeID: challenge,
|
||||||
|
ClientID: "hs256",
|
||||||
|
}
|
||||||
|
sessionData, _ := json.Marshal(session)
|
||||||
|
|
||||||
|
sessionb := &model.OpenIDSession{
|
||||||
|
ChallengeID: challenge,
|
||||||
|
ClientID: "hs256",
|
||||||
|
}
|
||||||
|
sessionDatab, _ := json.Marshal(sessionb)
|
||||||
|
|
||||||
|
gomock.InOrder(
|
||||||
|
s.mock.EXPECT().LoadOAuth2Session(s.ctx, storage.OAuth2SessionTypeAuthorizeCode, "ac_123").
|
||||||
|
Return(&model.OAuth2Session{ClientID: "hs256", Session: sessionData, Active: true}, nil),
|
||||||
|
s.mock.EXPECT().LoadOAuth2Session(s.ctx, storage.OAuth2SessionTypeAuthorizeCode, "ac_456").
|
||||||
|
Return(&model.OAuth2Session{ClientID: "hs256", Session: sessionData, Active: false}, nil),
|
||||||
|
s.mock.EXPECT().LoadOAuth2Session(s.ctx, storage.OAuth2SessionTypeAuthorizeCode, "ac_aaa").
|
||||||
|
Return(nil, sql.ErrNoRows),
|
||||||
|
s.mock.EXPECT().LoadOAuth2Session(s.ctx, storage.OAuth2SessionTypeAuthorizeCode, "ac_130").
|
||||||
|
Return(nil, fmt.Errorf("timeout")),
|
||||||
|
s.mock.EXPECT().LoadOAuth2Session(s.ctx, storage.OAuth2SessionTypeAuthorizeCode, "ac_badclient").
|
||||||
|
Return(&model.OAuth2Session{ClientID: "no-client", Session: sessionDatab, Active: true}, nil),
|
||||||
|
s.mock.EXPECT().LoadOAuth2Session(s.ctx, storage.OAuth2SessionTypeAccessToken, "at").
|
||||||
|
Return(&model.OAuth2Session{ClientID: "hs256", Session: sessionData, Active: true}, nil),
|
||||||
|
s.mock.EXPECT().LoadOAuth2Session(s.ctx, storage.OAuth2SessionTypeRefreshToken, "rt").
|
||||||
|
Return(&model.OAuth2Session{ClientID: "hs256", Session: sessionData, Active: true}, nil),
|
||||||
|
s.mock.EXPECT().LoadOAuth2Session(s.ctx, storage.OAuth2SessionTypePKCEChallenge, "pkce").
|
||||||
|
Return(&model.OAuth2Session{ClientID: "hs256", Session: sessionData, Active: true}, nil),
|
||||||
|
s.mock.EXPECT().LoadOAuth2Session(s.ctx, storage.OAuth2SessionTypeOpenIDConnect, "ot").
|
||||||
|
Return(&model.OAuth2Session{ClientID: "hs256", Session: sessionData, Active: true}, nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
LoadOAuth2PARContext(s.ctx, "urn:par").
|
||||||
|
Return(&model.OAuth2PARContext{Signature: "abc", RequestID: "abc", ClientID: "hs256", Session: sessionData}, nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
LoadOAuth2PARContext(s.ctx, "urn:par").
|
||||||
|
Return(nil, sql.ErrNoRows),
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
r fosite.Requester
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
r, err = s.store.GetAuthorizeCodeSession(s.ctx, "ac_123", &model.OpenIDSession{})
|
||||||
|
s.NotNil(r)
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
r, err = s.store.GetAuthorizeCodeSession(s.ctx, "ac_456", &model.OpenIDSession{})
|
||||||
|
s.NotNil(r)
|
||||||
|
s.EqualError(err, "Authorization code has ben invalidated")
|
||||||
|
|
||||||
|
r, err = s.store.GetAuthorizeCodeSession(s.ctx, "ac_aaa", &model.OpenIDSession{})
|
||||||
|
s.Nil(r)
|
||||||
|
s.EqualError(err, "not_found")
|
||||||
|
|
||||||
|
r, err = s.store.GetAuthorizeCodeSession(s.ctx, "ac_130", &model.OpenIDSession{})
|
||||||
|
s.Nil(r)
|
||||||
|
s.EqualError(err, "timeout")
|
||||||
|
|
||||||
|
r, err = s.store.GetAuthorizeCodeSession(s.ctx, "ac_badclient", &model.OpenIDSession{})
|
||||||
|
s.Nil(r)
|
||||||
|
s.EqualError(err, "invalid_client")
|
||||||
|
|
||||||
|
r, err = s.store.GetAccessTokenSession(s.ctx, "at", &model.OpenIDSession{})
|
||||||
|
s.NotNil(r)
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
r, err = s.store.GetRefreshTokenSession(s.ctx, "rt", &model.OpenIDSession{})
|
||||||
|
s.NotNil(r)
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
r, err = s.store.GetPKCERequestSession(s.ctx, "pkce", &model.OpenIDSession{})
|
||||||
|
s.NotNil(r)
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
r, err = s.store.GetOpenIDConnectSession(s.ctx, "ot", &fosite.Request{
|
||||||
|
ID: "abc",
|
||||||
|
Client: &oidc.BaseClient{
|
||||||
|
ID: "example",
|
||||||
|
},
|
||||||
|
Session: session,
|
||||||
|
})
|
||||||
|
s.NotNil(r)
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
r, err = s.store.GetPARSession(s.ctx, "urn:par")
|
||||||
|
s.NotNil(r)
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
r, err = s.store.GetPARSession(s.ctx, "urn:par")
|
||||||
|
s.Nil(r)
|
||||||
|
s.EqualError(err, "sql: no rows in result set")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StoreSuite) TestIsJWTUsed() {
|
||||||
|
gomock.InOrder(
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
LoadOAuth2BlacklistedJTI(s.ctx, "3a240379e8286a7a8ff5e99d68567e0e5e34e80168b8feffa89d3d33dea95b63").
|
||||||
|
Return(&model.OAuth2BlacklistedJTI{
|
||||||
|
ID: 1,
|
||||||
|
Signature: "3a240379e8286a7a8ff5e99d68567e0e5e34e80168b8feffa89d3d33dea95b63",
|
||||||
|
ExpiresAt: time.Now().Add(time.Hour),
|
||||||
|
}, nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
LoadOAuth2BlacklistedJTI(s.ctx, "e7f67ad76c80d57d34b19598462817932aec21d2806a08a786a8d4b9dd476068").
|
||||||
|
Return(&model.OAuth2BlacklistedJTI{
|
||||||
|
ID: 1,
|
||||||
|
Signature: "e7f67ad76c80d57d34b19598462817932aec21d2806a08a786a8d4b9dd476068",
|
||||||
|
ExpiresAt: time.Now().Add(-time.Hour),
|
||||||
|
}, nil),
|
||||||
|
s.mock.
|
||||||
|
EXPECT().
|
||||||
|
LoadOAuth2BlacklistedJTI(s.ctx, "f29ef0d85303a09411b76001c579980f1b1b7fc9deb1fa647875a724f4f231c6").
|
||||||
|
Return(nil, fmt.Errorf("failed to load")),
|
||||||
|
)
|
||||||
|
|
||||||
|
used, err := s.store.IsJWTUsed(s.ctx, "066ee771-e156-4886-b99f-ee09b0d3edf4")
|
||||||
|
s.True(used)
|
||||||
|
s.EqualError(err, "jti_known")
|
||||||
|
|
||||||
|
used, err = s.store.IsJWTUsed(s.ctx, "5dad3ff7-e4f2-41b6-98a3-b73d872076ce")
|
||||||
|
s.False(used)
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
used, err = s.store.IsJWTUsed(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7202")
|
||||||
|
s.True(used)
|
||||||
|
s.EqualError(err, "failed to load")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StoreSuite) TestMarkJWTUsedForTime() {
|
||||||
|
gomock.InOrder(
|
||||||
|
s.mock.EXPECT().
|
||||||
|
SaveOAuth2BlacklistedJTI(s.ctx, model.OAuth2BlacklistedJTI{Signature: "f29ef0d85303a09411b76001c579980f1b1b7fc9deb1fa647875a724f4f231c6", ExpiresAt: time.Unix(160000000, 0)}).
|
||||||
|
Return(nil),
|
||||||
|
s.mock.EXPECT().SaveOAuth2BlacklistedJTI(s.ctx, model.OAuth2BlacklistedJTI{Signature: "0dab0de97ed4e05da82763497448daf4f6b555c99218100e3ef5a81f36232940", ExpiresAt: time.Unix(160000000, 0)}).
|
||||||
|
Return(fmt.Errorf("already marked")),
|
||||||
|
)
|
||||||
|
|
||||||
|
s.NoError(s.store.MarkJWTUsedForTime(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7202", time.Unix(160000000, 0)))
|
||||||
|
s.EqualError(s.store.MarkJWTUsedForTime(s.ctx, "65471ccb-d650-4006-a95f-cb4f4e3d7201", time.Unix(160000000, 0)), "already marked")
|
||||||
|
}
|
||||||
|
|
|
@ -515,7 +515,10 @@ type OAuth2MutualTLSClientAuthenticationDiscoveryOptions struct {
|
||||||
within mtls_endpoint_aliases that do not define endpoints to which an OAuth client makes a direct request have
|
within mtls_endpoint_aliases that do not define endpoints to which an OAuth client makes a direct request have
|
||||||
no meaning and SHOULD be ignored.
|
no meaning and SHOULD be ignored.
|
||||||
*/
|
*/
|
||||||
MutualTLSEndpointAliases struct {
|
MutualTLSEndpointAliases OAuth2MutualTLSClientAuthenticationAliasesDiscoveryOptions `json:"mtls_endpoint_aliases"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OAuth2MutualTLSClientAuthenticationAliasesDiscoveryOptions struct {
|
||||||
AuthorizationEndpoint string `json:"authorization_endpoint,omitempty"`
|
AuthorizationEndpoint string `json:"authorization_endpoint,omitempty"`
|
||||||
TokenEndpoint string `json:"token_endpoint,omitempty"`
|
TokenEndpoint string `json:"token_endpoint,omitempty"`
|
||||||
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
|
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
|
||||||
|
@ -526,7 +529,6 @@ type OAuth2MutualTLSClientAuthenticationDiscoveryOptions struct {
|
||||||
FederationRegistrationEndpoint string `json:"federation_registration_endpoint,omitempty"`
|
FederationRegistrationEndpoint string `json:"federation_registration_endpoint,omitempty"`
|
||||||
PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint,omitempty"`
|
PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint,omitempty"`
|
||||||
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
|
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
|
||||||
} `json:"mtls_endpoint_aliases"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions struct {
|
type OAuth2JWTSecuredAuthorizationRequestDiscoveryOptions struct {
|
||||||
|
@ -954,15 +956,3 @@ type OpenIDConnectContext interface {
|
||||||
|
|
||||||
IssuerURL() (issuerURL *url.URL, err error)
|
IssuerURL() (issuerURL *url.URL, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockOpenIDConnectContext is a minimal implementation of OpenIDConnectContext for the purpose of testing.
|
|
||||||
type MockOpenIDConnectContext struct {
|
|
||||||
context.Context
|
|
||||||
|
|
||||||
MockIssuerURL *url.URL
|
|
||||||
}
|
|
||||||
|
|
||||||
// IssuerURL returns the MockIssuerURL.
|
|
||||||
func (m *MockOpenIDConnectContext) IssuerURL() (issuerURL *url.URL, err error) {
|
|
||||||
return m.MockIssuerURL, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package oidc
|
package oidc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -11,10 +12,11 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/model"
|
"github.com/authelia/authelia/v4/internal/model"
|
||||||
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewSession(t *testing.T) {
|
func TestNewSession(t *testing.T) {
|
||||||
session := NewSession()
|
session := oidc.NewSession()
|
||||||
|
|
||||||
require.NotNil(t, session)
|
require.NotNil(t, session)
|
||||||
|
|
||||||
|
@ -34,24 +36,24 @@ func TestNewSessionWithAuthorizeRequest(t *testing.T) {
|
||||||
|
|
||||||
formValues := url.Values{}
|
formValues := url.Values{}
|
||||||
|
|
||||||
formValues.Set(ClaimNonce, "abc123xyzauthelia")
|
formValues.Set(oidc.ClaimNonce, "abc123xyzauthelia")
|
||||||
|
|
||||||
request := &fosite.AuthorizeRequest{
|
request := &fosite.AuthorizeRequest{
|
||||||
Request: fosite.Request{
|
Request: fosite.Request{
|
||||||
ID: requestID.String(),
|
ID: requestID.String(),
|
||||||
Form: formValues,
|
Form: formValues,
|
||||||
Client: &BaseClient{ID: "example"},
|
Client: &oidc.BaseClient{ID: "example"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
extra := map[string]any{
|
extra := map[string]any{
|
||||||
ClaimPreferredUsername: "john",
|
oidc.ClaimPreferredUsername: "john",
|
||||||
}
|
}
|
||||||
|
|
||||||
requested := time.Unix(1647332518, 0)
|
requested := time.Unix(1647332518, 0)
|
||||||
authAt := time.Unix(1647332500, 0)
|
authAt := time.Unix(1647332500, 0)
|
||||||
issuer := examplecom
|
issuer := examplecom
|
||||||
amr := []string{AMRPasswordBasedAuthentication}
|
amr := []string{oidc.AMRPasswordBasedAuthentication}
|
||||||
|
|
||||||
consent := &model.OAuth2ConsentSession{
|
consent := &model.OAuth2ConsentSession{
|
||||||
ChallengeID: uuid.New(),
|
ChallengeID: uuid.New(),
|
||||||
|
@ -59,7 +61,7 @@ func TestNewSessionWithAuthorizeRequest(t *testing.T) {
|
||||||
Subject: uuid.NullUUID{UUID: subject, Valid: true},
|
Subject: uuid.NullUUID{UUID: subject, Valid: true},
|
||||||
}
|
}
|
||||||
|
|
||||||
session := NewSessionWithAuthorizeRequest(MustParseRequestURI(issuer), "primary", "john", amr, extra, authAt, consent, request)
|
session := oidc.NewSessionWithAuthorizeRequest(MustParseRequestURI(issuer), "primary", "john", amr, extra, authAt, consent, request)
|
||||||
|
|
||||||
require.NotNil(t, session)
|
require.NotNil(t, session)
|
||||||
require.NotNil(t, session.Extra)
|
require.NotNil(t, session.Extra)
|
||||||
|
@ -80,19 +82,36 @@ func TestNewSessionWithAuthorizeRequest(t *testing.T) {
|
||||||
assert.Equal(t, authAt, session.Claims.AuthTime)
|
assert.Equal(t, authAt, session.Claims.AuthTime)
|
||||||
assert.Equal(t, requested, session.Claims.RequestedAt)
|
assert.Equal(t, requested, session.Claims.RequestedAt)
|
||||||
assert.Equal(t, issuer, session.Claims.Issuer)
|
assert.Equal(t, issuer, session.Claims.Issuer)
|
||||||
assert.Equal(t, "john", session.Claims.Extra[ClaimPreferredUsername])
|
assert.Equal(t, "john", session.Claims.Extra[oidc.ClaimPreferredUsername])
|
||||||
|
|
||||||
assert.Equal(t, "primary", session.Headers.Get(JWTHeaderKeyIdentifier))
|
assert.Equal(t, "primary", session.Headers.Get(oidc.JWTHeaderKeyIdentifier))
|
||||||
|
|
||||||
consent = &model.OAuth2ConsentSession{
|
consent = &model.OAuth2ConsentSession{
|
||||||
ChallengeID: uuid.New(),
|
ChallengeID: uuid.New(),
|
||||||
RequestedAt: requested,
|
RequestedAt: requested,
|
||||||
}
|
}
|
||||||
|
|
||||||
session = NewSessionWithAuthorizeRequest(MustParseRequestURI(issuer), "primary", "john", nil, nil, authAt, consent, request)
|
session = oidc.NewSessionWithAuthorizeRequest(MustParseRequestURI(issuer), "primary", "john", nil, nil, authAt, consent, request)
|
||||||
|
|
||||||
require.NotNil(t, session)
|
require.NotNil(t, session)
|
||||||
require.NotNil(t, session.Claims)
|
require.NotNil(t, session.Claims)
|
||||||
assert.NotNil(t, session.Claims.Extra)
|
assert.NotNil(t, session.Claims.Extra)
|
||||||
assert.Nil(t, session.Claims.AuthenticationMethodsReferences)
|
assert.Nil(t, session.Claims.AuthenticationMethodsReferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MockOpenIDConnectContext is a minimal implementation of OpenIDConnectContext for the purpose of testing.
|
||||||
|
type MockOpenIDConnectContext struct {
|
||||||
|
context.Context
|
||||||
|
|
||||||
|
MockIssuerURL *url.URL
|
||||||
|
IssuerURLFunc func() (issuerURL *url.URL, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssuerURL returns the MockIssuerURL.
|
||||||
|
func (m *MockOpenIDConnectContext) IssuerURL() (issuerURL *url.URL, err error) {
|
||||||
|
if m.IssuerURLFunc != nil {
|
||||||
|
return m.IssuerURLFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.MockIssuerURL, nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBytesJoin(t *testing.T) {
|
||||||
|
a := []byte("a")
|
||||||
|
b := []byte("b")
|
||||||
|
|
||||||
|
assert.Equal(t, "ab", string(BytesJoin(a, b)))
|
||||||
|
assert.Equal(t, "a", string(BytesJoin(a)))
|
||||||
|
assert.Equal(t, "", string(BytesJoin()))
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTestingClock(t *testing.T) {
|
||||||
|
c := &TestingClock{
|
||||||
|
now: time.Unix(0, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, int64(0), c.Now().Unix())
|
||||||
|
c.now = time.Unix(20, 0)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(20), c.Now().Unix())
|
||||||
|
assert.Equal(t, int64(20000000000), c.Now().UnixNano())
|
||||||
|
|
||||||
|
c.Set(time.Unix(16000000, 0))
|
||||||
|
|
||||||
|
assert.Equal(t, int64(16000000), c.Now().Unix())
|
||||||
|
|
||||||
|
before := c.Now()
|
||||||
|
|
||||||
|
<-c.After(time.Millisecond * 100)
|
||||||
|
|
||||||
|
assert.Equal(t, before, c.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRealClock(t *testing.T) {
|
||||||
|
c := &RealClock{}
|
||||||
|
|
||||||
|
assert.WithinDuration(t, time.Now(), c.Now(), time.Second)
|
||||||
|
|
||||||
|
before := c.Now()
|
||||||
|
|
||||||
|
<-c.After(time.Millisecond * 100)
|
||||||
|
|
||||||
|
after := c.Now()
|
||||||
|
|
||||||
|
assert.WithinDuration(t, before, after, time.Millisecond*120)
|
||||||
|
|
||||||
|
diff := after.Sub(before)
|
||||||
|
|
||||||
|
assert.GreaterOrEqual(t, diff, time.Millisecond*100)
|
||||||
|
}
|
Loading…
Reference in New Issue