feat(oidc): issuer jwk certificates (#3989)
This allows for JWKs to include certificate information, either signed via Global PKI, Enterprise PKI, or self-signed.pull/3855/head
parent
66ea374227
commit
6810c91d34
|
@ -815,36 +815,79 @@ 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_private_key to sign JWT's. All certificates in the chain must be within the validity period, and every
|
||||||
|
## certificate included must be signed by the certificate immediately after it if provided.
|
||||||
|
# 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-----
|
||||||
|
|
||||||
## 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.
|
||||||
## 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-----
|
||||||
# MXIEogIB$AKCAQEAxZVJP3WF//PG2fLQoEC9DtdiFG/+00vqlbVzz47nyxKONIPI
|
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
||||||
# lmL3UdmqpGTKMe/5Brqse4ZAKlQHiDbwzK9ypnfigtHuvh/JO0S7ChP70RC67ed1
|
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
||||||
# HV1nyfz5eW3llbtGJPrlYLqITNgctHp6zmRUFtSzPj9qFvozI93LJi492yL1+vu8
|
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
||||||
# Un3Dm8+Qq6XM2tPdEcldB/dtBwOWoF+8eOOVsu0TDuB5bwlhBVGJuSAuzBPRS2bF
|
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
||||||
# Ga4uk0JDdkDOMCEQxC5uWDFxgfERSMFyfLVWD47woDbuWEBq10c0z+dpWPMp7Ain
|
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
||||||
# YnnkqicwCN88Z0zid6MmMQ65F4+9Hc+qC/p6xwIDAQABAoIBAGlhaAHKor+Su3o/
|
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
||||||
# AXqXTL5/rbYMzbLQiLt0XeJT69jpeqMTroZXHmWvXE3128mqnf0yzw/K2Ko6yxGh
|
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
||||||
# i+j/onya8FqpsVYCCgfsbn2/js1AyRJeIp6Y1ORsYnqbXJnxmkXa80AV/OBPW2/+
|
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
||||||
# 60TtSdQrebY3iFPc+i2k+9bPTvpyyDLKlz8UwdZG+k5uyYNIyQTccz+PjwsIvDij
|
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
||||||
# 7tKYamhhLN3QXt3/aZTFpjTgezP4WyriZxjWrddHowc47q2rwNS95ND39JcysJAc
|
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
||||||
# 0Pcbu8A5lVa7Fx33uOtzDfKWIW7xVEN+OtPgN+FbTjXcXk5IZedl+pW5lU5P++G/
|
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
||||||
# ZPvz+WECgYEA9g6HwdODW3e68bOqsFoKg35+vfUFMzlyMF8HFylNVfnLpTEDr637
|
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
||||||
# owzMFvcUxVd71b+gV5nnnbI+riUFIgyR8vhCjhy4moopDPahC4/KwN4NG6uz+i1h
|
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
||||||
# AB6D5+zn2BjnO/5xMMFGlApWtRNmJVGYlNDj3bXKh2VXzzy03VNeD8kCgYEAzZFL
|
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
||||||
# OlzoRB1HKpTWIECcuvxofMxLOLb3zs0k2t/FYNYIpovmGWCCAULz13y53e5+/+5m
|
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
||||||
# 7I9VUZJFaIhaZ36qVBApCKdru69pZMkWCcQO9jELFcx51Ez7OgJWzu7GS1QJCPKC
|
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
||||||
# fEDxI0rZK21j93/Sl/nUnEir7CYpQ+wvCaGuHg8CgYAXgbncfY1+DokwkB6NbHy2
|
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
||||||
# pT4Mfbz6cNGE538w6kQ2I4AeDvmwLentYMqaow478CinegAiflSPTzkHwAemghbr
|
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
||||||
# ZGZPV1UXhn13fJRUG2+eT1hnPVcbXnx223N0k8Bud6qXo65CnyRT/kzcTbcjd5Eh
|
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
||||||
# Hne2daicmMTzynPo9Q72aQKBgBmobO9X8VWvIdbaxO85oVZlctVA2pK1o7CYQmVf
|
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
||||||
# UM+JZ4MCKzI3rYJizPS0iK5+ujNPmmEkcs2/qBIoEsCgOrpLWhPOcc/3UPxXbPzD
|
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
||||||
# D+sCrBOIdhxdj23qJNOnUfDNCGOpgUfpAzAYg4q8GKInvi1h7XukRnEvQi9MJ4LY
|
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
||||||
# P1dZAoGASGcGnTMkmeSXP8ux+dvQJAiJskn/sJIgBZ5uq5GRCeLBUosRSVxM75UK
|
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
||||||
# vAh/c/RBj+pYXVKuPuHGZCQJxsdcRXzXNGouUtgbaYML5Me/Hagt20QzDRBfuGBg
|
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
||||||
# qeZBJaXhjElvw6PUWtg4x+LYRCBpq/bS3LK3ozZrSTukVkKDegw=
|
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
||||||
# -----END RSA PRIVATE KEY-----
|
# DO NOT USE==
|
||||||
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
## The lifespans configure the expiration for these token types.
|
## The lifespans configure the expiration for these token types.
|
||||||
# access_token_lifespan: 1h
|
# access_token_lifespan: 1h
|
||||||
|
|
|
@ -33,33 +33,72 @@ The following snippet provides a sample-configuration for the OIDC identity prov
|
||||||
identity_providers:
|
identity_providers:
|
||||||
oidc:
|
oidc:
|
||||||
hmac_secret: this_is_a_secret_abc123abc123abc
|
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: |
|
issuer_private_key: |
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
MXIEogIB$AKCAQEAxZVJP3WF//PG2fLQoEC9DtdiFG/+00vqlbVzz47nyxKONIPI
|
MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
||||||
lmL3UdmqpGTKMe/5Brqse4ZAKlQHiDbwzK9ypnfigtHuvh/JO0S7ChP70RC67ed1
|
T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
||||||
HV1nyfz5eW3llbtGJPrlYLqITNgctHp6zmRUFtSzPj9qFvozI93LJi492yL1+vu8
|
KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
||||||
Un3Dm8+Qq6XM2tPdEcldB/dtBwOWoF+8eOOVsu0TDuB5bwlhBVGJuSAuzBPRS2bF
|
+5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
||||||
Ga4uk0JDdkDOMCEQxC5uWDFxgfERSMFyfLVWD47woDbuWEBq10c0z+dpWPMp7Ain
|
LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
||||||
YnnkqicwCN88Z0zid6MmMQ65F4+9Hc+qC/p6xwIDAQABAoIBAGlhaAHKor+Su3o/
|
txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
||||||
AXqXTL5/rbYMzbLQiLt0XeJT69jpeqMTroZXHmWvXE3128mqnf0yzw/K2Ko6yxGh
|
aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
||||||
i+j/onya8FqpsVYCCgfsbn2/js1AyRJeIp6Y1ORsYnqbXJnxmkXa80AV/OBPW2/+
|
Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
||||||
60TtSdQrebY3iFPc+i2k+9bPTvpyyDLKlz8UwdZG+k5uyYNIyQTccz+PjwsIvDij
|
ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
||||||
7tKYamhhLN3QXt3/aZTFpjTgezP4WyriZxjWrddHowc47q2rwNS95ND39JcysJAc
|
LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
||||||
0Pcbu8A5lVa7Fx33uOtzDfKWIW7xVEN+OtPgN+FbTjXcXk5IZedl+pW5lU5P++G/
|
jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
||||||
ZPvz+WECgYEA9g6HwdODW3e68bOqsFoKg35+vfUFMzlyMF8HFylNVfnLpTEDr637
|
BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
||||||
owzMFvcUxVd71b+gV5nnnbI+riUFIgyR8vhCjhy4moopDPahC4/KwN4NG6uz+i1h
|
Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
||||||
AB6D5+zn2BjnO/5xMMFGlApWtRNmJVGYlNDj3bXKh2VXzzy03VNeD8kCgYEAzZFL
|
R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
||||||
OlzoRB1HKpTWIECcuvxofMxLOLb3zs0k2t/FYNYIpovmGWCCAULz13y53e5+/+5m
|
tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
||||||
7I9VUZJFaIhaZ36qVBApCKdru69pZMkWCcQO9jELFcx51Ez7OgJWzu7GS1QJCPKC
|
ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
||||||
fEDxI0rZK21j93/Sl/nUnEir7CYpQ+wvCaGuHg8CgYAXgbncfY1+DokwkB6NbHy2
|
lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
||||||
pT4Mfbz6cNGE538w6kQ2I4AeDvmwLentYMqaow478CinegAiflSPTzkHwAemghbr
|
6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
||||||
ZGZPV1UXhn13fJRUG2+eT1hnPVcbXnx223N0k8Bud6qXo65CnyRT/kzcTbcjd5Eh
|
fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
||||||
Hne2daicmMTzynPo9Q72aQKBgBmobO9X8VWvIdbaxO85oVZlctVA2pK1o7CYQmVf
|
9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
||||||
UM+JZ4MCKzI3rYJizPS0iK5+ujNPmmEkcs2/qBIoEsCgOrpLWhPOcc/3UPxXbPzD
|
jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
||||||
D+sCrBOIdhxdj23qJNOnUfDNCGOpgUfpAzAYg4q8GKInvi1h7XukRnEvQi9MJ4LY
|
rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
||||||
P1dZAoGASGcGnTMkmeSXP8ux+dvQJAiJskn/sJIgBZ5uq5GRCeLBUosRSVxM75UK
|
n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
||||||
vAh/c/RBj+pYXVKuPuHGZCQJxsdcRXzXNGouUtgbaYML5Me/Hagt20QzDRBfuGBg
|
yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
||||||
qeZBJaXhjElvw6PUWtg4x+LYRCBpq/bS3LK3ozZrSTukVkKDegw=
|
27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
||||||
|
DO NOT USE==
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
access_token_lifespan: 1h
|
access_token_lifespan: 1h
|
||||||
authorize_code_lifespan: 1m
|
authorize_code_lifespan: 1m
|
||||||
|
@ -120,6 +159,23 @@ It's __strongly recommended__ this is a
|
||||||
[Random Alphanumeric String](../miscellaneous/guides.md#generating-a-random-alphanumeric-string) with 64 or more
|
[Random Alphanumeric String](../miscellaneous/guides.md#generating-a-random-alphanumeric-string) with 64 or more
|
||||||
characters.
|
characters.
|
||||||
|
|
||||||
|
### 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] [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://www.rfc-editor.org/rfc/rfc7517
|
||||||
|
[x5c]: https://www.rfc-editor.org/rfc/rfc7517#section-4.7
|
||||||
|
[x5t]: https://www.rfc-editor.org/rfc/rfc7517#section-4.8
|
||||||
|
|
||||||
|
The first certificate in the chain must have the public key for the [issuer_private_key](#issuer_private_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
|
### issuer_private_key
|
||||||
|
|
||||||
{{< confkey type="string" required="yes" >}}
|
{{< confkey type="string" required="yes" >}}
|
||||||
|
@ -127,10 +183,13 @@ characters.
|
||||||
*__Important Note:__ This can also be defined using a [secret](../methods/secrets.md) which is __strongly recommended__
|
*__Important Note:__ This can also be defined using a [secret](../methods/secrets.md) which is __strongly recommended__
|
||||||
especially for containerized deployments.*
|
especially for containerized deployments.*
|
||||||
|
|
||||||
The private key in DER base64 ([RFC4648]) encoded PEM format used to encrypt the [OpenID Connect] [JWT]'s. The key must
|
The private key in DER base64 ([RFC4648]) encoded PEM format used to sign/encrypt the [OpenID Connect] issued [JWT]'s.
|
||||||
be generated by the administrator and can be done by following the
|
The key must be generated by the administrator and can be done by following the
|
||||||
[Generating an RSA Keypair](../miscellaneous/guides.md#generating-an-rsa-keypair) guide.
|
[Generating an RSA Keypair](../miscellaneous/guides.md#generating-an-rsa-keypair) guide.
|
||||||
|
|
||||||
|
If the [issuer_certificate_chain](#issuer_certificate_chain) is provided the private key must include matching public
|
||||||
|
key data for the first certificate in the chain.
|
||||||
|
|
||||||
### access_token_lifespan
|
### access_token_lifespan
|
||||||
|
|
||||||
{{< confkey type="duration" default="1h" required="no" >}}
|
{{< confkey type="duration" default="1h" required="no" >}}
|
||||||
|
|
|
@ -815,36 +815,79 @@ 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_private_key to sign JWT's. All certificates in the chain must be within the validity period, and every
|
||||||
|
## certificate included must be signed by the certificate immediately after it if provided.
|
||||||
|
# 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-----
|
||||||
|
|
||||||
## 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.
|
||||||
## 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-----
|
||||||
# MXIEogIB$AKCAQEAxZVJP3WF//PG2fLQoEC9DtdiFG/+00vqlbVzz47nyxKONIPI
|
# MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1
|
||||||
# lmL3UdmqpGTKMe/5Brqse4ZAKlQHiDbwzK9ypnfigtHuvh/JO0S7ChP70RC67ed1
|
# T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ
|
||||||
# HV1nyfz5eW3llbtGJPrlYLqITNgctHp6zmRUFtSzPj9qFvozI93LJi492yL1+vu8
|
# KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+
|
||||||
# Un3Dm8+Qq6XM2tPdEcldB/dtBwOWoF+8eOOVsu0TDuB5bwlhBVGJuSAuzBPRS2bF
|
# +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW
|
||||||
# Ga4uk0JDdkDOMCEQxC5uWDFxgfERSMFyfLVWD47woDbuWEBq10c0z+dpWPMp7Ain
|
# LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5
|
||||||
# YnnkqicwCN88Z0zid6MmMQ65F4+9Hc+qC/p6xwIDAQABAoIBAGlhaAHKor+Su3o/
|
# txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um
|
||||||
# AXqXTL5/rbYMzbLQiLt0XeJT69jpeqMTroZXHmWvXE3128mqnf0yzw/K2Ko6yxGh
|
# aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc
|
||||||
# i+j/onya8FqpsVYCCgfsbn2/js1AyRJeIp6Y1ORsYnqbXJnxmkXa80AV/OBPW2/+
|
# Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB
|
||||||
# 60TtSdQrebY3iFPc+i2k+9bPTvpyyDLKlz8UwdZG+k5uyYNIyQTccz+PjwsIvDij
|
# ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr
|
||||||
# 7tKYamhhLN3QXt3/aZTFpjTgezP4WyriZxjWrddHowc47q2rwNS95ND39JcysJAc
|
# LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG
|
||||||
# 0Pcbu8A5lVa7Fx33uOtzDfKWIW7xVEN+OtPgN+FbTjXcXk5IZedl+pW5lU5P++G/
|
# jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5
|
||||||
# ZPvz+WECgYEA9g6HwdODW3e68bOqsFoKg35+vfUFMzlyMF8HFylNVfnLpTEDr637
|
# BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR
|
||||||
# owzMFvcUxVd71b+gV5nnnbI+riUFIgyR8vhCjhy4moopDPahC4/KwN4NG6uz+i1h
|
# Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV
|
||||||
# AB6D5+zn2BjnO/5xMMFGlApWtRNmJVGYlNDj3bXKh2VXzzy03VNeD8kCgYEAzZFL
|
# R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i
|
||||||
# OlzoRB1HKpTWIECcuvxofMxLOLb3zs0k2t/FYNYIpovmGWCCAULz13y53e5+/+5m
|
# tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9
|
||||||
# 7I9VUZJFaIhaZ36qVBApCKdru69pZMkWCcQO9jELFcx51Ez7OgJWzu7GS1QJCPKC
|
# ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR
|
||||||
# fEDxI0rZK21j93/Sl/nUnEir7CYpQ+wvCaGuHg8CgYAXgbncfY1+DokwkB6NbHy2
|
# lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx
|
||||||
# pT4Mfbz6cNGE538w6kQ2I4AeDvmwLentYMqaow478CinegAiflSPTzkHwAemghbr
|
# 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos
|
||||||
# ZGZPV1UXhn13fJRUG2+eT1hnPVcbXnx223N0k8Bud6qXo65CnyRT/kzcTbcjd5Eh
|
# fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ
|
||||||
# Hne2daicmMTzynPo9Q72aQKBgBmobO9X8VWvIdbaxO85oVZlctVA2pK1o7CYQmVf
|
# 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX
|
||||||
# UM+JZ4MCKzI3rYJizPS0iK5+ujNPmmEkcs2/qBIoEsCgOrpLWhPOcc/3UPxXbPzD
|
# jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe
|
||||||
# D+sCrBOIdhxdj23qJNOnUfDNCGOpgUfpAzAYg4q8GKInvi1h7XukRnEvQi9MJ4LY
|
# rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84
|
||||||
# P1dZAoGASGcGnTMkmeSXP8ux+dvQJAiJskn/sJIgBZ5uq5GRCeLBUosRSVxM75UK
|
# n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk
|
||||||
# vAh/c/RBj+pYXVKuPuHGZCQJxsdcRXzXNGouUtgbaYML5Me/Hagt20QzDRBfuGBg
|
# yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ
|
||||||
# qeZBJaXhjElvw6PUWtg4x+LYRCBpq/bS3LK3ozZrSTukVkKDegw=
|
# 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid..
|
||||||
# -----END RSA PRIVATE KEY-----
|
# DO NOT USE==
|
||||||
|
# -----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
## The lifespans configure the expiration for these token types.
|
## The lifespans configure the expiration for these token types.
|
||||||
# access_token_lifespan: 1h
|
# access_token_lifespan: 1h
|
||||||
|
|
|
@ -31,8 +31,9 @@ const (
|
||||||
errFmtSecretIOIssue = "secrets: error loading secret path %s into key '%s': %v"
|
errFmtSecretIOIssue = "secrets: error loading secret path %s into key '%s': %v"
|
||||||
errFmtGenerateConfiguration = "error occurred generating configuration: %+v"
|
errFmtGenerateConfiguration = "error occurred generating configuration: %+v"
|
||||||
|
|
||||||
errFmtDecodeHookCouldNotParse = "could not decode '%s' to a %s: %w"
|
errFmtDecodeHookCouldNotParse = "could not decode '%s' to a %s%s: %w"
|
||||||
errFmtDecodeHookCouldNotParseEmptyValue = "could not decode an empty value to a %s: %w"
|
errFmtDecodeHookCouldNotParseBasic = "could not decode to a %s%s: %w"
|
||||||
|
errFmtDecodeHookCouldNotParseEmptyValue = "could not decode an empty value to a %s%s: %w"
|
||||||
)
|
)
|
||||||
|
|
||||||
var secretSuffixes = []string{"key", "secret", "password", "token"}
|
var secretSuffixes = []string{"key", "secret", "password", "token"}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -23,11 +25,11 @@ func StringToMailAddressHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
kindStr := "mail.Address (RFC5322)"
|
prefixType := ""
|
||||||
|
|
||||||
if t.Kind() == reflect.Ptr {
|
if t.Kind() == reflect.Ptr {
|
||||||
ptr = true
|
ptr = true
|
||||||
kindStr = "*" + kindStr
|
prefixType = "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedType := reflect.TypeOf(mail.Address{})
|
expectedType := reflect.TypeOf(mail.Address{})
|
||||||
|
@ -44,7 +46,7 @@ func StringToMailAddressHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
|
|
||||||
if dataStr != "" {
|
if dataStr != "" {
|
||||||
if result, err = mail.ParseAddress(dataStr); err != nil {
|
if result, err = mail.ParseAddress(dataStr); err != nil {
|
||||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType.String()+" (RFC5322)", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,11 +71,11 @@ func StringToURLHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
kindStr := "url.URL"
|
prefixType := ""
|
||||||
|
|
||||||
if t.Kind() == reflect.Ptr {
|
if t.Kind() == reflect.Ptr {
|
||||||
ptr = true
|
ptr = true
|
||||||
kindStr = "*" + kindStr
|
prefixType = "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedType := reflect.TypeOf(url.URL{})
|
expectedType := reflect.TypeOf(url.URL{})
|
||||||
|
@ -90,7 +92,7 @@ func StringToURLHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
|
|
||||||
if dataStr != "" {
|
if dataStr != "" {
|
||||||
if result, err = url.Parse(dataStr); err != nil {
|
if result, err = url.Parse(dataStr); err != nil {
|
||||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,11 +121,11 @@ func ToTimeDurationHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
kindStr := "time.Duration"
|
prefixType := ""
|
||||||
|
|
||||||
if t.Kind() == reflect.Ptr {
|
if t.Kind() == reflect.Ptr {
|
||||||
ptr = true
|
ptr = true
|
||||||
kindStr = "*" + kindStr
|
prefixType = "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedType := reflect.TypeOf(time.Duration(0))
|
expectedType := reflect.TypeOf(time.Duration(0))
|
||||||
|
@ -141,7 +143,7 @@ func ToTimeDurationHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
dataStr := data.(string)
|
dataStr := data.(string)
|
||||||
|
|
||||||
if result, err = utils.ParseDurationString(dataStr); err != nil {
|
if result, err = utils.ParseDurationString(dataStr); err != nil {
|
||||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType, err)
|
||||||
}
|
}
|
||||||
case f.Kind() == reflect.Int:
|
case f.Kind() == reflect.Int:
|
||||||
seconds := data.(int)
|
seconds := data.(int)
|
||||||
|
@ -176,11 +178,11 @@ func StringToRegexpHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
kindStr := "regexp.Regexp"
|
prefixType := ""
|
||||||
|
|
||||||
if t.Kind() == reflect.Ptr {
|
if t.Kind() == reflect.Ptr {
|
||||||
ptr = true
|
ptr = true
|
||||||
kindStr = "*" + kindStr
|
prefixType = "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedType := reflect.TypeOf(regexp.Regexp{})
|
expectedType := reflect.TypeOf(regexp.Regexp{})
|
||||||
|
@ -197,7 +199,7 @@ func StringToRegexpHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
|
|
||||||
if dataStr != "" {
|
if dataStr != "" {
|
||||||
if result, err = regexp.Compile(dataStr); err != nil {
|
if result, err = regexp.Compile(dataStr); err != nil {
|
||||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +208,7 @@ func StringToRegexpHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
}
|
}
|
||||||
|
|
||||||
if result == nil {
|
if result == nil {
|
||||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseEmptyValue, kindStr, errDecodeNonPtrMustHaveValue)
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseEmptyValue, prefixType, expectedType, errDecodeNonPtrMustHaveValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return *result, nil
|
return *result, nil
|
||||||
|
@ -222,11 +224,11 @@ func StringToAddressHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
kindStr := "Address"
|
prefixType := ""
|
||||||
|
|
||||||
if t.Kind() == reflect.Ptr {
|
if t.Kind() == reflect.Ptr {
|
||||||
ptr = true
|
ptr = true
|
||||||
kindStr = "*" + kindStr
|
prefixType = "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedType := reflect.TypeOf(schema.Address{})
|
expectedType := reflect.TypeOf(schema.Address{})
|
||||||
|
@ -242,7 +244,7 @@ func StringToAddressHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
var result *schema.Address
|
var result *schema.Address
|
||||||
|
|
||||||
if result, err = schema.NewAddressFromString(dataStr); err != nil {
|
if result, err = schema.NewAddressFromString(dataStr); err != nil {
|
||||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ptr {
|
if ptr {
|
||||||
|
@ -252,3 +254,131 @@ func StringToAddressHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
return *result, nil
|
return *result, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringToX509CertificateHookFunc decodes strings to x509.Certificate's.
|
||||||
|
func StringToX509CertificateHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Kind() != reflect.Ptr {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedType := reflect.TypeOf(x509.Certificate{})
|
||||||
|
|
||||||
|
if t.Elem() != expectedType {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStr := data.(string)
|
||||||
|
|
||||||
|
var result *x509.Certificate
|
||||||
|
|
||||||
|
if dataStr == "" {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var i interface{}
|
||||||
|
|
||||||
|
if i, err = utils.ParseX509FromPEM([]byte(dataStr)); err != nil {
|
||||||
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch r := i.(type) {
|
||||||
|
case *x509.Certificate:
|
||||||
|
return r, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, fmt.Errorf("the data is for a %T not a *%s", r, expectedType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToX509CertificateChainHookFunc decodes strings to schema.X509CertificateChain's.
|
||||||
|
func StringToX509CertificateChainHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
|
||||||
|
var ptr bool
|
||||||
|
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixType := ""
|
||||||
|
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
ptr = true
|
||||||
|
prefixType = "*"
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedType := reflect.TypeOf(schema.X509CertificateChain{})
|
||||||
|
|
||||||
|
if ptr && t.Elem() != expectedType {
|
||||||
|
return data, nil
|
||||||
|
} else if !ptr && t != expectedType {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStr := data.(string)
|
||||||
|
|
||||||
|
var result *schema.X509CertificateChain
|
||||||
|
|
||||||
|
if dataStr == "" && ptr {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if result, err = schema.NewX509CertificateChain(dataStr); err != nil {
|
||||||
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, prefixType, expectedType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ptr {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if result == nil {
|
||||||
|
return schema.X509CertificateChain{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return *result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToRSAPrivateKeyHookFunc decodes strings to rsa.PrivateKey's.
|
||||||
|
func StringToRSAPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Kind() != reflect.Ptr {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedType := reflect.TypeOf(rsa.PrivateKey{})
|
||||||
|
|
||||||
|
if t.Elem() != expectedType {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStr := data.(string)
|
||||||
|
|
||||||
|
var result *rsa.PrivateKey
|
||||||
|
|
||||||
|
if dataStr == "" {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var i interface{}
|
||||||
|
|
||||||
|
if i, err = utils.ParseX509FromPEM([]byte(dataStr)); err != nil {
|
||||||
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch r := i.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return r, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, fmt.Errorf("the data is for a %T not a *%s", r, expectedType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
package configuration_test
|
package configuration_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -833,7 +838,7 @@ func TestStringToAddressHookFunc(t *testing.T) {
|
||||||
name: "ShouldFailDecode",
|
name: "ShouldFailDecode",
|
||||||
have: "tcp://&!@^#*&!@#&*@!:2020",
|
have: "tcp://&!@^#*&!@#&*@!:2020",
|
||||||
expected: schema.Address{},
|
expected: schema.Address{},
|
||||||
err: "could not decode 'tcp://&!@^#*&!@#&*@!:2020' to a Address: could not parse string 'tcp://&!@^#*&!@#&*@!:2020' as address: expected format is [<scheme>://]<ip>[:<port>]: parse \"tcp://&!@^\": invalid character \"^\" in host name",
|
err: "could not decode 'tcp://&!@^#*&!@#&*@!:2020' to a schema.Address: could not parse string 'tcp://&!@^#*&!@#&*@!:2020' as address: expected format is [<scheme>://]<ip>[:<port>]: parse \"tcp://&!@^\": invalid character \"^\" in host name",
|
||||||
decode: false,
|
decode: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -862,6 +867,426 @@ func TestStringToAddressHookFunc(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStringToRSAPrivateKeyHookFunc(t *testing.T) {
|
||||||
|
var nilkey *rsa.PrivateKey
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
have interface{}
|
||||||
|
want interface{}
|
||||||
|
err string
|
||||||
|
decode bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeRSAPrivateKey",
|
||||||
|
have: x509PrivateKeyRSA1,
|
||||||
|
want: mustParseRSAPrivateKey(x509PrivateKeyRSA1),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldNotDecodeToECDSAPrivateKey",
|
||||||
|
have: x509PrivateKeyRSA1,
|
||||||
|
want: &ecdsa.PrivateKey{},
|
||||||
|
decode: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldNotDecodeEmptyKey",
|
||||||
|
have: "",
|
||||||
|
want: nilkey,
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldNotDecodeECDSAKeyToRSAKey",
|
||||||
|
have: x509PrivateKeyEC1,
|
||||||
|
want: nilkey,
|
||||||
|
decode: true,
|
||||||
|
err: "could not decode to a *rsa.PrivateKey: the data is for a *ecdsa.PrivateKey not a *rsa.PrivateKey",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldNotDecodeBadRSAPrivateKey",
|
||||||
|
have: x509PrivateKeyRSA2,
|
||||||
|
want: nilkey,
|
||||||
|
decode: true,
|
||||||
|
err: "could not decode to a *rsa.PrivateKey: failed to parse PEM block containing the key",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
hook := configuration.StringToRSAPrivateKeyHookFunc()
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
||||||
|
switch {
|
||||||
|
case !tc.decode:
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tc.have, result)
|
||||||
|
case tc.err == "":
|
||||||
|
assert.NoError(t, err)
|
||||||
|
require.Equal(t, tc.want, result)
|
||||||
|
default:
|
||||||
|
assert.EqualError(t, err, tc.err)
|
||||||
|
assert.Nil(t, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringToX509CertificateHookFunc(t *testing.T) {
|
||||||
|
var nilkey *x509.Certificate
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
have interface{}
|
||||||
|
want interface{}
|
||||||
|
err string
|
||||||
|
decode bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeRSACertificate",
|
||||||
|
have: x509CertificateRSA1,
|
||||||
|
want: mustParseX509Certificate(x509CertificateRSA1),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeECDSACertificate",
|
||||||
|
have: x509CACertificateECDSA,
|
||||||
|
want: mustParseX509Certificate(x509CACertificateECDSA),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeRSACACertificate",
|
||||||
|
have: x509CACertificateRSA,
|
||||||
|
want: mustParseX509Certificate(x509CACertificateRSA),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeECDSACACertificate",
|
||||||
|
have: x509CACertificateECDSA,
|
||||||
|
want: mustParseX509Certificate(x509CACertificateECDSA),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeEmptyCertificateToNil",
|
||||||
|
have: "",
|
||||||
|
want: nilkey,
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldNotDecodeECDSAKeyToCertificate",
|
||||||
|
have: x509PrivateKeyEC1,
|
||||||
|
want: nilkey,
|
||||||
|
decode: true,
|
||||||
|
err: "could not decode to a *x509.Certificate: the data is for a *ecdsa.PrivateKey not a *x509.Certificate",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldNotDecodeBadRSAPrivateKeyToCertificate",
|
||||||
|
have: x509PrivateKeyRSA2,
|
||||||
|
want: nilkey,
|
||||||
|
decode: true,
|
||||||
|
err: "could not decode to a *x509.Certificate: failed to parse PEM block containing the key",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
hook := configuration.StringToX509CertificateHookFunc()
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
||||||
|
switch {
|
||||||
|
case !tc.decode:
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tc.have, result)
|
||||||
|
case tc.err == "":
|
||||||
|
assert.NoError(t, err)
|
||||||
|
require.Equal(t, tc.want, result)
|
||||||
|
default:
|
||||||
|
assert.EqualError(t, err, tc.err)
|
||||||
|
assert.Nil(t, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringToX509CertificateChainHookFunc(t *testing.T) {
|
||||||
|
var nilkey *schema.X509CertificateChain
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
have interface{}
|
||||||
|
expected interface{}
|
||||||
|
err, verr string
|
||||||
|
decode bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeRSACertificate",
|
||||||
|
have: x509CertificateRSA1,
|
||||||
|
expected: mustParseX509CertificateChain(x509CertificateRSA1),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeRSACertificateNoPtr",
|
||||||
|
have: x509CertificateRSA1,
|
||||||
|
expected: *mustParseX509CertificateChain(x509CertificateRSA1),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeRSACertificateChain",
|
||||||
|
have: buildChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||||
|
expected: mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeRSACertificateChainNoPtr",
|
||||||
|
have: buildChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||||
|
expected: *mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldNotDecodeBadRSACertificateChain",
|
||||||
|
have: buildChain(x509CertificateRSA1, x509CACertificateECDSA),
|
||||||
|
expected: mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateECDSA),
|
||||||
|
verr: "certificate #1 in chain is not signed properly by certificate #2 in chain: x509: signature algorithm specifies an RSA public key, but have public key of type *ecdsa.PublicKey",
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeECDSACertificate",
|
||||||
|
have: x509CACertificateECDSA,
|
||||||
|
expected: mustParseX509CertificateChain(x509CACertificateECDSA),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeRSACACertificate",
|
||||||
|
have: x509CACertificateRSA,
|
||||||
|
expected: mustParseX509CertificateChain(x509CACertificateRSA),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeECDSACACertificate",
|
||||||
|
have: x509CACertificateECDSA,
|
||||||
|
expected: mustParseX509CertificateChain(x509CACertificateECDSA),
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeEmptyCertificateToNil",
|
||||||
|
have: "",
|
||||||
|
expected: nilkey,
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldDecodeEmptyCertificateToEmptyStruct",
|
||||||
|
have: "",
|
||||||
|
expected: schema.X509CertificateChain{},
|
||||||
|
decode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldNotDecodeECDSAKeyToCertificate",
|
||||||
|
have: x509PrivateKeyEC1,
|
||||||
|
expected: nilkey,
|
||||||
|
decode: true,
|
||||||
|
err: "could not decode to a *schema.X509CertificateChain: the PEM data chain contains a EC PRIVATE KEY but only certificates are expected",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldNotDecodeBadRSAPrivateKeyToCertificate",
|
||||||
|
have: x509PrivateKeyRSA2,
|
||||||
|
expected: nilkey,
|
||||||
|
decode: true,
|
||||||
|
err: "could not decode to a *schema.X509CertificateChain: invalid PEM block",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
hook := configuration.StringToX509CertificateChainHookFunc()
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
|
actual, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.expected), tc.have)
|
||||||
|
switch {
|
||||||
|
case !tc.decode:
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tc.have, actual)
|
||||||
|
case tc.err == "":
|
||||||
|
assert.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expected, actual)
|
||||||
|
|
||||||
|
if tc.expected == nilkey {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch chain := actual.(type) {
|
||||||
|
case *schema.X509CertificateChain:
|
||||||
|
require.NotNil(t, chain)
|
||||||
|
if tc.verr == "" {
|
||||||
|
assert.NoError(t, chain.Validate())
|
||||||
|
} else {
|
||||||
|
assert.EqualError(t, chain.Validate(), tc.verr)
|
||||||
|
}
|
||||||
|
case schema.X509CertificateChain:
|
||||||
|
require.NotNil(t, chain)
|
||||||
|
if tc.verr == "" {
|
||||||
|
assert.NoError(t, chain.Validate())
|
||||||
|
} else {
|
||||||
|
assert.EqualError(t, chain.Validate(), tc.verr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assert.EqualError(t, err, tc.err)
|
||||||
|
assert.Nil(t, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
x509PrivateKeyRSA1 = `
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEA6z1LOg1ZCqb0lytXWZ+MRBpMHEXOoTOLYgfZXt1IYyE3Z758
|
||||||
|
cyalk0NYQhY5cZDsXPYWPvAHiPMUxutWkoxFwby56S+AbIMa3/Is+ILrHRJs8Exn
|
||||||
|
ZkpyrYFxPX12app2kErdmAkHSx0Z5/kuXiz96PHs8S8/ZbyZolLHzdfLtSzjvRm5
|
||||||
|
Zue5iFzsf19NJz5CIBfv8g5lRwtE8wNJoRSpn1xq7fqfuA0weDNFPzjlNWRLy6aa
|
||||||
|
rK7qJexRkmkCs4sLgyl+9NODYJpvmN8E1yhyC27E0joI6rBFVW7Ihv+cSPCdDzGp
|
||||||
|
EWe81x3AeqAa3mjVqkiq4u4Z2i8JDgBaPboqJwIDAQABAoIBAAFdLZ58jVOefDSU
|
||||||
|
L8F5R1rtvBs93GDa56f926jNJ6pLewLC+/2+757W+SAI+PRLntM7Kg3bXm/Q2QH+
|
||||||
|
Q1Y+MflZmspbWCdI61L5GIGoYKyeers59i+FpvySj5GHtLQRiTZ0+Kv1AXHSDWBm
|
||||||
|
9XneUOqU3IbZe0ifu1RRno72/VtjkGXbW8Mkkw+ohyGbIeTx/0/JQ6sSNZTT3Vk7
|
||||||
|
8i4IXptq3HSF0/vqZuah8rShoeNq72pD1YLM9YPdL5by1QkDLnqATDiCpLBTCaNV
|
||||||
|
I8sqYEun+HYbQzBj8ZACG2JVZpEEidONWQHw5BPWO95DSZYrVnEkuCqeH+u5vYt7
|
||||||
|
CHuJ3AECgYEA+W3v5z+j91w1VPHS0VB3SCDMouycAMIUnJPAbt+0LPP0scUFsBGE
|
||||||
|
hPAKddC54pmMZRQ2KIwBKiyWfCrJ8Xz8Yogn7fJgmwTHidJBr2WQpIEkNGlK3Dzi
|
||||||
|
jXL2sh0yC7sHvn0DqiQ79l/e7yRbSnv2wrTJEczOOH2haD7/tBRyCYECgYEA8W+q
|
||||||
|
E9YyGvEltnPFaOxofNZ8LHVcZSsQI5b6fc0iE7fjxFqeXPXEwGSOTwqQLQRiHn9b
|
||||||
|
CfPmIG4Vhyq0otVmlPvUnfBZ2OK+tl5X2/mQFO3ROMdvpi0KYa994uqfJdSTaqLn
|
||||||
|
jjoKFB906UFHnDQDLZUNiV1WwnkTglgLc+xrd6cCgYEAqqthyv6NyBTM3Tm2gcio
|
||||||
|
Ra9Dtntl51LlXZnvwy3IkDXBCd6BHM9vuLKyxZiziGx+Vy90O1xI872cnot8sINQ
|
||||||
|
Am+dur/tAEVN72zxyv0Y8qb2yfH96iKy9gxi5s75TnOEQgAygLnYWaWR2lorKRUX
|
||||||
|
bHTdXBOiS58S0UzCFEslGIECgYBqkO4SKWYeTDhoKvuEj2yjRYyzlu28XeCWxOo1
|
||||||
|
otiauX0YSyNBRt2cSgYiTzhKFng0m+QUJYp63/wymB/5C5Zmxi0XtWIDADpLhqLj
|
||||||
|
HmmBQ2Mo26alQ5YkffBju0mZyhVzaQop1eZi8WuKFV1FThPlB7hc3E0SM5zv2Grd
|
||||||
|
tQnOWwKBgQC40yZY0PcjuILhy+sIc0Wvh7LUA7taSdTye149kRvbvsCDN7Jh75lM
|
||||||
|
USjhLXY0Nld2zBm9r8wMb81mXH29uvD+tDqqsICvyuKlA/tyzXR+QTr7dCVKVwu0
|
||||||
|
1YjCJ36UpTsLre2f8nOSLtNmRfDPtbOE2mkOoO9dD9UU0XZwnvn9xw==
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
|
||||||
|
x509PrivateKeyRSA2 = `
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
bad key
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
|
||||||
|
x509PrivateKeyEC1 = `
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIMn970LSn8aKVhBM4vyUmpZyEdCT4riN+Lp4QU04zUhYoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEMD69n22nd78GmaRDzy/s7muqhbc/OEnFS2mNtiRAA5FaX+kbkCB5
|
||||||
|
8pu/k2jkaSVNZtBYKPVAibHkhvakjVb66A==
|
||||||
|
-----END EC PRIVATE KEY-----`
|
||||||
|
|
||||||
|
x509CertificateRSA1 = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC5TCCAc2gAwIBAgIQfBUmKLmEvMqS6S9auKCY2DANBgkqhkiG9w0BAQsFADAT
|
||||||
|
MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMjA5MDgxMDA5MThaFw0yMzA5MDgxMDA5
|
||||||
|
MThaMBMxETAPBgNVBAoTCEF1dGhlbGlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
|
MIIBCgKCAQEApqno1cOpDcgKmOJqeDQGIGH5/ZnqcJ4xud6eOUfbDqel3b0RkAQX
|
||||||
|
mFYWEDO/PDOAOjYk/xSwZGo3jDofOHGhrKstQqLdweHGfme5NXYHJda7nGv/OY5q
|
||||||
|
zUuEG4xBVgUsvbshWZ18H+bIQpwiP6tDAabxc0B7J15F1pArK8QN4pDTfsqZDwMi
|
||||||
|
Qyo638XfUbDzEVZRbdDKxHz5g0w2vFdXon8uOxRRb0+zlHF9nM4PiESNgiUIYeua
|
||||||
|
8Q5yP10SY2k9zlQ/OFJ4XhQmioCJvNjJE/TSc5/ECub2n7hTZhN5TGKagukZ5NAy
|
||||||
|
KgbvNYW+CN+H4pFJt/9WptiDfBqhlUvjnwIDAQABozUwMzAOBgNVHQ8BAf8EBAMC
|
||||||
|
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0B
|
||||||
|
AQsFAAOCAQEAH9veGzfWqXxsa5s2KHV2Jzed9V8KSs1Qy9QKRez1i2OMvMPh2DRM
|
||||||
|
RLzAAp/XigjxLQF/LFXuoFW0Qg8BRb44iRgZrCiqVOUnd3xTrS/CcFExnpQI4F12
|
||||||
|
/U70o97rkTonCOHmUUW6vQfWSXR/GU3/faRLJjiqcpWLZhTQNrnsip1ym3B2NMdk
|
||||||
|
gMKkT8Acx1DX48MvTE4+DyqCS8TlJbacBJ2RFFELKu3jYnVNyrb0ywLxoCtWqBBE
|
||||||
|
veVj+VMn9hNY1u5uydLsUDOlT5QyQcEuUzjjdhsJKEgDE5daNtB2OJJnd9IOMzUA
|
||||||
|
hasPZETCCKabTpWiEPw1Cn/ZRqya0SZqFg==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
`
|
||||||
|
|
||||||
|
x509CACertificateRSA = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDBDCCAeygAwIBAgIRAJfz0dHS9UkDngE55lUPdu4wDQYJKoZIhvcNAQELBQAw
|
||||||
|
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNMjIwOTA4MDk1OTI1WhcNMjMwOTA4MDk1
|
||||||
|
OTI1WjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||||
|
ADCCAQoCggEBALfivbwq9r5X8N+NSbNHVuKbCb9f9vD5Xw2pOjSVvVjFkWQ1YKJu
|
||||||
|
JGx9yskhHBZTBt76cInipA+0PqCBrBrjij1lh2StvzRVuQwgFG6H01LxBPi0JyYv
|
||||||
|
Is94F6PHr6fSBgFWB5GNQ797KQIOdIr057uEFbp0eBMxxqiQ9gdyD0HPretrx1Uy
|
||||||
|
kHuF6jck958combn9luHW0i53mt8706j7UAhxFqu9YUeklTM1VqUiRm5+nJKIdNA
|
||||||
|
LiDMGVAuoxjhF6aIgY0yh5mL5mKtYYzhtA8WryrMzBgFRUGzHCSI1TNisA8wSf2T
|
||||||
|
Z2JhbFHrFPR5fiSqAEHok3UXu++wsfl/lisCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
||||||
|
AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
||||||
|
OSXG42bCPNuWeP0ahScUMVjxe/wwDQYJKoZIhvcNAQELBQADggEBAFRnubHiYy1H
|
||||||
|
PGODKA++UY9eAmmaCJxzuWpY8FY9fBz8/VBzcp8vaURPmmQ/34QcqfaSHDM2jIaL
|
||||||
|
dQ2o9Ae5NjbRzLB6a5DcVO50oHG4BHP1ix4Bt3POr8J80KgA9pOIyAQqbAlFBSzQ
|
||||||
|
l9yrzVULyf+qpUmByRf5qy2kQJOBfMJbn5j+BprWKwbcI8OAZWWSLItTXqJDrFTk
|
||||||
|
OMZK4wZ6KiZM07KWMlwW/CE0QRzDk5MXfbwRt4D8pyx6rGKqI7QRusjm5osIpHZV
|
||||||
|
26FdBdBvEhq4i8UsmDsQqH3iMY1AKmojZToZb5rStOZWHO/BZZ7nT2bscNjwm0E8
|
||||||
|
6E2l6czk8ss=
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
x509CACertificateECDSA = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBdzCCAR2gAwIBAgIQUzb62irYb/7B2H0c1AbriDAKBggqhkjOPQQDAjATMREw
|
||||||
|
DwYDVQQKEwhBdXRoZWxpYTAeFw0yMjA5MDgxMDEzNDZaFw0yMzA5MDgxMDEzNDZa
|
||||||
|
MBMxETAPBgNVBAoTCEF1dGhlbGlhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
||||||
|
b/EiIBpmifCI34JdI7luetygue2rTtoNH0QXhtrjMuZNugT29LUz+DobZQxvGsOY
|
||||||
|
4TXzAQXq4gnTb7enNWFgsaNTMFEwDgYDVR0PAQH/BAQDAgKkMA8GA1UdJQQIMAYG
|
||||||
|
BFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUxlDPBKHKawuvhtQTN874
|
||||||
|
TeCEKjkwCgYIKoZIzj0EAwIDSAAwRQIgAQeV01FZ/VkSERwaRKTeXAXxmKyc/05O
|
||||||
|
uDv6M2spMi0CIQC8uOSMcv11vp1ylsGg38N6XYA+GQa1BHRd79+91hC+7w==
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
)
|
||||||
|
|
||||||
|
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 mustParseX509Certificate(data string) *x509.Certificate {
|
||||||
|
block, _ := pem.Decode([]byte(data))
|
||||||
|
if block == nil || len(block.Bytes) == 0 {
|
||||||
|
panic("not a PEM")
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cert
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildChain(pems ...string) string {
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
|
||||||
|
for i, data := range pems {
|
||||||
|
if i != 0 {
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustParseX509CertificateChain(datas ...string) *schema.X509CertificateChain {
|
||||||
|
chain, err := schema.NewX509CertificateChain(buildChain(datas...))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain
|
||||||
|
}
|
||||||
|
|
||||||
func testInt32Ptr(i int32) *int32 {
|
func testInt32Ptr(i int32) *int32 {
|
||||||
return &i
|
return &i
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,9 @@ func unmarshal(ko *koanf.Koanf, val *schema.StructValidator, path string, o inte
|
||||||
StringToURLHookFunc(),
|
StringToURLHookFunc(),
|
||||||
StringToRegexpHookFunc(),
|
StringToRegexpHookFunc(),
|
||||||
StringToAddressHookFunc(),
|
StringToAddressHookFunc(),
|
||||||
|
StringToX509CertificateHookFunc(),
|
||||||
|
StringToX509CertificateChainHookFunc(),
|
||||||
|
StringToRSAPrivateKeyHookFunc(),
|
||||||
ToTimeDurationHookFunc(),
|
ToTimeDurationHookFunc(),
|
||||||
),
|
),
|
||||||
Metadata: nil,
|
Metadata: nil,
|
||||||
|
|
|
@ -48,7 +48,7 @@ func TestShouldErrorSecretNotExist(t *testing.T) {
|
||||||
|
|
||||||
errFmt := utils.GetExpectedErrTxt("filenotfound")
|
errFmt := utils.GetExpectedErrTxt("filenotfound")
|
||||||
|
|
||||||
// ignore the errors before this as they are checked by the valdator.
|
// ignore the errors before this as they are checked by the validator.
|
||||||
assert.EqualError(t, errs[0], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "authentication"), "authentication_backend.ldap.password", fmt.Sprintf(errFmt, filepath.Join(dir, "authentication"))))
|
assert.EqualError(t, errs[0], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "authentication"), "authentication_backend.ldap.password", fmt.Sprintf(errFmt, filepath.Join(dir, "authentication"))))
|
||||||
assert.EqualError(t, errs[1], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "duo"), "duo_api.secret_key", fmt.Sprintf(errFmt, filepath.Join(dir, "duo"))))
|
assert.EqualError(t, errs[1], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "duo"), "duo_api.secret_key", fmt.Sprintf(errFmt, filepath.Join(dir, "duo"))))
|
||||||
assert.EqualError(t, errs[2], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "jwt"), "jwt_secret", fmt.Sprintf(errFmt, filepath.Join(dir, "jwt"))))
|
assert.EqualError(t, errs[2], fmt.Sprintf(errFmtSecretIOIssue, filepath.Join(dir, "jwt"), "jwt_secret", fmt.Sprintf(errFmt, filepath.Join(dir, "jwt"))))
|
||||||
|
|
|
@ -57,3 +57,8 @@ const (
|
||||||
// regexpHasScheme checks if a string has a scheme. Valid characters for schemes include alphanumeric, hyphen,
|
// regexpHasScheme checks if a string has a scheme. Valid characters for schemes include alphanumeric, hyphen,
|
||||||
// period, and plus characters.
|
// period, and plus characters.
|
||||||
var regexpHasScheme = regexp.MustCompile(`^[-+.a-zA-Z\d]+://`)
|
var regexpHasScheme = regexp.MustCompile(`^[-+.a-zA-Z\d]+://`)
|
||||||
|
|
||||||
|
const (
|
||||||
|
blockCERTIFICATE = "CERTIFICATE"
|
||||||
|
blockRSAPRIVATEKEY = "RSA PRIVATE KEY"
|
||||||
|
)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package schema
|
package schema
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -12,8 +13,9 @@ 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"`
|
||||||
IssuerPrivateKey string `koanf:"issuer_private_key"`
|
IssuerCertificateChain X509CertificateChain `koanf:"issuer_certificate_chain"`
|
||||||
|
IssuerPrivateKey *rsa.PrivateKey `koanf:"issuer_private_key"`
|
||||||
|
|
||||||
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"`
|
||||||
|
|
|
@ -18,6 +18,7 @@ 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_certificate_chain",
|
||||||
"identity_providers.oidc.issuer_private_key",
|
"identity_providers.oidc.issuer_private_key",
|
||||||
"identity_providers.oidc.access_token_lifespan",
|
"identity_providers.oidc.access_token_lifespan",
|
||||||
"identity_providers.oidc.authorize_code_lifespan",
|
"identity_providers.oidc.authorize_code_lifespan",
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
package schema
|
package schema
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewAddressFromString returns an *Address and error depending on the ability to parse the string as an Address.
|
// NewAddressFromString returns an *Address and error depending on the ability to parse the string as an Address.
|
||||||
|
@ -99,3 +106,161 @@ func (a Address) HostPort() string {
|
||||||
func (a Address) Listener() (net.Listener, error) {
|
func (a Address) Listener() (net.Listener, error) {
|
||||||
return net.Listen(a.Scheme, a.HostPort())
|
return net.Listen(a.Scheme, a.HostPort())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewX509CertificateChain creates a new *X509CertificateChain from a given string, parsing each PEM block one by one.
|
||||||
|
func NewX509CertificateChain(in string) (chain *X509CertificateChain, err error) {
|
||||||
|
if in == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
chain = &X509CertificateChain{
|
||||||
|
certs: []*x509.Certificate{},
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []byte(in)
|
||||||
|
|
||||||
|
var (
|
||||||
|
block *pem.Block
|
||||||
|
cert *x509.Certificate
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
block, data = pem.Decode(data)
|
||||||
|
|
||||||
|
if block == nil || len(block.Bytes) == 0 {
|
||||||
|
return nil, fmt.Errorf("invalid PEM block")
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.Type != blockCERTIFICATE {
|
||||||
|
return nil, fmt.Errorf("the PEM data chain contains a %s but only certificates are expected", block.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cert, err = x509.ParseCertificate(block.Bytes); err != nil {
|
||||||
|
return nil, fmt.Errorf("the PEM data chain contains an invalid certificate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.certs = append(chain.certs, cert)
|
||||||
|
|
||||||
|
if len(data) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// X509CertificateChain is a helper struct that holds a list of *x509.Certificate's.
|
||||||
|
type X509CertificateChain struct {
|
||||||
|
certs []*x509.Certificate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thumbprint returns the Thumbprint for the first certificate.
|
||||||
|
func (c *X509CertificateChain) Thumbprint(hash crypto.Hash) []byte {
|
||||||
|
if len(c.certs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
h := hash.New()
|
||||||
|
|
||||||
|
h.Write(c.certs[0].Raw)
|
||||||
|
|
||||||
|
return h.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasCertificates returns true if the chain has any certificates.
|
||||||
|
func (c *X509CertificateChain) HasCertificates() (has bool) {
|
||||||
|
return len(c.certs) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal checks if the provided *x509.Certificate is equal to the first *x509.Certificate in the chain.
|
||||||
|
func (c *X509CertificateChain) Equal(other *x509.Certificate) (equal bool) {
|
||||||
|
if len(c.certs) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.certs[0].Equal(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EqualKey checks if the provided key (public or private) has a public key equal to the first public key in this chain.
|
||||||
|
//
|
||||||
|
//nolint:gocyclo // This is an adequately clear function even with the complexity.
|
||||||
|
func (c *X509CertificateChain) EqualKey(other any) (equal bool) {
|
||||||
|
if len(c.certs) == 0 || other == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch key := other.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
return key.Equal(c.certs[0].PublicKey)
|
||||||
|
case rsa.PublicKey:
|
||||||
|
return key.Equal(c.certs[0].PublicKey)
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return key.PublicKey.Equal(c.certs[0].PublicKey)
|
||||||
|
case rsa.PrivateKey:
|
||||||
|
return key.PublicKey.Equal(c.certs[0].PublicKey)
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
return key.Equal(c.certs[0].PublicKey)
|
||||||
|
case ecdsa.PublicKey:
|
||||||
|
return key.Equal(c.certs[0].PublicKey)
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
return key.PublicKey.Equal(c.certs[0].PublicKey)
|
||||||
|
case ecdsa.PrivateKey:
|
||||||
|
return key.PublicKey.Equal(c.certs[0].PublicKey)
|
||||||
|
case *ed25519.PublicKey:
|
||||||
|
return key.Equal(c.certs[0].PublicKey)
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
return key.Equal(c.certs[0].PublicKey)
|
||||||
|
case *ed25519.PrivateKey:
|
||||||
|
switch pub := key.Public().(type) {
|
||||||
|
case *ed25519.PublicKey:
|
||||||
|
return pub.Equal(c.certs[0].PublicKey)
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
return pub.Equal(c.certs[0].PublicKey)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case ed25519.PrivateKey:
|
||||||
|
switch pub := key.Public().(type) {
|
||||||
|
case *ed25519.PublicKey:
|
||||||
|
return pub.Equal(c.certs[0].PublicKey)
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
return pub.Equal(c.certs[0].PublicKey)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Certificates for this X509CertificateChain.
|
||||||
|
func (c *X509CertificateChain) Certificates() []*x509.Certificate {
|
||||||
|
return c.certs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the X509CertificateChain ensuring the certificates were provided in the correct order
|
||||||
|
// (with nth being signed by the nth+1), and that all of the certificates are valid based on the current time.
|
||||||
|
func (c *X509CertificateChain) Validate() (err error) {
|
||||||
|
n := len(c.certs)
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for i, cert := range c.certs {
|
||||||
|
if !cert.NotBefore.IsZero() && cert.NotBefore.After(now) {
|
||||||
|
return fmt.Errorf("certificate #%d in chain is invalid before %d but the time is %d", i+1, cert.NotBefore.Unix(), now.Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
if cert.NotAfter.Before(now) {
|
||||||
|
return fmt.Errorf("certificate #%d in chain is invalid after %d but the time is %d", i+1, cert.NotAfter.Unix(), now.Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
if i+1 >= n {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cert.CheckSignatureFrom(c.certs[i+1]); err != nil {
|
||||||
|
return fmt.Errorf("certificate #%d in chain is not signed properly by certificate #%d in chain: %w", i+1, i+2, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
package schema
|
package schema
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewAddressFromString(t *testing.T) {
|
func TestNewAddressFromString(t *testing.T) {
|
||||||
|
@ -48,3 +55,499 @@ func TestNewAddressFromString(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewX509CertificateChain(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have string
|
||||||
|
thumbprintSHA256 string
|
||||||
|
err string
|
||||||
|
}{
|
||||||
|
{"ShouldParseCertificate", x509CertificateRSA,
|
||||||
|
"956e53c127b18143241fb75d2149369d41ad1e4094c40cedcef22e468b37886b", ""},
|
||||||
|
{"ShouldParseCertificateChain", x509CertificateRSA + "\n" + x509CACertificateRSA,
|
||||||
|
"956e53c127b18143241fb75d2149369d41ad1e4094c40cedcef22e468b37886b", ""},
|
||||||
|
{"ShouldNotParseInvalidCertificate", x509CertificateRSAInvalid, "",
|
||||||
|
"the PEM data chain contains an invalid certificate: x509: malformed certificate"},
|
||||||
|
{"ShouldNotParseInvalidCertificateBlock", x509CertificateRSAInvalidBlock, "", "invalid PEM block"},
|
||||||
|
{"ShouldNotParsePrivateKey", x509PrivateKeyRSA, "",
|
||||||
|
"the PEM data chain contains a RSA PRIVATE KEY but only certificates are expected"},
|
||||||
|
{"ShouldNotParseEmptyPEMBlock", x509CertificateEmpty, "", "invalid PEM block"},
|
||||||
|
{"ShouldNotParseEmptyData", "", "", ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
actual, err := NewX509CertificateChain(tc.have)
|
||||||
|
|
||||||
|
switch len(tc.err) {
|
||||||
|
case 0:
|
||||||
|
switch len(tc.have) {
|
||||||
|
case 0:
|
||||||
|
assert.Nil(t, actual)
|
||||||
|
default:
|
||||||
|
assert.NotNil(t, actual)
|
||||||
|
|
||||||
|
assert.Equal(t, tc.thumbprintSHA256, fmt.Sprintf("%x", actual.Thumbprint(crypto.SHA256)))
|
||||||
|
assert.True(t, actual.HasCertificates())
|
||||||
|
}
|
||||||
|
assert.NoError(t, err)
|
||||||
|
default:
|
||||||
|
assert.Nil(t, actual)
|
||||||
|
assert.EqualError(t, err, tc.err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestX509CertificateChain(t *testing.T) {
|
||||||
|
chain := &X509CertificateChain{}
|
||||||
|
|
||||||
|
assert.Nil(t, chain.Thumbprint(crypto.SHA256))
|
||||||
|
assert.False(t, chain.HasCertificates())
|
||||||
|
assert.Len(t, chain.Certificates(), 0)
|
||||||
|
|
||||||
|
assert.False(t, chain.Equal(nil))
|
||||||
|
assert.False(t, chain.Equal(&x509.Certificate{}))
|
||||||
|
|
||||||
|
assert.False(t, chain.EqualKey(nil))
|
||||||
|
assert.False(t, chain.EqualKey(&rsa.PrivateKey{}))
|
||||||
|
|
||||||
|
cert := MustParseCertificate(x509CertificateRSA)
|
||||||
|
cacert := MustParseCertificate(x509CACertificateRSA)
|
||||||
|
|
||||||
|
chain = MustParseX509CertificateChain(x509CertificateRSA + "\n" + x509CACertificateRSA)
|
||||||
|
key := MustParseRSAPrivateKey(x509PrivateKeyRSA)
|
||||||
|
|
||||||
|
thumbprint := chain.Thumbprint(crypto.SHA256)
|
||||||
|
assert.NotNil(t, thumbprint)
|
||||||
|
assert.Equal(t, "956e53c127b18143241fb75d2149369d41ad1e4094c40cedcef22e468b37886b", fmt.Sprintf("%x", thumbprint))
|
||||||
|
|
||||||
|
assert.True(t, chain.Equal(cert))
|
||||||
|
assert.False(t, chain.Equal(cacert))
|
||||||
|
assert.True(t, chain.EqualKey(key))
|
||||||
|
|
||||||
|
assert.NoError(t, chain.Validate())
|
||||||
|
|
||||||
|
chain = MustParseX509CertificateChain(x509CertificateRSA + "\n" + x509CertificateRSA)
|
||||||
|
assert.EqualError(t, chain.Validate(), "certificate #1 in chain is not signed properly by certificate #2 in chain: x509: invalid signature: parent certificate cannot sign this kind of certificate")
|
||||||
|
|
||||||
|
chain = MustParseX509CertificateChain(x509CertificateRSAExpired + "\n" + x509CACertificateRSAExpired)
|
||||||
|
|
||||||
|
err := chain.Validate()
|
||||||
|
require.NotNil(t, err)
|
||||||
|
assert.Regexp(t, regexp.MustCompile(`^certificate #1 in chain is invalid after 31536000 but the time is \d+$`), err.Error())
|
||||||
|
|
||||||
|
chain = MustParseX509CertificateChain(x509CertificateRSANotBefore + "\n" + x509CACertificateRSAotBefore)
|
||||||
|
|
||||||
|
err = chain.Validate()
|
||||||
|
require.NotNil(t, err)
|
||||||
|
assert.Regexp(t, regexp.MustCompile(`^certificate #1 in chain is invalid before 13569465600 but the time is \d+$`), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustParseX509CertificateChain(data string) *X509CertificateChain {
|
||||||
|
chain, err := NewX509CertificateChain(data)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if chain == nil {
|
||||||
|
panic("nil chain")
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustParseCertificate(data string) *x509.Certificate {
|
||||||
|
block, x := pem.Decode([]byte(data))
|
||||||
|
if block == nil {
|
||||||
|
panic("not pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(x) != 0 {
|
||||||
|
panic("extra data")
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.Type != blockCERTIFICATE {
|
||||||
|
panic(fmt.Sprintf("not certifiate block: %s", block.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cert
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
||||||
|
block, x := pem.Decode([]byte(data))
|
||||||
|
if block == nil {
|
||||||
|
panic("not pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(x) != 0 {
|
||||||
|
panic("extra data")
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.Type != blockRSAPRIVATEKEY {
|
||||||
|
panic(fmt.Sprintf("not rsa private key block: %s", block.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Valid from 2022 to 2122 (years).
|
||||||
|
x509CertificateRSA = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC6DCCAdCgAwIBAgIRAIxvm0gFgsbh3D22rSZLuFQwDQYJKoZIhvcNAQELBQAw
|
||||||
|
EzERMA8GA1UEChMIQXV0aGVsaWEwIBcNMjIxMDAyMDAzMDQyWhgPMjEyMjA5MDgw
|
||||||
|
MDMwNDJaMBMxETAPBgNVBAoTCEF1dGhlbGlhMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||||
|
AQ8AMIIBCgKCAQEAy71EOkV3jOpVQtVTH5HYcI4PryUCiAEyxAIuO+66gaAa4aCd
|
||||||
|
UCRr8iO/pt5nOwPxjPo+hMHhkcKpX7evj+wgYXAccpIQFSCYWTJkaXFL0jL7yFuE
|
||||||
|
5xpjgRM/x6FfK0IbN5WmVWO9EjesbyMCyDoYpjwzIrxnB70F9Y0nrXst1SnW/Sy0
|
||||||
|
01BQZNzD1tky1KDvEkw7L5mMPZFZMr5wV+ELvbo1LLvvrGYhhzbXWk7pPbxT0gAa
|
||||||
|
7yVvQbDKuCDqssAUyQa2JdlDaQocpldtK6l+dc3IsSWKd2UMouta75ngr9E1igy3
|
||||||
|
t7owMRqH8NjwKHt6KQeDVSdBnWNjG572vaRimQIDAQABozUwMzAOBgNVHQ8BAf8E
|
||||||
|
BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADANBgkqhkiG
|
||||||
|
9w0BAQsFAAOCAQEAaZJ09yGa+DGr/iTqshGdPKNCtcf/CXCkL52xiI7DzLxDt30P
|
||||||
|
8vCuXXrrTGBY7eWYupcNy/MyqaUrz1ED+map3nQzZQBJ9vWIfr01B9phkg/WSaNJ
|
||||||
|
1DlYtbPYzr86BlGP1V5d3Wv6JqF3tkWHI0kI38CT68fWdDKrfa5j3JdZGIVJW+51
|
||||||
|
U0IE3Nqhfc76YzwQ3sNX5FT2Fr55RowH+l5OBPk0Bcztq58XmyPR/bvPfDASt8iS
|
||||||
|
DBT+0iiDiwk6LvOkasL8p7nuh5Grc9LMEYXY/QMUbkIWhIVRFlqyJA9s8vGHx1D4
|
||||||
|
96iYKudj+yvO17Szzr/NNmcwETbCs4j6P6QeiA==
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
// Private Key for x509CertificateRSA.
|
||||||
|
x509PrivateKeyRSA = `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAy71EOkV3jOpVQtVTH5HYcI4PryUCiAEyxAIuO+66gaAa4aCd
|
||||||
|
UCRr8iO/pt5nOwPxjPo+hMHhkcKpX7evj+wgYXAccpIQFSCYWTJkaXFL0jL7yFuE
|
||||||
|
5xpjgRM/x6FfK0IbN5WmVWO9EjesbyMCyDoYpjwzIrxnB70F9Y0nrXst1SnW/Sy0
|
||||||
|
01BQZNzD1tky1KDvEkw7L5mMPZFZMr5wV+ELvbo1LLvvrGYhhzbXWk7pPbxT0gAa
|
||||||
|
7yVvQbDKuCDqssAUyQa2JdlDaQocpldtK6l+dc3IsSWKd2UMouta75ngr9E1igy3
|
||||||
|
t7owMRqH8NjwKHt6KQeDVSdBnWNjG572vaRimQIDAQABAoIBAA/EhhM8bRQqzo5t
|
||||||
|
lBFNaELNu8kCRD/iV9tzj8BzqVt+2JW9qG8bYn9K5Po1HCglFfyjIVOE7cAqIJGX
|
||||||
|
1a59x8PCuXDkfPolm6TLkZnXeta5u2K2MoLwN+M1aio5AvSGGTUkD8tr/KX8SQwQ
|
||||||
|
2ZZFaML0xcBadF7U8jEey4NRlSp5/voiIAB+FrJHepZBz2XJYCX5s2vYLPMn+51R
|
||||||
|
1HyO0n2aQ9H1Na8aBjTfAp9GDKJWBV3bSM7cVaLGlMFj/HNXUNVnSsVsJj0tdWKz
|
||||||
|
K6r9zPskLnS+eNjCgqrOtZSqJ7M3PL0/PoTFPrr1Fevr+soKWCaPF94Ib94O9SEq
|
||||||
|
scvP3kECgYEA0HBdGab0HjcZgFtsIaMm+eBcDhUmUrvMPUw6FmspKnc8wplscONW
|
||||||
|
wrDGhR0dpT8+aAMD5jFC2pvyHjI5AWkW+53LB15j6SVzUlUMfS3VTwE2prLtDHDs
|
||||||
|
nCDW2+fXY2kjv45efZGpMGbLJVePx2RCPzUlAlc14lzxnHgpo7eho1cCgYEA+jpi
|
||||||
|
Eo/Jqa5CNd4hrXqFxZTFtU2Mn38ZKI3QK/l47/347yHLebjsYIIwJRoHenxWxNlz
|
||||||
|
Y+BZ38vkP+f9BGAVGiRcyMmIJU0X305wKwl26Y2Q/tEm2OpwmDboD2pL9byi9vfY
|
||||||
|
bz7pQGK/l9j86KofRwVJJRLsofPI1SsjnC8c448CgYAkpg0IjJ1RjriSJADwLSKW
|
||||||
|
PseQxlE1rMVtZbC07mSPjeWGBbnWY3KGytQs5YCn5GXRne4alEC/9Tlt68CwKc0b
|
||||||
|
spPXGNaSUL5lFIUcoWlm+bylNMKPNG+1x+RfR/VMCll5vcuJYooP85L2Xt3t3gfz
|
||||||
|
2yFFtxXHVjY5H7uaiJgIAwKBgQDvkGXEj5TqtsL8/6YOiHb6Kuz+Hzi6mtxjTyI2
|
||||||
|
d6mpWuWxTBGaf8kOvJWLb9gpFFGeNPGcdXaWJIZqCJjcT4Dkflu2f/uwepaYXGhX
|
||||||
|
S8Bk6fwfee5PTmRt1mNmHsaKhgcfmznDh9+YnPIBVuULe5RmUlEtBWk3xEZKj/qP
|
||||||
|
1Ss7UQKBgAwZQz+h5Z/XOJH3Qs5nJBKAZUiYkj3ux7G6tjx0cz7XcUYd/6enBpkY
|
||||||
|
JeqVHB6G+bMRLwb+Hc5Vgpbd5GdaUWo8udaghHgSGPUVcn0lK38XhYek6ACGz7Lo
|
||||||
|
xEfgtKoBlUq+uPb8H05HY0t9KybA3LA5wkRYYnJ17/nkZtrrJAmX
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
|
||||||
|
// Valid from 2022 to 2122 (years).
|
||||||
|
x509CACertificateRSA = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDBTCCAe2gAwIBAgIQAK/NIAl3Bdg4Xk0y/ZGL7jANBgkqhkiG9w0BAQsFADAT
|
||||||
|
MREwDwYDVQQKEwhBdXRoZWxpYTAgFw0yMjEwMDIwMDMwMjFaGA8yMTIyMDkwODAw
|
||||||
|
MzAyMVowEzERMA8GA1UEChMIQXV0aGVsaWEwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||||
|
DwAwggEKAoIBAQCg7jdO1HmydfkPzTtz57pvAS3YOdBT0hlNjJ4N2lrKhNnixrzK
|
||||||
|
+4R1dWQDP2SHbZQ0TskF8eQ8HhTr7AsApotTthJFkUgV2g+bv7wVroz0Hok5xtd4
|
||||||
|
bnpOvG3YUCP13Nk3ZVxdQXqR3/G3MrbyiXVPcgU+0giJ8EBykbtMu8L79/1iyk+m
|
||||||
|
w4fZfzTOeorRgspO3z3+pTAib2MCTA7bby1dX9qI/ysFPLdbJYfNQDxij8SzNLyJ
|
||||||
|
EkQ4kh3jKXf1VcZjbQTtYTZ3JJDqM08OxGMKuXUxPHd72Xlb+Fzql8LjYdEy/YKA
|
||||||
|
3r8FMf14lzcjvxtLnFXh//hiXh4+xgXMkrLZAgMBAAGjUzBRMA4GA1UdDwEB/wQE
|
||||||
|
AwICpDAPBgNVHSUECDAGBgRVHSUAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
|
||||||
|
FGKpXiZA+8VQyMBqTTep+dVTthSbMA0GCSqGSIb3DQEBCwUAA4IBAQAE4DJg+Rb4
|
||||||
|
iiocvxxQ85lhh94ql++E8MKuzIdN7ORs+ybUnsDD1WFDebubroTQuTSBkFrNuGNJ
|
||||||
|
8B7NZsHiWWLvNsrnxxeC5CicqfhSDti0rKWsbGyeoq7Kqok5E4pwOzeRsxL2e/Hm
|
||||||
|
G6LsUQuQMUG2vxKNynqmJS4VpgSVkiGhUfURFuRRDuRpVQ/XTl7jDIGf/ls7TAZq
|
||||||
|
1AnmnSi4Cqy4hrTnwYUYkFCcH69onUKAoaVNl1eAH7ogxakz32WyWObY98NBrjzA
|
||||||
|
I6VQlaQNSHtdFqDpu7NWJZZZSgN4BknbMYQEPNYCm701cPB4ahJbpg5C3pVPFSql
|
||||||
|
Bc9iI6nN3PCr
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Private Key for x509CACertificateRSA.
|
||||||
|
x509CAPrivateKeyRSA = `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEAoO43TtR5snX5D807c+e6bwEt2DnQU9IZTYyeDdpayoTZ4sa8
|
||||||
|
yvuEdXVkAz9kh22UNE7JBfHkPB4U6+wLAKaLU7YSRZFIFdoPm7+8Fa6M9B6JOcbX
|
||||||
|
eG56Trxt2FAj9dzZN2VcXUF6kd/xtzK28ol1T3IFPtIIifBAcpG7TLvC+/f9YspP
|
||||||
|
psOH2X80znqK0YLKTt89/qUwIm9jAkwO228tXV/aiP8rBTy3WyWHzUA8Yo/EszS8
|
||||||
|
iRJEOJId4yl39VXGY20E7WE2dySQ6jNPDsRjCrl1MTx3e9l5W/hc6pfC42HRMv2C
|
||||||
|
gN6/BTH9eJc3I78bS5xV4f/4Yl4ePsYFzJKy2QIDAQABAoIBADlsZw3Y4Ufdsq6B
|
||||||
|
w/oasLqVSB+EmaKfMGosh+VXidgDyZ+S3KDtWJl09uf1wdBVOHHlvvNBGfidn0eD
|
||||||
|
pXVo+AQ5zpFGQtuRQMqJgvqVmzQshTi5i/8sJLZdpDBwgDRlxphusaORDsRojV6a
|
||||||
|
WQ94HwTnIZoF5ggaU1TOTXAW+39er+3CAkHZlqeCHliSdVWdAPy2AGTOKqTP3Pko
|
||||||
|
owbHkuCw53oWsDB9N8zqdVF+UBht0sFOQ8tEHq0OY1HJRtPhfvcFno9rADsna/Tg
|
||||||
|
5m0sWUwP+uQ2+n18ahqunclzANu/w1SE+DVvXmeKdWMXEyuyL7muKsLgW5Ksa4jR
|
||||||
|
h6gAwBUCgYEAwmigbqAWrVhh5W1t388WcfsD0M7a4vMg7a3L5Im5mThgUHu3K5bM
|
||||||
|
EYLR8RnReEUdxdF86ASup4ddyVlz7z+YuQ9+XdTwMLK5Zujqfu9U/vsLHBc8h9eP
|
||||||
|
B7C6etfnTBfQeQryPaako8mixBpS0/pQhDhBHpWcYLLTh+uE17+C2U8CgYEA0+pe
|
||||||
|
EzWj7RHZiaHp6WWOqGuaCa3JR0uh2zns1gVx0cCdaCYFI+fOmrihrvBCYTXCiDZ4
|
||||||
|
k4HGD48i1R8nUsV6HJfw0yhR2UmB46TnFYb0RgzuJhXlXtlivYVbSDhyzl1NBafC
|
||||||
|
9zuCITMqGw921w/unOamMD94VSBfxT4EMBpvV1cCgYEAlhNuxfePigHQkOwJBd03
|
||||||
|
1oWQTIFjOA+4O8MOwz4OqNl8gKUAogWnQ11Z9GWZ7t5sPWmaowH6UhmNrQIBHZBa
|
||||||
|
tYHga08WnIFb3rWvUI4xbyUdTnIhqDwfjjA/xNUnGPbJWKe6mR0ru8TMgdZQWpPB
|
||||||
|
1FAY9SNJtNxXr3WA94w/1sECgYBu0cEgioyPDSaVsvZ/93wC10JWjWsUvZiG7GPO
|
||||||
|
CErdRb0LGdbWUALbJnJm6X3NGDACy3mCqfrJaDDvArutrVeOXGa0BgHHf4lNYo71
|
||||||
|
0v0rJNflUs4AK+5W7cYunlZrVJ9StchfQd9rPTZnsE6VaN9/bZ663HYxDh0HKMdH
|
||||||
|
4IsZQQKBgQCsBJ2bP9dc7QcqMXs8k37USIMs4nz6YWkV0Q74bBhbbnKIhY4mxhwf
|
||||||
|
5WMBUntxYQT0yyygA8q/wJrjiWI9dr+Le0VAtg4Wn8CW0bNYvnMySn2++2E8m6jM
|
||||||
|
DBXePomOtkIwEdLGcO8csBLYQKn4x/5ONpYI2QAifIB8Gdxqnp5clg==
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Valid from 1970 to 1971 (years).
|
||||||
|
x509CertificateRSAExpired = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC5jCCAc6gAwIBAgIRAPKEPEnRO1hurtNAdEuDJA8wDQYJKoZIhvcNAQELBQAw
|
||||||
|
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
||||||
|
MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||||
|
ADCCAQoCggEBAN+qPAlnoqHMeBeXUF7qnaZXvHS4p8m2N9+hU8us6GYl3mYdFRDy
|
||||||
|
PGBYWewtIE0RsexBa7UYOV6IXdfheipsmRZZzUxjPbP/VfNuafxdZMVgQzWZZAtt
|
||||||
|
JJHRhLBATSfkutoPe3eUXxFonEhvl5ErU4327M9cZlLPRsIiVoTWOmigTT0jctx+
|
||||||
|
u/3IyEVtV982SpttYnpCZ9lCvaSgjpvf1Mim+dbGF0KPKitAbuFnNpWsbRzIYfiy
|
||||||
|
rGMvxuftkywJ/e6Lx34HJjq/4+K1qII9clIiwAxa1RTnLbBuSLzVHxmj3L5hQhap
|
||||||
|
jf7HMhLReW2XLJNw4xUShSKpvapBRGbly18CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
||||||
|
AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
||||||
|
AQELBQADggEBAE5cRfJFTGOozfa9vOedNjif6MLqCtnVv9SRy7k/pdz0KSpYfpRo
|
||||||
|
dPJ0kBIV2JwIVAXfge6SNn5DK/fyDzERq/kQaCzgBZ6//hCf6Y1BApGajLvOmhPx
|
||||||
|
KijvMtqQ8IZnqwrURN04pQCt936grsas3Y6haxNyfdJDnQ5FgTe6kDOJU+rXyLp1
|
||||||
|
N7H6hMXAd9+T++F50PM8AwtRZM6jSUVEhyrniKQSdkOQnXO5ng9if/7GntNzn56o
|
||||||
|
7cV3sBeenxEmvaXsR30C+A+Ankxr8HBlVOCYcJpbtsCmOB2PVRq9Q5KJAylHRWE1
|
||||||
|
JedOdWjWvrVaP2IqRopS9mV3Ckf1E19YWFg=
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Private Key for x509CertificateRSAExpired.
|
||||||
|
x509PrivateKeyRSAExpired = `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEA36o8CWeiocx4F5dQXuqdple8dLinybY336FTy6zoZiXeZh0V
|
||||||
|
EPI8YFhZ7C0gTRGx7EFrtRg5Xohd1+F6KmyZFlnNTGM9s/9V825p/F1kxWBDNZlk
|
||||||
|
C20kkdGEsEBNJ+S62g97d5RfEWicSG+XkStTjfbsz1xmUs9GwiJWhNY6aKBNPSNy
|
||||||
|
3H67/cjIRW1X3zZKm21iekJn2UK9pKCOm9/UyKb51sYXQo8qK0Bu4Wc2laxtHMhh
|
||||||
|
+LKsYy/G5+2TLAn97ovHfgcmOr/j4rWogj1yUiLADFrVFOctsG5IvNUfGaPcvmFC
|
||||||
|
FqmN/scyEtF5bZcsk3DjFRKFIqm9qkFEZuXLXwIDAQABAoIBADBgYrHqD3wNfKAl
|
||||||
|
o0WUW1riOSnJ0sjHN9iPzU8NbArEABF4Etlie3qfQXva2tSwkho2oDRANBBlUF7k
|
||||||
|
LwdEC+yQqd3uzSbEgHOxmwzxql0ikAbk0YXDKpi7h4aTsdyCFYQauyrHFbTvOnZU
|
||||||
|
ZKUKiPz4vomvQ5Z/rJ9KzAnZSDLeqbJfBXPPitlE8DAiYypGKDUmX0unMJh/x0Pw
|
||||||
|
mIP/DTd+nMl+QpoSR0nS8r8Pr+4oBJ8K6k9Oni2DKdIW8IvoQJBBa9cm8Y0fHkSl
|
||||||
|
hB7fncY5bE0lOZ8jBlSNuGfZHjVihwBA+rYAcWpyzdBx3SHRSe5AH4RKBPaERgSt
|
||||||
|
SBV35PECgYEA7ayQs2SMggOiEK4Wf9AzywieaHiHa2ObJRg2dHlIgVkUDL3zB/b6
|
||||||
|
57jPMXAtMyGQDW6pZF6Oq3mgYP2A9alB6QKjpX1OGFmqZJxtRMAm0KPs2C2inWzg
|
||||||
|
dz9OW18jDlKKsHR00JktqsNgOZC8ldE2cyqgwBNXT/P9GyUMC9RmYhkCgYEA8Okk
|
||||||
|
9u8IoIHJEWbtmmh0d1CEmPz4zQosTgUl2GLbNaCE/zDvA82YUQmi1yaF1FHjXMoa
|
||||||
|
tD0Plkixoj/ezASeSE+duVpgXflYL4IHbqQq9JQg39vyaSuU1g3wP+hnmnCT62vb
|
||||||
|
z4v7ugDLLkSlvNeEQLR3GvZvInZnfdwI5/mjeDcCgYA1UGlhJGP0YjY/gZ2gbCbC
|
||||||
|
G5vVGXxfFYfeyVClzfL6uO2rcgyLM9bSlf08PMqW1qeGq9Upo6BjTLQyLYt5D8+u
|
||||||
|
Ih5tZ+9VvP9g9En6ixPp52ugjpQUtjCf7z53dp7ZfqCHtofhpwq8bHkwUIxNGxIY
|
||||||
|
wW4vx+blE3kqVqQeHzYcOQKBgBgBAv/fvVpQ1Dn5qX8THVeuHCgqPJghhVyYwraW
|
||||||
|
0wS648WRmJ8mYyDf9uu9GOSY7DCYqqR+2Qi+YYSrHIXzh9nopOyNBsEWUSUarabm
|
||||||
|
kKkiAUyM29CC2Sei5+dWPsxynyp76sD5T7Gu1o/boy/3wWO5F40GNPiYF6PAwtpq
|
||||||
|
U1FtAoGBANfr5OcnCIdtHLCEVRCaJdzTkQj5X1g9dF0D/gWkBIF0hibcs9yV2i1Z
|
||||||
|
JtxBrOvctkRsY7/C8dCms1gkfwDyTpKuMk9iDd3wfDGP3LdD1+V10pCm5ShHIGNm
|
||||||
|
/pRFpN45nR5iCX9mnvr8YJLUsrBkh7N4c4ao8xsXzOLBOk8WvtXL
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Valid from 1970 to 1971 (years).
|
||||||
|
x509CACertificateRSAExpired = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDAzCCAeugAwIBAgIQB07G1WhPAiAHaM6FogkZ7TANBgkqhkiG9w0BAQsFADAT
|
||||||
|
MREwDwYDVQQKEwhBdXRoZWxpYTAeFw03MDAxMDEwMDAwMDBaFw03MTAxMDEwMDAw
|
||||||
|
MDBaMBMxETAPBgNVBAoTCEF1dGhlbGlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
|
MIIBCgKCAQEArly1jZXqLaAQJwTaoT0QaePV7SejQZj418Id7X7Gx1bgjmIS0mL8
|
||||||
|
058c8Av1ThRaUmVAMkbBc4MED9KnKKckU+qMBV0+2xovrn7TvAKHG3yLUtasn/Uz
|
||||||
|
2UzBXMgbCIMVCloWkPQ6BuSaNsZnSBmTj/IC16bAZwm5lIS9ZjzQ+QnZg8x5ftNR
|
||||||
|
Jx8Gar8xb4tQbhb6uZU5zxfuV1+4qk04lf6E48IVrf57NBXpJhdzxuvdBj0l46k3
|
||||||
|
zf44gybrXpr9O0n0Eb/H8lkIoIDM+vanoRvdg868QQw8C/r06M4E8gJJzZk7Ad2c
|
||||||
|
oCq66peom6eLUSo2DVfVmmQ2KjLUyW0L6QIDAQABo1MwUTAOBgNVHQ8BAf8EBAMC
|
||||||
|
AqQwDwYDVR0lBAgwBgYEVR0lADAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSQ
|
||||||
|
qzXTP6jV517cAknynvP0vr4ChzANBgkqhkiG9w0BAQsFAAOCAQEAGvX14cJM+uxh
|
||||||
|
rSBYa3qHEfSvlSwbQsCxZv5VXLJj6SLYUXqMLxCuPH16ZKD8MygmnF5/8Mp3ZIdF
|
||||||
|
Aesehtaoth1H06q6iXWw+wgT9fZKGIHL4RftvjFWncD+Lbk8hP/DkLqsGt+Mj23u
|
||||||
|
JAByhiG8QmbXu/X7kfXSvXjhQ7f7C+bNKxb03r7mT5gI8mCUp5MyLp3DPk3dKTwa
|
||||||
|
uby/wjlFMHi92HjfQ6mCn5ijc/ltiMh1wtXf53IEESYvrWV5ABjV8xnumI4j4idB
|
||||||
|
7yHjCn5id379go8e8a+W8jODNzUORzSio8tDhL4c13tiD4PzlMJ1tUr+CIoCzqB5
|
||||||
|
m99SvwJ74A==
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Private Key for x509CACertificateRSAExpired.
|
||||||
|
x509CAPrivateKeyRSAExpired = `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEArly1jZXqLaAQJwTaoT0QaePV7SejQZj418Id7X7Gx1bgjmIS
|
||||||
|
0mL8058c8Av1ThRaUmVAMkbBc4MED9KnKKckU+qMBV0+2xovrn7TvAKHG3yLUtas
|
||||||
|
n/Uz2UzBXMgbCIMVCloWkPQ6BuSaNsZnSBmTj/IC16bAZwm5lIS9ZjzQ+QnZg8x5
|
||||||
|
ftNRJx8Gar8xb4tQbhb6uZU5zxfuV1+4qk04lf6E48IVrf57NBXpJhdzxuvdBj0l
|
||||||
|
46k3zf44gybrXpr9O0n0Eb/H8lkIoIDM+vanoRvdg868QQw8C/r06M4E8gJJzZk7
|
||||||
|
Ad2coCq66peom6eLUSo2DVfVmmQ2KjLUyW0L6QIDAQABAoIBAAiOZBpekO9MO36u
|
||||||
|
rkvbQ0Lu+0B4AXrmls9/pxhQcFC34q0aAvJwCRgZZsIg1BjQxt3kOhI9hqC0fS6J
|
||||||
|
l8pW6WF00QoyWTNHRa+6aYmAVkDzC6M1BaOT1MeFDLgQ2cLBK/cmFJVoZrCP50Fo
|
||||||
|
2wieuK8HoTwT4r0rrP+sw96QfXC7BjC1VSL9GXYemKz0RXEUvXXmzGGc9YE8vCt/
|
||||||
|
PXOb4TV30TIQrivkywSTJi8A1jUjYI2rPgo6JCl6GZGmc7hVX4jJ9lbBhUH76ozO
|
||||||
|
KS1Yzo/veWL4rVspc2exT5cuX7JIuFCjVi0Nlv1MKv39jpfTfKQh0ug6pHlxUzqX
|
||||||
|
Rl6Ln0ECgYEAwX0HtsmKMoSIrU4k90TKyhzjNoL8EaMKrSoZ4nK599igd8g+y6eD
|
||||||
|
jc1qO60lOHObyLPFor0rQT7K2KCD7GKYSX5+sh9leQEASl7cJkSAG17WBlrf9Pas
|
||||||
|
nUXjTRx3moEILAWmuov4UrYpkuEFk65d98xP3uPtDylFj57Bc+a8DOcCgYEA5rHK
|
||||||
|
qdjE8c0/1kgmItDJjmKxrJ5K5hY4w7SkpZeg4Rwr2WAjv3je/nx34D8S7m6R2uzp
|
||||||
|
NQYAAHXdzHt4iegupyW/3UXJboEscSTuC6/v3llawAozh02nDsMrdC1LreQ1IiFy
|
||||||
|
mKDmPZWxiAZXxEJ0hi0YMCcQnBY673eAleostq8CgYEAtia/mVvYh0Bv7z9O253e
|
||||||
|
jzFs0ce0B+KGzYiB/8XjvyknwDw6qbzUwy0romyZSrDDasma+F7AFtdHXXKXX3Ve
|
||||||
|
SmoUWhnmjGjd3iW5eSkptRqtwCPTDKkgzZqapuBy1Hg+ujrDwIC+0Rb+wnCmsGYJ
|
||||||
|
vpuQYZQPeyNugguBsVv5kucCgYBToTRE6k5LEgsIVVNt356RvXmHiELCsl+VotDl
|
||||||
|
Ltilgp7qyI1tBhZgzyJt6q+kO/UoFiZckHZDtHbZgBEsfT0cXvT09C2Xn8BKrAaX
|
||||||
|
ugoM4vuhDpGrhR0AnwQLs7fxq/8PBm0So5GT1cZr91Ct1yGC2qogGqlMzEpFMV8t
|
||||||
|
+ZyIBQKBgFyI2cZ3/uHQMkWUguml9w134bIGpqGdp8jf9YTvWs3Ax8/qxAVmFmtm
|
||||||
|
fot+QiamambblrhdT6pau11Mp06FzytorQH0qKd8mPAqvqtvcSoDZzqXjrkUnZIx
|
||||||
|
uLUcfb41clRhlfGDUj5MWimfi7d/Vakh86zAa8pg5WBCtXr0bZ/j
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Valid from 2400 to 2401 (years).
|
||||||
|
x509CertificateRSANotBefore = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC6TCCAdGgAwIBAgIQYQWHYM90zNnwv22xhOPkszANBgkqhkiG9w0BAQsFADAT
|
||||||
|
MREwDwYDVQQKEwhBdXRoZWxpYTAiGA8yNDAwMDEwMTAwMDAwMFoYDzI0MDAxMjMx
|
||||||
|
MDAwMDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||||
|
ggEPADCCAQoCggEBAPFYYindnRfs8aJfXbaX5IRVj10uKlT4i0BwJ5IYaC4O/3UQ
|
||||||
|
km7do8lL2Ea2N2L5tQJhk2d+yoWGPeaUyuYP692jPA+4BW6RPuroSPB9WEU+x1ir
|
||||||
|
it/AzJtavg0Lu2fGZkXxAZJj2MlrXT7csaGwRAvvPEHS6EJW4UtERYIqfpKGB39I
|
||||||
|
sUhKNvY3edF9sosUAJmiZ8Q4K/uYoyCxyiE1QKLaiIjcZJxtzXkzwVBy1ZlmG+r5
|
||||||
|
VNNguQQFsS8f7uRlOmo0o3hDG9dByUn7PgFEExbgBtdmNoIPk/pfMFM8NIHK+wOC
|
||||||
|
q/SO2e/MX0IhJZXfq2VTZFgrisPovg8GpHSHRCkCAwEAAaM1MDMwDgYDVR0PAQH/
|
||||||
|
BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZI
|
||||||
|
hvcNAQELBQADggEBADBTRbfg/UQeJpdMogm9tleXJBcHqgOgiBxkKYxGSlRg4vlr
|
||||||
|
tM8USAr24whLvb8KDhT6PaSY8wyPuCxqwqiKR84eselxOAcgDLV9n36OcWRm+oFl
|
||||||
|
Th1S1JUtjbrctU7i6pp4BwUmBwkVALrbrj+cGVG7uBfbP8L/onmjg9KkY5ttnbyb
|
||||||
|
Qi/Pv9zYLEo394hL3oeaphkP0iE6cHOvII/qFhnpLREINGp3g8V2I46Id/xYDi22
|
||||||
|
WRabgMuFGpel7Q26yh+YXyHoIkKdiOXMNNXTsuvp5EDBGybTIXWK3xrqTsREMVDr
|
||||||
|
EmiaOgL8c7+PpSWuUggJLb/JXDYnPtvekH3gPao=
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Private Key for x509CertificateRSANotBefore.
|
||||||
|
x509PrivateKeyRSANotBefore = `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEA8VhiKd2dF+zxol9dtpfkhFWPXS4qVPiLQHAnkhhoLg7/dRCS
|
||||||
|
bt2jyUvYRrY3Yvm1AmGTZ37KhYY95pTK5g/r3aM8D7gFbpE+6uhI8H1YRT7HWKuK
|
||||||
|
38DMm1q+DQu7Z8ZmRfEBkmPYyWtdPtyxobBEC+88QdLoQlbhS0RFgip+koYHf0ix
|
||||||
|
SEo29jd50X2yixQAmaJnxDgr+5ijILHKITVAotqIiNxknG3NeTPBUHLVmWYb6vlU
|
||||||
|
02C5BAWxLx/u5GU6ajSjeEMb10HJSfs+AUQTFuAG12Y2gg+T+l8wUzw0gcr7A4Kr
|
||||||
|
9I7Z78xfQiElld+rZVNkWCuKw+i+DwakdIdEKQIDAQABAoIBACGcZHdeJK2bUv+A
|
||||||
|
9oUiXDHN1JxufHi+8G218NzYx1F6xzrfZvVHqrKy/FjEsav4CKxfOG8Wak/0JRTC
|
||||||
|
rgsiNn/0Zr3tq9v9IF0IonfTjQJ/vrVrlniY2iXcmlEozB2ktMOSz9w6SYurhx3l
|
||||||
|
EFvrN17OH38vRydOACxCQsfg8SWofY6SV0gcvlCcuM4lKBiuOBWGcf+xwIs3B+Bs
|
||||||
|
Frd282jRWtlcYd+zDE+vLxugNizLGpRKCMEdcKPRw9fkBKDI/f56WegNTUZYYFrV
|
||||||
|
LEmYIbOwMawvbi0mOdLsp27CfmeUjkEbwzgdNwjFrWIFAk0wT3QvDrKxDYDLM2Z3
|
||||||
|
+PtBMwECgYEA9ICYgzPMbN3CsQ+eWSQXXNk35V7PlMl1DC4UIHhi53BMT1ZhvkHf
|
||||||
|
D+eqXQ3BSqOUR7b417VBGkK8UtQuQXh9FwwVU0RhVkjpX0nTBhe8gGF3f3094rX4
|
||||||
|
Ckhm8XYQEWCUA9HNhCW+KSNVWqgw9Qi0awEY7HaiR628br39/EckvokCgYEA/LHI
|
||||||
|
HA9ixEBeTjds52rK6n9bPHeI87qxF62lLQYXvosyJij9/ruUfXwfJjG3EvCfcW7N
|
||||||
|
fr2EvgzPbCozC1V6gI9k5CXhOsf+wD6M8A7g7YHUa/dPq2B8bfqaMD0vW7OoZiLQ
|
||||||
|
NpfMtBvZxd1wukPGypLGWabPLo8u6bSfxTqz8KECgYAudnmFBUTls0aaKyOmQOuH
|
||||||
|
o2ex2NCNr7Lke6UrfnUdEgQOV5X/d7kR5q5DPKfsrSUyc5zaMQGMIf5zpwqbOnBa
|
||||||
|
/trWlfoBUZ23k+ncEIqrwtnYik5GVNor6hJV9F+dTcMS7r2lTR7T5nkD305eYicW
|
||||||
|
5oB7/xdbk7JpQQWQ+VwMMQKBgQC3hbKs1mvH1mvnaI+aftACgR5VCweW4/bsGHwG
|
||||||
|
+A7Unyl713eowrk0ban9xkuM4N8btfpe2uuGT61xhDBwQdNnfT0sCWrLkyasnoEj
|
||||||
|
c9reA9Wv1/yvnbKg+Ul0UWuMsS1TiGMp0xOjlzqRXqMZVFITG4gc4m5EBU9wAnOq
|
||||||
|
/VhkIQKBgBUwpoL+K1OswQKV/SHH7n5b/By02mLuMkQR0NlpWRJY6eTDk3FAUkHn
|
||||||
|
+T996U0a+7OY+mATVqrfLBcsa6i+HGpb4jZL+kkdtfmtHUnN8YOAwopWp3uoPRmF
|
||||||
|
lpakAQq8NPVcrX6PLDkHeOlKhE4ercYFqcCRKcnMB3nD+re6x5l6
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Valid from 2400 to 2401 (years).
|
||||||
|
x509CACertificateRSAotBefore = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDBzCCAe+gAwIBAgIQeR2/TbyH9gEzyjuTijMGVzANBgkqhkiG9w0BAQsFADAT
|
||||||
|
MREwDwYDVQQKEwhBdXRoZWxpYTAiGA8yNDAwMDEwMTAwMDAwMFoYDzI0MDAxMjMx
|
||||||
|
MDAwMDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||||
|
ggEPADCCAQoCggEBAJ7vpRSDXVvwOLGmjbZdoG25OdsWgmhVAWpFCUifotqorz+z
|
||||||
|
+VgwPnvVDp1cTp07y+mDJK++GNBOG1pS5G4or6Y1HAlT3nGpE0FYhrtDBQmhvqV0
|
||||||
|
6mPM/Dq5JIuGiju4LX0KBlaFJugJevw3ySnoPUu0BQ9mTZUgggNwetqsAX7TioFj
|
||||||
|
TkVIMtgranigOvWjJQyLlmiK1TOgMqgWYNR20SE4CkmIp9SjOdeW7kNVMOojRx9a
|
||||||
|
VgElA2TN57/Je+/tLbvTDDCP9o59SIXxn5N6JQ2/XdDZPNBHrxnmchQVDXWCkP0A
|
||||||
|
gkV7V1dl8ur85iEdN+F31Kvd0nzCCaC3YUMxgmcCAwEAAaNTMFEwDgYDVR0PAQH/
|
||||||
|
BAQDAgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
|
||||||
|
FgQUB4/oEXJWhMwKBpMesSOnQdD+G0UwDQYJKoZIhvcNAQELBQADggEBAIlzOnWB
|
||||||
|
xIhMm3zpLfpJGBi62d9J7Rlf5NitWoztyHdJpQ9y99s67QonR7UY7n11PMdtsLja
|
||||||
|
hWy1/iZ1o5F2zGKPzSS8pscdIuo4B+TocLiHkEx7ttyQ0MepoDt1RlTOjqilqbfD
|
||||||
|
A4GyGidns1VZuH8wP8NpZNlWajsXgvkYT433RzPgKe7qoI3DFQwc72SBuZSHHyjE
|
||||||
|
9SVgdN0KmfFXMum4BurwftelF1etGR+4II3cDG80CH2ZvYdqCURPoa+ny/qqMtzq
|
||||||
|
W2CnwP59TrotQgKCFJS5EdL3MXaZSvK9z2LERdxDvp4OSoJYoxSMJawfkVwZ15rk
|
||||||
|
apA21VwIrpFg54A=
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Private Key for x509CACertificateRSAotBefore.
|
||||||
|
x509CAPrivateKeyRSANotBefore = `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEAnu+lFINdW/A4saaNtl2gbbk52xaCaFUBakUJSJ+i2qivP7P5
|
||||||
|
WDA+e9UOnVxOnTvL6YMkr74Y0E4bWlLkbiivpjUcCVPecakTQViGu0MFCaG+pXTq
|
||||||
|
Y8z8Orkki4aKO7gtfQoGVoUm6Al6/DfJKeg9S7QFD2ZNlSCCA3B62qwBftOKgWNO
|
||||||
|
RUgy2CtqeKA69aMlDIuWaIrVM6AyqBZg1HbRITgKSYin1KM515buQ1Uw6iNHH1pW
|
||||||
|
ASUDZM3nv8l77+0tu9MMMI/2jn1IhfGfk3olDb9d0Nk80EevGeZyFBUNdYKQ/QCC
|
||||||
|
RXtXV2Xy6vzmIR034XfUq93SfMIJoLdhQzGCZwIDAQABAoIBAEneATBOeYZwWDkg
|
||||||
|
un5Gd3hnfN85T/SjhVvZqB3rq6nKemC2Ca4WBgRRmlBChXsIPpZR0CwpwqiVlJrf
|
||||||
|
KbGVEUXDKzuekiTrOrrFJSFFXcMDPHLzqrglnhjA0Z5TMk3dJK8XiKiPi+yN823j
|
||||||
|
k4f5mvtjOHLWzjn/+M0WatLU3IEPnqpnE+pEKrkZQa7Mg+xHprvt67Q4aCgP5lfy
|
||||||
|
A04eoUo7+TMRsK718vb02E81ZQSLgSbQMd0W8Dkt7vRkYRNL0OKBlPQcP9qZlw5s
|
||||||
|
swy4ne9jgmJKY3mmdnURTjvdJb20dUsSSzseZ8Tj6UYUDntXrB62YhZvC0ZRhGY/
|
||||||
|
Nnf10IECgYEAzSi1l1G6ZblV2g2jPqqD4EsdUvitnN5t592dw52+SyizNj7j+pLt
|
||||||
|
OPi+bt5HW4orHQHlPi6wt1BQIZ7UVHmljKQq7ByxOX0SRrRg428JPlFMjCp3bG1t
|
||||||
|
zmRQwADGkfqn3JQcmY9VDjtn5oCx18bNpDR1gNiK6zImK/jmWBOiaKcCgYEAxlKN
|
||||||
|
vYeG70ZrtVBz6jmX6yOtN8/hBA0mifZVHmkKX6earU3Ds2Uj2Hg+AgMqoFD2aRSp
|
||||||
|
wodEYzV6hSpvdLzqBi6SklnfF4NBqJ51TEFBWaVMUZjTwConOcc+vvvSDbCkmnoF
|
||||||
|
yTcqVm2p91HD7859ACcO+m8nsFJGldbJFl8RkEECgYEAkSLajEkyH2Kk3JTHRr7k
|
||||||
|
eplJDniEgbRNdjmusUN36r3JQnftWkf08FfwiIhRXO37IBNGNN5c/+IePhqZxYUl
|
||||||
|
W8CL6OtHaQ8VDdXvsRXNKTvkdkhYoeksRFVtVtd1orH7bK2PKgdfOalHEKc8qRSo
|
||||||
|
SCEge101sbuRi4wSkH6bZ4MCgYAerDXv0j40U5fk+wRyfWXZoDLyJtyOW9pSDB8u
|
||||||
|
DODl2m45z4UtAb+Bg1dTyFmXYe46Yk+/HlydW3APmHiUfYNUYW+Z4vx2Dn7hLWDG
|
||||||
|
4nDRBJfBJvnZBqv6a65wq1HZfDB5E9ZBQJ7zrxJShfrf4/fBRkkywm5I/vCbzBRd
|
||||||
|
uWZmAQKBgQCJE5rx3rWZz+srunmmkg5LXBMveD+1HRvlwlj/gELG2b2ytNUmnVey
|
||||||
|
2naApHnvW7lZdrADpbzLKGEDB/EsaIPJjqQw45OoIZwPdM4bm0/w5c1ZLnStXGCz
|
||||||
|
Th/7Sva6x6FW7tHY6ldqybcMj8w3kA4ByQEOg2BtPnWTm1NX/qcr8Q==
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
*/
|
||||||
|
|
||||||
|
x509CertificateRSAInvalid = `-----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=
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
x509CertificateRSAInvalidBlock = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw
|
||||||
|
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw
|
||||||
|
MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||||
|
ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q
|
||||||
|
/Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6
|
||||||
|
LuYx2rBYSlMSN5UZQm/RxMtXf^K2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY
|
||||||
|
91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H
|
||||||
|
kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR
|
||||||
|
Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
||||||
|
AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
||||||
|
AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh
|
||||||
|
/ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4
|
||||||
|
lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq
|
||||||
|
wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg
|
||||||
|
OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i
|
||||||
|
ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I=
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
x509CertificateEmpty = `-----BEGIN CERTIFICATE-----
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
)
|
||||||
|
|
|
@ -132,6 +132,8 @@ const (
|
||||||
errFmtOIDCNoClientsConfigured = "identity_providers: oidc: option 'clients' must have one or " +
|
errFmtOIDCNoClientsConfigured = "identity_providers: oidc: option 'clients' must have one or " +
|
||||||
"more clients configured"
|
"more clients configured"
|
||||||
errFmtOIDCNoPrivateKey = "identity_providers: oidc: option 'issuer_private_key' is required"
|
errFmtOIDCNoPrivateKey = "identity_providers: oidc: option 'issuer_private_key' is required"
|
||||||
|
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', " +
|
errFmtOIDCEnforcePKCEInvalidValue = "identity_providers: oidc: option 'enforce_pkce' must be 'never', " +
|
||||||
"'public_clients_only' or 'always', but it is configured as '%s'"
|
"'public_clients_only' or 'always', but it is configured as '%s'"
|
||||||
|
|
||||||
|
@ -294,9 +296,11 @@ var validSessionSameSiteValues = []string{"none", "lax", "strict"}
|
||||||
var validLoLevels = []string{"trace", "debug", "info", "warn", "error"}
|
var validLoLevels = []string{"trace", "debug", "info", "warn", "error"}
|
||||||
|
|
||||||
var validWebauthnConveyancePreferences = []string{string(protocol.PreferNoAttestation), string(protocol.PreferIndirectAttestation), string(protocol.PreferDirectAttestation)}
|
var validWebauthnConveyancePreferences = []string{string(protocol.PreferNoAttestation), string(protocol.PreferIndirectAttestation), string(protocol.PreferDirectAttestation)}
|
||||||
|
|
||||||
var validWebauthnUserVerificationRequirement = []string{string(protocol.VerificationDiscouraged), string(protocol.VerificationPreferred), string(protocol.VerificationRequired)}
|
var validWebauthnUserVerificationRequirement = []string{string(protocol.VerificationDiscouraged), string(protocol.VerificationPreferred), string(protocol.VerificationRequired)}
|
||||||
|
|
||||||
var validRFC7231HTTPMethodVerbs = []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "TRACE", "CONNECT", "OPTIONS"}
|
var validRFC7231HTTPMethodVerbs = []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "TRACE", "CONNECT", "OPTIONS"}
|
||||||
|
|
||||||
var validRFC4918HTTPMethodVerbs = []string{"COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK"}
|
var validRFC4918HTTPMethodVerbs = []string{"COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK"}
|
||||||
|
|
||||||
var validACLHTTPMethodVerbs = append(validRFC7231HTTPMethodVerbs, validRFC4918HTTPMethodVerbs...)
|
var validACLHTTPMethodVerbs = append(validRFC7231HTTPMethodVerbs, validRFC4918HTTPMethodVerbs...)
|
||||||
|
@ -306,9 +310,13 @@ var validACLRulePolicies = []string{policyBypass, policyOneFactor, policyTwoFact
|
||||||
var validDefault2FAMethods = []string{"totp", "webauthn", "mobile_push"}
|
var validDefault2FAMethods = []string{"totp", "webauthn", "mobile_push"}
|
||||||
|
|
||||||
var validOIDCScopes = []string{oidc.ScopeOpenID, oidc.ScopeEmail, oidc.ScopeProfile, oidc.ScopeGroups, oidc.ScopeOfflineAccess}
|
var validOIDCScopes = []string{oidc.ScopeOpenID, oidc.ScopeEmail, oidc.ScopeProfile, oidc.ScopeGroups, oidc.ScopeOfflineAccess}
|
||||||
|
|
||||||
var validOIDCGrantTypes = []string{"implicit", "refresh_token", "authorization_code", "password", "client_credentials"}
|
var validOIDCGrantTypes = []string{"implicit", "refresh_token", "authorization_code", "password", "client_credentials"}
|
||||||
|
|
||||||
var validOIDCResponseModes = []string{"form_post", "query", "fragment"}
|
var validOIDCResponseModes = []string{"form_post", "query", "fragment"}
|
||||||
|
|
||||||
var validOIDCUserinfoAlgorithms = []string{"none", "RS256"}
|
var validOIDCUserinfoAlgorithms = []string{"none", "RS256"}
|
||||||
|
|
||||||
var validOIDCCORSEndpoints = []string{oidc.AuthorizationEndpoint, oidc.TokenEndpoint, oidc.IntrospectionEndpoint, oidc.RevocationEndpoint, oidc.UserinfoEndpoint}
|
var validOIDCCORSEndpoints = []string{oidc.AuthorizationEndpoint, oidc.TokenEndpoint, oidc.IntrospectionEndpoint, oidc.RevocationEndpoint, oidc.UserinfoEndpoint}
|
||||||
|
|
||||||
var reKeyReplacer = regexp.MustCompile(`\[\d+]`)
|
var reKeyReplacer = regexp.MustCompile(`\[\d+]`)
|
||||||
|
|
|
@ -16,45 +16,60 @@ func ValidateIdentityProviders(config *schema.IdentityProvidersConfiguration, va
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateOIDC(config *schema.OpenIDConnectConfiguration, validator *schema.StructValidator) {
|
func validateOIDC(config *schema.OpenIDConnectConfiguration, validator *schema.StructValidator) {
|
||||||
if config != nil {
|
if config == nil {
|
||||||
if config.IssuerPrivateKey == "" {
|
return
|
||||||
validator.Push(fmt.Errorf(errFmtOIDCNoPrivateKey))
|
}
|
||||||
|
|
||||||
|
setOIDCDefaults(config)
|
||||||
|
|
||||||
|
if config.IssuerPrivateKey == nil {
|
||||||
|
validator.Push(fmt.Errorf(errFmtOIDCNoPrivateKey))
|
||||||
|
} else if config.IssuerCertificateChain.HasCertificates() {
|
||||||
|
if !config.IssuerCertificateChain.EqualKey(config.IssuerPrivateKey) {
|
||||||
|
validator.Push(fmt.Errorf(errFmtOIDCCertificateMismatch))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.AccessTokenLifespan == time.Duration(0) {
|
if err := config.IssuerCertificateChain.Validate(); err != nil {
|
||||||
config.AccessTokenLifespan = schema.DefaultOpenIDConnectConfiguration.AccessTokenLifespan
|
validator.Push(fmt.Errorf(errFmtOIDCCertificateChain, err))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if config.AuthorizeCodeLifespan == time.Duration(0) {
|
if config.MinimumParameterEntropy != 0 && config.MinimumParameterEntropy < 8 {
|
||||||
config.AuthorizeCodeLifespan = schema.DefaultOpenIDConnectConfiguration.AuthorizeCodeLifespan
|
validator.PushWarning(fmt.Errorf(errFmtOIDCServerInsecureParameterEntropy, config.MinimumParameterEntropy))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.IDTokenLifespan == time.Duration(0) {
|
if config.EnforcePKCE != "never" && config.EnforcePKCE != "public_clients_only" && config.EnforcePKCE != "always" {
|
||||||
config.IDTokenLifespan = schema.DefaultOpenIDConnectConfiguration.IDTokenLifespan
|
validator.Push(fmt.Errorf(errFmtOIDCEnforcePKCEInvalidValue, config.EnforcePKCE))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.RefreshTokenLifespan == time.Duration(0) {
|
validateOIDCOptionsCORS(config, validator)
|
||||||
config.RefreshTokenLifespan = schema.DefaultOpenIDConnectConfiguration.RefreshTokenLifespan
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.MinimumParameterEntropy != 0 && config.MinimumParameterEntropy < 8 {
|
if len(config.Clients) == 0 {
|
||||||
validator.PushWarning(fmt.Errorf(errFmtOIDCServerInsecureParameterEntropy, config.MinimumParameterEntropy))
|
validator.Push(fmt.Errorf(errFmtOIDCNoClientsConfigured))
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if config.EnforcePKCE == "" {
|
|
||||||
config.EnforcePKCE = schema.DefaultOpenIDConnectConfiguration.EnforcePKCE
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.EnforcePKCE != "never" && config.EnforcePKCE != "public_clients_only" && config.EnforcePKCE != "always" {
|
|
||||||
validator.Push(fmt.Errorf(errFmtOIDCEnforcePKCEInvalidValue, config.EnforcePKCE))
|
|
||||||
}
|
|
||||||
|
|
||||||
validateOIDCOptionsCORS(config, validator)
|
|
||||||
validateOIDCClients(config, validator)
|
validateOIDCClients(config, validator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(config.Clients) == 0 {
|
func setOIDCDefaults(config *schema.OpenIDConnectConfiguration) {
|
||||||
validator.Push(fmt.Errorf(errFmtOIDCNoClientsConfigured))
|
if config.AccessTokenLifespan == time.Duration(0) {
|
||||||
}
|
config.AccessTokenLifespan = schema.DefaultOpenIDConnectConfiguration.AccessTokenLifespan
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.AuthorizeCodeLifespan == time.Duration(0) {
|
||||||
|
config.AuthorizeCodeLifespan = schema.DefaultOpenIDConnectConfiguration.AuthorizeCodeLifespan
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.IDTokenLifespan == time.Duration(0) {
|
||||||
|
config.IDTokenLifespan = schema.DefaultOpenIDConnectConfiguration.IDTokenLifespan
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.RefreshTokenLifespan == time.Duration(0) {
|
||||||
|
config.RefreshTokenLifespan = schema.DefaultOpenIDConnectConfiguration.RefreshTokenLifespan
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.EnforcePKCE == "" {
|
||||||
|
config.EnforcePKCE = schema.DefaultOpenIDConnectConfiguration.EnforcePKCE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package validator
|
package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -19,8 +22,7 @@ func TestShouldRaiseErrorWhenInvalidOIDCServerConfiguration(t *testing.T) {
|
||||||
validator := schema.NewStructValidator()
|
validator := schema.NewStructValidator()
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "abc",
|
HMACSecret: "abc",
|
||||||
IssuerPrivateKey: "",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +39,7 @@ func TestShouldNotRaiseErrorWhenCORSEndpointsValid(t *testing.T) {
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
IssuerPrivateKey: "key-material",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
CORS: schema.OpenIDConnectCORSConfiguration{
|
CORS: schema.OpenIDConnectCORSConfiguration{
|
||||||
Endpoints: []string{oidc.AuthorizationEndpoint, oidc.TokenEndpoint, oidc.IntrospectionEndpoint, oidc.RevocationEndpoint, oidc.UserinfoEndpoint},
|
Endpoints: []string{oidc.AuthorizationEndpoint, oidc.TokenEndpoint, oidc.IntrospectionEndpoint, oidc.RevocationEndpoint, oidc.UserinfoEndpoint},
|
||||||
},
|
},
|
||||||
|
@ -60,7 +62,7 @@ func TestShouldRaiseErrorWhenCORSEndpointsNotValid(t *testing.T) {
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
IssuerPrivateKey: "key-material",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
CORS: schema.OpenIDConnectCORSConfiguration{
|
CORS: schema.OpenIDConnectCORSConfiguration{
|
||||||
Endpoints: []string{oidc.AuthorizationEndpoint, oidc.TokenEndpoint, oidc.IntrospectionEndpoint, oidc.RevocationEndpoint, oidc.UserinfoEndpoint, "invalid_endpoint"},
|
Endpoints: []string{oidc.AuthorizationEndpoint, oidc.TokenEndpoint, oidc.IntrospectionEndpoint, oidc.RevocationEndpoint, oidc.UserinfoEndpoint, "invalid_endpoint"},
|
||||||
},
|
},
|
||||||
|
@ -85,7 +87,7 @@ func TestShouldRaiseErrorWhenOIDCPKCEEnforceValueInvalid(t *testing.T) {
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
IssuerPrivateKey: "key-material",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
EnforcePKCE: "invalid",
|
EnforcePKCE: "invalid",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -104,7 +106,7 @@ func TestShouldRaiseErrorWhenOIDCCORSOriginsHasInvalidValues(t *testing.T) {
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
IssuerPrivateKey: "key-material",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
CORS: schema.OpenIDConnectCORSConfiguration{
|
CORS: schema.OpenIDConnectCORSConfiguration{
|
||||||
AllowedOrigins: utils.URLsFromStringSlice([]string{"https://example.com/", "https://site.example.com/subpath", "https://site.example.com?example=true", "*"}),
|
AllowedOrigins: utils.URLsFromStringSlice([]string{"https://example.com/", "https://site.example.com/subpath", "https://site.example.com?example=true", "*"}),
|
||||||
AllowedOriginsFromClientRedirectURIs: true,
|
AllowedOriginsFromClientRedirectURIs: true,
|
||||||
|
@ -140,7 +142,7 @@ func TestShouldRaiseErrorWhenOIDCServerNoClients(t *testing.T) {
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
IssuerPrivateKey: "key-material",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,7 +322,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
IssuerPrivateKey: "key-material",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
Clients: tc.Clients,
|
Clients: tc.Clients,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -344,7 +346,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadScopes(t *testing.T) {
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
IssuerPrivateKey: "key-material",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
|
@ -370,7 +372,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadGrantTypes(t *testing.T)
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
IssuerPrivateKey: "key-material",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
|
@ -391,12 +393,66 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadGrantTypes(t *testing.T)
|
||||||
assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'good_id': option 'grant_types' must only have the values 'implicit', 'refresh_token', 'authorization_code', 'password', 'client_credentials' but one option is configured as 'bad_grant_type'")
|
assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: client 'good_id': option 'grant_types' must only have the values 'implicit', 'refresh_token', 'authorization_code', 'password', 'client_credentials' but one option is configured as 'bad_grant_type'")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShouldNotErrorOnCertificateValid(t *testing.T) {
|
||||||
|
validator := schema.NewStructValidator()
|
||||||
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
|
IssuerCertificateChain: mustParseX509CertificateChain(testCert1),
|
||||||
|
IssuerPrivateKey: mustParseRSAPrivateKey(testKey1),
|
||||||
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
{
|
||||||
|
ID: "good_id",
|
||||||
|
Secret: "good_secret",
|
||||||
|
Policy: "two_factor",
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://google.com/callback",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateIdentityProviders(config, validator)
|
||||||
|
|
||||||
|
assert.Len(t, validator.Warnings(), 0)
|
||||||
|
assert.Len(t, validator.Errors(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShouldRaiseErrorOnCertificateNotValid(t *testing.T) {
|
||||||
|
validator := schema.NewStructValidator()
|
||||||
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
|
IssuerCertificateChain: mustParseX509CertificateChain(testCert1),
|
||||||
|
IssuerPrivateKey: mustParseRSAPrivateKey(testKey2),
|
||||||
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
{
|
||||||
|
ID: "good_id",
|
||||||
|
Secret: "good_secret",
|
||||||
|
Policy: "two_factor",
|
||||||
|
RedirectURIs: []string{
|
||||||
|
"https://google.com/callback",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateIdentityProviders(config, validator)
|
||||||
|
|
||||||
|
assert.Len(t, validator.Warnings(), 0)
|
||||||
|
require.Len(t, validator.Errors(), 1)
|
||||||
|
|
||||||
|
assert.EqualError(t, validator.Errors()[0], "identity_providers: oidc: option 'issuer_private_key' does not appear to be the private key the certificate provided by option 'issuer_certificate_chain'")
|
||||||
|
}
|
||||||
|
|
||||||
func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadResponseModes(t *testing.T) {
|
func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadResponseModes(t *testing.T) {
|
||||||
validator := schema.NewStructValidator()
|
validator := schema.NewStructValidator()
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
IssuerPrivateKey: "key-material",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
|
@ -422,7 +478,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadUserinfoAlg(t *testing.T
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
IssuerPrivateKey: "key-material",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
|
@ -448,7 +504,7 @@ func TestValidateIdentityProvidersShouldRaiseWarningOnSecurityIssue(t *testing.T
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "abc",
|
HMACSecret: "abc",
|
||||||
IssuerPrivateKey: "abc",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
MinimumParameterEntropy: 1,
|
MinimumParameterEntropy: 1,
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
|
@ -476,7 +532,7 @@ func TestValidateIdentityProvidersShouldRaiseErrorsOnInvalidClientTypes(t *testi
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "hmac1",
|
HMACSecret: "hmac1",
|
||||||
IssuerPrivateKey: "key2",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "client-with-invalid-secret",
|
ID: "client-with-invalid-secret",
|
||||||
|
@ -514,7 +570,7 @@ func TestValidateIdentityProvidersShouldNotRaiseErrorsOnValidPublicClients(t *te
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "hmac1",
|
HMACSecret: "hmac1",
|
||||||
IssuerPrivateKey: "key2",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "installed-app-client",
|
ID: "installed-app-client",
|
||||||
|
@ -555,7 +611,7 @@ func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
||||||
config := &schema.IdentityProvidersConfiguration{
|
config := &schema.IdentityProvidersConfiguration{
|
||||||
OIDC: &schema.OpenIDConnectConfiguration{
|
OIDC: &schema.OpenIDConnectConfiguration{
|
||||||
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
HMACSecret: "rLABDrx87et5KvRHVUgTm3pezWWd8LMN",
|
||||||
IssuerPrivateKey: "../../../README.md",
|
IssuerPrivateKey: &rsa.PrivateKey{},
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
|
@ -691,3 +747,111 @@ func TestValidateOIDCClientRedirectURIsSupportingPrivateUseURISchemes(t *testing
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
testCert1 = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC5jCCAc6gAwIBAgIRAJZ+6KrHw95zIDgm2arCTCgwDQYJKoZIhvcNAQELBQAw
|
||||||
|
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNMjIwOTA4MDIyNDQyWhcNMjMwOTA4MDIy
|
||||||
|
NDQyWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||||
|
ADCCAQoCggEBAMAE7muDAJtLsV3WgOpjrZ1JD1RlhuSOa3V+4zo2NYFQSdZW18SZ
|
||||||
|
fYYgUwLOleEy3VQ3N9MEFh/rWNHYHdsBjDvz/Q1EzAlXqthGd0Sic/UDYtrahrko
|
||||||
|
jCSkZCQ5YVO9ivMRth6XdUlu7RHVYY3aSOWPx2wiw9cdN+e4p73W6KwyzT7ezbUD
|
||||||
|
0Nng0Z7CNQTLHv3LBsLUODc4aVOvp2B4aAaw6cn990buKMvUuo2ge9gh0c5gIOM5
|
||||||
|
dU7xOGAt7RzwCIHnG4CGAWPFuuS215ZeelgQr/9/fhtzDqSuBZw5f10vXnAyBwei
|
||||||
|
vN6Kffj2RXB+koFwBguT84A6cfmxWllGNF0CAwEAAaM1MDMwDgYDVR0PAQH/BAQD
|
||||||
|
AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
|
||||||
|
AQELBQADggEBAFvORjj7RGoIc3q0fv6QjuncZ0Mu1/24O0smCr6tq5d6RQBRpb1M
|
||||||
|
jEsbTMLZErrHbyw/DWC75eJhW6T+6HiVTo6brBXkmDL+QGkLgRNOkZla6cnmIpmL
|
||||||
|
bf9iPmmcThscQERgYZzNg19zqK8JAQU/6PgU/N6OXTL/mQQoB972ET9dUl7lGx1Q
|
||||||
|
2l8XBe8t4QTp4t1xd3c4azxWvFNpzWBjC5eBWiVHLJmFXr4xpcnPFYFETOkvEqwt
|
||||||
|
pMQ2x895BoLrep6b+g0xeF4pmmIQwA9KrUVr++gpYaRzytaOIYwcIPMzt9iLWKQe
|
||||||
|
6ZSOrTVi8pPugYXp+LhVk/WI7r8EWtyADu0=
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
testKey1 = `
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEAwATua4MAm0uxXdaA6mOtnUkPVGWG5I5rdX7jOjY1gVBJ1lbX
|
||||||
|
xJl9hiBTAs6V4TLdVDc30wQWH+tY0dgd2wGMO/P9DUTMCVeq2EZ3RKJz9QNi2tqG
|
||||||
|
uSiMJKRkJDlhU72K8xG2Hpd1SW7tEdVhjdpI5Y/HbCLD1x0357invdborDLNPt7N
|
||||||
|
tQPQ2eDRnsI1BMse/csGwtQ4NzhpU6+nYHhoBrDpyf33Ru4oy9S6jaB72CHRzmAg
|
||||||
|
4zl1TvE4YC3tHPAIgecbgIYBY8W65LbXll56WBCv/39+G3MOpK4FnDl/XS9ecDIH
|
||||||
|
B6K83op9+PZFcH6SgXAGC5PzgDpx+bFaWUY0XQIDAQABAoIBAQClcdpHcglMxOwe
|
||||||
|
kRpkWdwWAAQgUJXoSbnW86wu1NRHBfmInyyrrSBVN3aunXbQITZIQIdt3kB94haW
|
||||||
|
P6KBt5Svd2saSqOOjSWb0SMkVOCaQ/+h19VqpcASNj4+Y94y+8ZD5ofHVfJtghDr
|
||||||
|
Y7H5OhHDEZ3e0xlwODGaCyUkUY4KBv/oIlILoh4phbDYHkZH8AzDnEiyVE1JAWlN
|
||||||
|
voAQysgSU7eEnNCi1S07jl5bY+MD3XpJkAfQsJYhqYT/qetStZ12PuXjpbIr3y53
|
||||||
|
qjCrKeWTyDN+gOznyIGuiR6nvXeQAw/o9hZiah4RuHXTPs/3GAcRXcuMR0pbgJ+B
|
||||||
|
yfX6eLK1AoGBAPKkJKPYJD2NHukAelNbT2OgeDIgJmfOIvLa73/x2hXvWwa4VwIC
|
||||||
|
POuKXtT/a02J4pYMGlaKXfHgLmaW2HPObOIjpxqgRIswsiKS1AbaDtkWnhaS1/SJ
|
||||||
|
oZ7Fk8DdX+1QT4J/fj/2uxRT0GhXdMxDpK7ekpmRE+APPCGhmOMgmWszAoGBAMqX
|
||||||
|
Ts1RdGWgDxLi15rDqdqRBARJG7Om/xC2voXVmbAb4Q+QoNrNeiIAM2usuhrVuj5V
|
||||||
|
c16m9fxswRNYqQBYyShDi5wp5a8UjfqDpzJdku2bmrBaL+XVq8PY+oTK6KS3ss8U
|
||||||
|
CGQ8P6Phz5JGavn/nDMRZ4EwEWqbEMUqJAJlpmIvAoGAQ9Wj8LJyn0qew6FAkaFL
|
||||||
|
dpzcPZdDZW35009l+a0RvWQnXJ+Yo5UglvEeRgoKY6kS0cQccOlKDl8QWdn+NZIW
|
||||||
|
WrqA8y6vOwKoKoZGBIxd7k8mb0UqXtFDf/HYtuis8tmrAN7H2vYNo0czUphwrNKU
|
||||||
|
bdcHwSsQFWns87IL3iO1AIUCgYBzmBX8jOePPN6c9hXzVoVKEshp8ZT+0uBilwLq
|
||||||
|
tk/07lNiYDGH5woy8E5mt62QtjaIbpVfgoCEwUEBWutDKWXNtYypVDabyWyhbhEu
|
||||||
|
abn2HX0L9smxqFNTcjCvKF/J7I74HQQUvVPKnIOlgMx1TOXBNcMLMXQekc/lz/+v
|
||||||
|
5nQjPQKBgQDjdJABeiy9tU0tzLWUVc5QoQKnlfSJoFLis46REb1yHwU9OjTc05Wx
|
||||||
|
5lAXdTjDmnelDdGWNWHjWOiKSkTxhvQD3jXriI5y8Sdxe3zS3ikYvbMbi97GJz0O
|
||||||
|
5oyNJo6/froW1dLkJJWR8hg2PQbtoOo6l9HHSd91BnJJ4qFbq9ZrXQ==
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
|
||||||
|
testKey2 = `
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEA6z1LOg1ZCqb0lytXWZ+MRBpMHEXOoTOLYgfZXt1IYyE3Z758
|
||||||
|
cyalk0NYQhY5cZDsXPYWPvAHiPMUxutWkoxFwby56S+AbIMa3/Is+ILrHRJs8Exn
|
||||||
|
ZkpyrYFxPX12app2kErdmAkHSx0Z5/kuXiz96PHs8S8/ZbyZolLHzdfLtSzjvRm5
|
||||||
|
Zue5iFzsf19NJz5CIBfv8g5lRwtE8wNJoRSpn1xq7fqfuA0weDNFPzjlNWRLy6aa
|
||||||
|
rK7qJexRkmkCs4sLgyl+9NODYJpvmN8E1yhyC27E0joI6rBFVW7Ihv+cSPCdDzGp
|
||||||
|
EWe81x3AeqAa3mjVqkiq4u4Z2i8JDgBaPboqJwIDAQABAoIBAAFdLZ58jVOefDSU
|
||||||
|
L8F5R1rtvBs93GDa56f926jNJ6pLewLC+/2+757W+SAI+PRLntM7Kg3bXm/Q2QH+
|
||||||
|
Q1Y+MflZmspbWCdI61L5GIGoYKyeers59i+FpvySj5GHtLQRiTZ0+Kv1AXHSDWBm
|
||||||
|
9XneUOqU3IbZe0ifu1RRno72/VtjkGXbW8Mkkw+ohyGbIeTx/0/JQ6sSNZTT3Vk7
|
||||||
|
8i4IXptq3HSF0/vqZuah8rShoeNq72pD1YLM9YPdL5by1QkDLnqATDiCpLBTCaNV
|
||||||
|
I8sqYEun+HYbQzBj8ZACG2JVZpEEidONWQHw5BPWO95DSZYrVnEkuCqeH+u5vYt7
|
||||||
|
CHuJ3AECgYEA+W3v5z+j91w1VPHS0VB3SCDMouycAMIUnJPAbt+0LPP0scUFsBGE
|
||||||
|
hPAKddC54pmMZRQ2KIwBKiyWfCrJ8Xz8Yogn7fJgmwTHidJBr2WQpIEkNGlK3Dzi
|
||||||
|
jXL2sh0yC7sHvn0DqiQ79l/e7yRbSnv2wrTJEczOOH2haD7/tBRyCYECgYEA8W+q
|
||||||
|
E9YyGvEltnPFaOxofNZ8LHVcZSsQI5b6fc0iE7fjxFqeXPXEwGSOTwqQLQRiHn9b
|
||||||
|
CfPmIG4Vhyq0otVmlPvUnfBZ2OK+tl5X2/mQFO3ROMdvpi0KYa994uqfJdSTaqLn
|
||||||
|
jjoKFB906UFHnDQDLZUNiV1WwnkTglgLc+xrd6cCgYEAqqthyv6NyBTM3Tm2gcio
|
||||||
|
Ra9Dtntl51LlXZnvwy3IkDXBCd6BHM9vuLKyxZiziGx+Vy90O1xI872cnot8sINQ
|
||||||
|
Am+dur/tAEVN72zxyv0Y8qb2yfH96iKy9gxi5s75TnOEQgAygLnYWaWR2lorKRUX
|
||||||
|
bHTdXBOiS58S0UzCFEslGIECgYBqkO4SKWYeTDhoKvuEj2yjRYyzlu28XeCWxOo1
|
||||||
|
otiauX0YSyNBRt2cSgYiTzhKFng0m+QUJYp63/wymB/5C5Zmxi0XtWIDADpLhqLj
|
||||||
|
HmmBQ2Mo26alQ5YkffBju0mZyhVzaQop1eZi8WuKFV1FThPlB7hc3E0SM5zv2Grd
|
||||||
|
tQnOWwKBgQC40yZY0PcjuILhy+sIc0Wvh7LUA7taSdTye149kRvbvsCDN7Jh75lM
|
||||||
|
USjhLXY0Nld2zBm9r8wMb81mXH29uvD+tDqqsICvyuKlA/tyzXR+QTr7dCVKVwu0
|
||||||
|
1YjCJ36UpTsLre2f8nOSLtNmRfDPtbOE2mkOoO9dD9UU0XZwnvn9xw==
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
)
|
||||||
|
|
||||||
|
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 mustParseX509CertificateChain(data string) schema.X509CertificateChain {
|
||||||
|
chain, err := schema.NewX509CertificateChain(data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return *chain
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
"github.com/ory/fosite/token/jwt"
|
"github.com/ory/fosite/token/jwt"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/valyala/fasthttp"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||||
"github.com/authelia/authelia/v4/internal/model"
|
"github.com/authelia/authelia/v4/internal/model"
|
||||||
|
@ -35,7 +36,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
||||||
ctx.Logger.Errorf("UserInfo Request failed with error: %+v", rfc)
|
ctx.Logger.Errorf("UserInfo Request failed with error: %+v", rfc)
|
||||||
|
|
||||||
if rfc.StatusCode() == http.StatusUnauthorized {
|
if rfc.StatusCode() == http.StatusUnauthorized {
|
||||||
rw.Header().Set("WWW-Authenticate", fmt.Sprintf(`Bearer error="%s",error_description="%s"`, rfc.ErrorField, rfc.GetDescription()))
|
rw.Header().Set(fasthttp.HeaderWWWAuthenticate, fmt.Sprintf(`Bearer error="%s",error_description="%s"`, rfc.ErrorField, rfc.GetDescription()))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Providers.OpenIDConnect.WriteError(rw, req, err)
|
ctx.Providers.OpenIDConnect.WriteError(rw, req, err)
|
||||||
|
@ -90,9 +91,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
||||||
|
|
||||||
claims["aud"] = audience
|
claims["aud"] = audience
|
||||||
|
|
||||||
var (
|
var token string
|
||||||
keyID, token string
|
|
||||||
)
|
|
||||||
|
|
||||||
ctx.Logger.Tracef("UserInfo Response with id '%s' on client with id '%s' is being sent with the following claims: %+v", requester.GetID(), clientID, claims)
|
ctx.Logger.Tracef("UserInfo Response with id '%s' on client with id '%s' is being sent with the following claims: %+v", requester.GetID(), clientID, claims)
|
||||||
|
|
||||||
|
@ -109,14 +108,8 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,
|
||||||
claims["jti"] = jti.String()
|
claims["jti"] = jti.String()
|
||||||
claims["iat"] = time.Now().Unix()
|
claims["iat"] = time.Now().Unix()
|
||||||
|
|
||||||
if keyID, err = ctx.Providers.OpenIDConnect.KeyManager.Strategy().GetPublicKeyID(req.Context()); err != nil {
|
|
||||||
ctx.Providers.OpenIDConnect.WriteError(rw, req, fosite.ErrServerError.WithHintf("Could not find the active JWK."))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
headers := &jwt.Headers{
|
headers := &jwt.Headers{
|
||||||
Extra: map[string]interface{}{"kid": keyID},
|
Extra: map[string]any{"kid": ctx.Providers.OpenIDConnect.KeyManager.GetActiveKeyID()},
|
||||||
}
|
}
|
||||||
|
|
||||||
if token, _, err = ctx.Providers.OpenIDConnect.KeyManager.Strategy().Generate(req.Context(), claims, headers); err != nil {
|
if token, _, err = ctx.Providers.OpenIDConnect.KeyManager.Strategy().Generate(req.Context(), claims, headers); err != nil {
|
||||||
|
|
|
@ -37,8 +37,10 @@ func LoggerCtxPrintf(level logrus.Level) (logger *CtxPrintfLogger) {
|
||||||
func InitializeLogger(config schema.LogConfiguration, log bool) error {
|
func InitializeLogger(config schema.LogConfiguration, log bool) error {
|
||||||
setLevelStr(config.Level, log)
|
setLevelStr(config.Level, log)
|
||||||
|
|
||||||
callerLevels := []logrus.Level{}
|
var callerLevels []logrus.Level
|
||||||
|
|
||||||
stackLevels := []logrus.Level{logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel}
|
stackLevels := []logrus.Level{logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel}
|
||||||
|
|
||||||
logrus.AddHook(logrus_stack.NewHook(callerLevels, stackLevels))
|
logrus.AddHook(logrus_stack.NewHook(callerLevels, stackLevels))
|
||||||
|
|
||||||
if config.Format == logFormatJSON {
|
if config.Format == logFormatJSON {
|
||||||
|
|
|
@ -9,19 +9,17 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ory/fosite/token/jwt"
|
"github.com/ory/fosite/token/jwt"
|
||||||
jose "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/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewKeyManagerWithConfiguration when provided a schema.OpenIDConnectConfiguration creates a new KeyManager and adds an
|
// NewKeyManagerWithConfiguration when provided a schema.OpenIDConnectConfiguration creates a new KeyManager and adds an
|
||||||
// initial key to the manager.
|
// initial key to the manager.
|
||||||
func NewKeyManagerWithConfiguration(configuration *schema.OpenIDConnectConfiguration) (manager *KeyManager, err error) {
|
func NewKeyManagerWithConfiguration(config *schema.OpenIDConnectConfiguration) (manager *KeyManager, err error) {
|
||||||
manager = NewKeyManager()
|
manager = NewKeyManager()
|
||||||
|
|
||||||
_, _, err = manager.AddActivePrivateKeyData(configuration.IssuerPrivateKey)
|
if _, err = manager.AddActiveJWK(config.IssuerCertificateChain, config.IssuerPrivateKey); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,174 +28,152 @@ func NewKeyManagerWithConfiguration(configuration *schema.OpenIDConnectConfigura
|
||||||
|
|
||||||
// NewKeyManager creates a new empty KeyManager.
|
// NewKeyManager creates a new empty KeyManager.
|
||||||
func NewKeyManager() (manager *KeyManager) {
|
func NewKeyManager() (manager *KeyManager) {
|
||||||
manager = new(KeyManager)
|
return &KeyManager{
|
||||||
manager.keys = map[string]*rsa.PrivateKey{}
|
jwks: &jose.JSONWebKeySet{},
|
||||||
manager.keySet = new(jose.JSONWebKeySet)
|
}
|
||||||
|
|
||||||
return manager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strategy returns the RS256JWTStrategy.
|
// Strategy returns the fosite jwt.JWTStrategy.
|
||||||
func (m *KeyManager) Strategy() (strategy *RS256JWTStrategy) {
|
func (m *KeyManager) Strategy() (strategy jwt.JWTStrategy) {
|
||||||
return m.strategy
|
if m.jwk == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.jwk.Strategy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetKeySet returns the joseJSONWebKeySet containing the rsa.PublicKey types.
|
// GetKeySet returns the joseJSONWebKeySet containing the rsa.PublicKey types.
|
||||||
func (m *KeyManager) GetKeySet() (keySet *jose.JSONWebKeySet) {
|
func (m *KeyManager) GetKeySet() (jwks *jose.JSONWebKeySet) {
|
||||||
return m.keySet
|
return m.jwks
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetActiveWebKey obtains the currently active jose.JSONWebKey.
|
// GetActiveJWK obtains the currently active jose.JSONWebKey.
|
||||||
func (m *KeyManager) GetActiveWebKey() (webKey *jose.JSONWebKey, err error) {
|
func (m *KeyManager) GetActiveJWK() (jwk *jose.JSONWebKey, err error) {
|
||||||
webKeys := m.keySet.Key(m.activeKeyID)
|
if m.jwks == nil || m.jwk == nil {
|
||||||
if len(webKeys) == 1 {
|
return nil, errors.New("could not obtain the active JWK from an improperly configured key manager")
|
||||||
return &webKeys[0], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(webKeys) == 0 {
|
jwks := m.jwks.Key(m.jwk.id)
|
||||||
|
|
||||||
|
if len(jwks) == 1 {
|
||||||
|
return &jwks[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(jwks) == 0 {
|
||||||
return nil, errors.New("could not find a key with the active key id")
|
return nil, errors.New("could not find a key with the active key id")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &webKeys[0], errors.New("multiple keys with the same key id")
|
return nil, errors.New("multiple keys with the same key id")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetActiveKeyID returns the key id of the currently active key.
|
// GetActiveKeyID returns the key id of the currently active key.
|
||||||
func (m *KeyManager) GetActiveKeyID() (keyID string) {
|
func (m *KeyManager) GetActiveKeyID() (keyID string) {
|
||||||
return m.activeKeyID
|
if m.jwk == nil {
|
||||||
}
|
return ""
|
||||||
|
|
||||||
// GetActiveKey returns the rsa.PublicKey of the currently active key.
|
|
||||||
func (m *KeyManager) GetActiveKey() (key *rsa.PublicKey, err error) {
|
|
||||||
if key, ok := m.keys[m.activeKeyID]; ok {
|
|
||||||
return &key.PublicKey, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("failed to retrieve active public key")
|
return m.jwk.id
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetActivePrivateKey returns the rsa.PrivateKey of the currently active key.
|
// GetActivePrivateKey returns the rsa.PrivateKey of the currently active key.
|
||||||
func (m *KeyManager) GetActivePrivateKey() (key *rsa.PrivateKey, err error) {
|
func (m *KeyManager) GetActivePrivateKey() (key *rsa.PrivateKey, err error) {
|
||||||
if key, ok := m.keys[m.activeKeyID]; ok {
|
if m.jwk == nil {
|
||||||
return key, nil
|
return nil, errors.New("failed to retrieve active private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("failed to retrieve active private key")
|
return m.jwk.key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddActivePrivateKeyData adds a rsa.PublicKey given the key in the PEM string format, then sets it to the active key.
|
// AddActiveJWK is used to add a cert and key pair.
|
||||||
func (m *KeyManager) AddActivePrivateKeyData(data string) (key *rsa.PrivateKey, webKey *jose.JSONWebKey, err error) {
|
func (m *KeyManager) AddActiveJWK(chain schema.X509CertificateChain, key *rsa.PrivateKey) (jwk *JWK, err error) {
|
||||||
ikey, err := utils.ParseX509FromPEM([]byte(data))
|
// TODO: Add a mutex when implementing key rotation to be utilized here and in methods which retrieve the JWK or JWKS.
|
||||||
if err != nil {
|
if m.jwk, err = NewJWK(chain, key); err != nil {
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if key, ok = ikey.(*rsa.PrivateKey); !ok {
|
|
||||||
return nil, nil, errors.New("key must be an RSA private key")
|
|
||||||
}
|
|
||||||
|
|
||||||
webKey, err = m.AddActivePrivateKey(key)
|
|
||||||
|
|
||||||
return key, webKey, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddActivePrivateKey adds a rsa.PublicKey, then sets it to the active key.
|
|
||||||
func (m *KeyManager) AddActivePrivateKey(key *rsa.PrivateKey) (webKey *jose.JSONWebKey, err error) {
|
|
||||||
wk := jose.JSONWebKey{
|
|
||||||
Key: &key.PublicKey,
|
|
||||||
Algorithm: "RS256",
|
|
||||||
Use: "sig",
|
|
||||||
}
|
|
||||||
|
|
||||||
keyID, err := wk.Thumbprint(crypto.SHA1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
strKeyID := strings.ToLower(fmt.Sprintf("%x", keyID))
|
m.jwks.Keys = append(m.jwks.Keys, *m.jwk.JSONWebKey())
|
||||||
if len(strKeyID) >= 7 {
|
|
||||||
// Shorten the key if it's greater than 7 to a length of exactly 7.
|
|
||||||
strKeyID = strKeyID[0:6]
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := m.keys[strKeyID]; ok {
|
return m.jwk, nil
|
||||||
return nil, fmt.Errorf("key id %s already exists", strKeyID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add Mutex here when implementing key rotation.
|
|
||||||
wk.KeyID = strKeyID
|
|
||||||
m.keySet.Keys = append(m.keySet.Keys, wk)
|
|
||||||
m.keys[strKeyID] = key
|
|
||||||
m.activeKeyID = strKeyID
|
|
||||||
|
|
||||||
m.strategy, err = NewRS256JWTStrategy(wk.KeyID, key)
|
|
||||||
if err != nil {
|
|
||||||
return &wk, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &wk, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRS256JWTStrategy returns a new RS256JWTStrategy.
|
// JWTStrategy is a decorator struct for the fosite jwt.JWTStrategy.
|
||||||
func NewRS256JWTStrategy(id string, key *rsa.PrivateKey) (strategy *RS256JWTStrategy, err error) {
|
type JWTStrategy struct {
|
||||||
strategy = new(RS256JWTStrategy)
|
jwt.JWTStrategy
|
||||||
strategy.JWTStrategy = new(jwt.RS256JWTStrategy)
|
|
||||||
|
|
||||||
strategy.SetKey(id, key)
|
id string
|
||||||
|
|
||||||
return strategy, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RS256JWTStrategy is a decorator struct for the fosite RS256JWTStrategy.
|
|
||||||
type RS256JWTStrategy struct {
|
|
||||||
JWTStrategy *jwt.RS256JWTStrategy
|
|
||||||
|
|
||||||
keyID string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeyID returns the key id.
|
// KeyID returns the key id.
|
||||||
func (s RS256JWTStrategy) KeyID() (id string) {
|
func (s *JWTStrategy) KeyID() (id string) {
|
||||||
return s.keyID
|
return s.id
|
||||||
}
|
|
||||||
|
|
||||||
// SetKey sets the provided key id and key as the active key (this is what triggers fosite to use it).
|
|
||||||
func (s *RS256JWTStrategy) SetKey(id string, key *rsa.PrivateKey) {
|
|
||||||
s.keyID = id
|
|
||||||
s.JWTStrategy.PrivateKey = key
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash is a decorator func for the underlying fosite RS256JWTStrategy.
|
|
||||||
func (s *RS256JWTStrategy) Hash(ctx context.Context, in []byte) ([]byte, error) {
|
|
||||||
return s.JWTStrategy.Hash(ctx, in)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSigningMethodLength is a decorator func for the underlying fosite RS256JWTStrategy.
|
|
||||||
func (s *RS256JWTStrategy) GetSigningMethodLength() int {
|
|
||||||
return s.JWTStrategy.GetSigningMethodLength()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSignature is a decorator func for the underlying fosite RS256JWTStrategy.
|
|
||||||
func (s *RS256JWTStrategy) GetSignature(ctx context.Context, token string) (string, error) {
|
|
||||||
return s.JWTStrategy.GetSignature(ctx, token)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate is a decorator func for the underlying fosite RS256JWTStrategy.
|
|
||||||
func (s *RS256JWTStrategy) Generate(ctx context.Context, claims jwt.MapClaims, header jwt.Mapper) (string, string, error) {
|
|
||||||
return s.JWTStrategy.Generate(ctx, claims, header)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate is a decorator func for the underlying fosite RS256JWTStrategy.
|
|
||||||
func (s *RS256JWTStrategy) Validate(ctx context.Context, token string) (string, error) {
|
|
||||||
return s.JWTStrategy.Validate(ctx, token)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode is a decorator func for the underlying fosite RS256JWTStrategy.
|
|
||||||
func (s *RS256JWTStrategy) Decode(ctx context.Context, token string) (*jwt.Token, error) {
|
|
||||||
return s.JWTStrategy.Decode(ctx, token)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPublicKeyID is a decorator func for the underlying fosite RS256JWTStrategy.
|
// GetPublicKeyID is a decorator func for the underlying fosite RS256JWTStrategy.
|
||||||
func (s *RS256JWTStrategy) GetPublicKeyID(_ context.Context) (string, error) {
|
func (s *JWTStrategy) GetPublicKeyID(_ context.Context) (string, error) {
|
||||||
return s.keyID, nil
|
return s.id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewJWK creates a new JWK.
|
||||||
|
func NewJWK(chain schema.X509CertificateChain, key *rsa.PrivateKey) (j *JWK, err error) {
|
||||||
|
if key == nil {
|
||||||
|
return nil, fmt.Errorf("JWK is not properly initialized: missing key")
|
||||||
|
}
|
||||||
|
|
||||||
|
j = &JWK{
|
||||||
|
key: key,
|
||||||
|
chain: chain,
|
||||||
|
}
|
||||||
|
|
||||||
|
jwk := &jose.JSONWebKey{
|
||||||
|
Algorithm: "RS256",
|
||||||
|
Use: "sig",
|
||||||
|
Key: &key.PublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
var thumbprint []byte
|
||||||
|
|
||||||
|
if thumbprint, err = jwk.Thumbprint(crypto.SHA1); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to calculate SHA1 thumbprint for certificate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
j.id = strings.ToLower(fmt.Sprintf("%x", thumbprint))
|
||||||
|
|
||||||
|
if len(j.id) >= 7 {
|
||||||
|
j.id = j.id[:6]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(j.id) >= 7 {
|
||||||
|
j.id = j.id[:6]
|
||||||
|
}
|
||||||
|
|
||||||
|
return j, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// JWK is a utility wrapper for JSON Web Key's.
|
||||||
|
type JWK struct {
|
||||||
|
id string
|
||||||
|
key *rsa.PrivateKey
|
||||||
|
chain schema.X509CertificateChain
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strategy returns the relevant jwt.JWTStrategy for this JWT.
|
||||||
|
func (j *JWK) Strategy() (strategy jwt.JWTStrategy) {
|
||||||
|
return &JWTStrategy{id: j.id, JWTStrategy: &jwt.RS256JWTStrategy{PrivateKey: j.key}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSONWebKey returns the relevant *jose.JSONWebKey for this JWT.
|
||||||
|
func (j *JWK) JSONWebKey() (jwk *jose.JSONWebKey) {
|
||||||
|
jwk = &jose.JSONWebKey{
|
||||||
|
Key: &j.key.PublicKey,
|
||||||
|
KeyID: j.id,
|
||||||
|
Algorithm: "RS256",
|
||||||
|
Use: "sig",
|
||||||
|
Certificates: j.chain.Certificates(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(jwk.Certificates) != 0 {
|
||||||
|
jwk.CertificateThumbprintSHA1, jwk.CertificateThumbprintSHA256 = j.chain.Thumbprint(crypto.SHA1), j.chain.Thumbprint(crypto.SHA256)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jwk
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,42 +8,37 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestKeyManager_AddActiveKeyData(t *testing.T) {
|
func TestKeyManager_AddActiveJWK(t *testing.T) {
|
||||||
manager := NewKeyManager()
|
manager := NewKeyManager()
|
||||||
assert.Nil(t, manager.strategy)
|
assert.Nil(t, manager.jwk)
|
||||||
assert.Nil(t, manager.Strategy())
|
assert.Nil(t, manager.Strategy())
|
||||||
|
|
||||||
key, wk, err := manager.AddActivePrivateKeyData(exampleIssuerPrivateKey)
|
j, err := manager.AddActiveJWK(schema.X509CertificateChain{}, mustParseRSAPrivateKey(exampleIssuerPrivateKey))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, key)
|
require.NotNil(t, j)
|
||||||
require.NotNil(t, wk)
|
require.NotNil(t, manager.jwk)
|
||||||
require.NotNil(t, manager.strategy)
|
|
||||||
require.NotNil(t, manager.Strategy())
|
require.NotNil(t, manager.Strategy())
|
||||||
|
|
||||||
thumbprint, err := wk.Thumbprint(crypto.SHA1)
|
thumbprint, err := j.JSONWebKey().Thumbprint(crypto.SHA1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
kid := strings.ToLower(fmt.Sprintf("%x", thumbprint)[0:6])
|
kid := strings.ToLower(fmt.Sprintf("%x", thumbprint)[:6])
|
||||||
assert.Equal(t, manager.activeKeyID, kid)
|
assert.Equal(t, manager.jwk.id, kid)
|
||||||
assert.Equal(t, kid, wk.KeyID)
|
assert.Equal(t, kid, j.JSONWebKey().KeyID)
|
||||||
assert.Len(t, manager.keys, 1)
|
assert.Len(t, manager.jwks.Keys, 1)
|
||||||
assert.Len(t, manager.keySet.Keys, 1)
|
|
||||||
assert.Contains(t, manager.keys, kid)
|
|
||||||
|
|
||||||
keys := manager.keySet.Key(kid)
|
keys := manager.jwks.Key(kid)
|
||||||
assert.Equal(t, keys[0].KeyID, kid)
|
assert.Equal(t, keys[0].KeyID, kid)
|
||||||
|
|
||||||
privKey, err := manager.GetActivePrivateKey()
|
privKey, err := manager.GetActivePrivateKey()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, privKey)
|
assert.NotNil(t, privKey)
|
||||||
|
|
||||||
pubKey, err := manager.GetActiveKey()
|
webKey, err := manager.GetActiveJWK()
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, pubKey)
|
|
||||||
|
|
||||||
webKey, err := manager.GetActiveWebKey()
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, webKey)
|
assert.NotNil(t, webKey)
|
||||||
|
|
||||||
|
|
|
@ -14,17 +14,16 @@ import (
|
||||||
|
|
||||||
// NewOpenIDConnectProvider new-ups a OpenIDConnectProvider.
|
// NewOpenIDConnectProvider new-ups a OpenIDConnectProvider.
|
||||||
func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, storageProvider storage.Provider) (provider OpenIDConnectProvider, err error) {
|
func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, storageProvider storage.Provider) (provider OpenIDConnectProvider, err error) {
|
||||||
provider = OpenIDConnectProvider{
|
|
||||||
Fosite: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return provider, nil
|
return provider, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.Store = NewOpenIDConnectStore(config, storageProvider)
|
provider = OpenIDConnectProvider{
|
||||||
|
Fosite: nil,
|
||||||
|
Store: NewOpenIDConnectStore(config, storageProvider),
|
||||||
|
}
|
||||||
|
|
||||||
composeConfiguration := &compose.Config{
|
cconfig := &compose.Config{
|
||||||
AccessTokenLifespan: config.AccessTokenLifespan,
|
AccessTokenLifespan: config.AccessTokenLifespan,
|
||||||
AuthorizeCodeLifespan: config.AuthorizeCodeLifespan,
|
AuthorizeCodeLifespan: config.AuthorizeCodeLifespan,
|
||||||
IDTokenLifespan: config.IDTokenLifespan,
|
IDTokenLifespan: config.IDTokenLifespan,
|
||||||
|
@ -50,19 +49,19 @@ func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, storage
|
||||||
|
|
||||||
strategy := &compose.CommonStrategy{
|
strategy := &compose.CommonStrategy{
|
||||||
CoreStrategy: compose.NewOAuth2HMACStrategy(
|
CoreStrategy: compose.NewOAuth2HMACStrategy(
|
||||||
composeConfiguration,
|
cconfig,
|
||||||
[]byte(utils.HashSHA256FromString(config.HMACSecret)),
|
[]byte(utils.HashSHA256FromString(config.HMACSecret)),
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
OpenIDConnectTokenStrategy: compose.NewOpenIDConnectStrategy(
|
OpenIDConnectTokenStrategy: compose.NewOpenIDConnectStrategy(
|
||||||
composeConfiguration,
|
cconfig,
|
||||||
key,
|
key,
|
||||||
),
|
),
|
||||||
JWTStrategy: provider.KeyManager.Strategy(),
|
JWTStrategy: provider.KeyManager.Strategy(),
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.Fosite = compose.Compose(
|
provider.Fosite = compose.Compose(
|
||||||
composeConfiguration,
|
cconfig,
|
||||||
provider.Store,
|
provider.Store,
|
||||||
strategy,
|
strategy,
|
||||||
PlainTextHasher{},
|
PlainTextHasher{},
|
||||||
|
@ -109,7 +108,7 @@ func (p OpenIDConnectProvider) Pairwise() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes data with herodot.JSONWriter.
|
// Write writes data with herodot.JSONWriter.
|
||||||
func (p OpenIDConnectProvider) Write(w http.ResponseWriter, r *http.Request, e interface{}, opts ...herodot.EncoderOptions) {
|
func (p OpenIDConnectProvider) Write(w http.ResponseWriter, r *http.Request, e any, opts ...herodot.EncoderOptions) {
|
||||||
p.herodot.Write(w, r, e, opts...)
|
p.herodot.Write(w, r, e, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package oidc
|
package oidc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -20,17 +23,10 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_NotConfigured(t *testing
|
||||||
assert.Nil(t, provider.Store)
|
assert.Nil(t, provider.Store)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_BadIssuerKey(t *testing.T) {
|
|
||||||
_, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
|
||||||
IssuerPrivateKey: "BAD KEY",
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
assert.Error(t, err, "abc")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing.T) {
|
func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing.T) {
|
||||||
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
|
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||||
EnablePKCEPlainChallenge: true,
|
EnablePKCEPlainChallenge: true,
|
||||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
@ -63,8 +59,9 @@ func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing
|
||||||
|
|
||||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *testing.T) {
|
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *testing.T) {
|
||||||
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||||
|
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
|
@ -102,8 +99,9 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *tes
|
||||||
|
|
||||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnownConfiguration(t *testing.T) {
|
||||||
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||||
|
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
|
@ -193,8 +191,9 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
||||||
|
|
||||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfiguration(t *testing.T) {
|
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfiguration(t *testing.T) {
|
||||||
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||||
|
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
|
@ -270,7 +269,8 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
|
||||||
|
|
||||||
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnownConfigurationWithPlainPKCE(t *testing.T) {
|
func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnownConfigurationWithPlainPKCE(t *testing.T) {
|
||||||
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
provider, err := NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
|
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||||
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
HMACSecret: "asbdhaaskmdlkamdklasmdlkams",
|
||||||
EnablePKCEPlainChallenge: true,
|
EnablePKCEPlainChallenge: true,
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
|
@ -293,3 +293,21 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
||||||
assert.Equal(t, "S256", disco.CodeChallengeMethodsSupported[0])
|
assert.Equal(t, "S256", disco.CodeChallengeMethodsSupported[0])
|
||||||
assert.Equal(t, "plain", disco.CodeChallengeMethodsSupported[1])
|
assert.Equal(t, "plain", disco.CodeChallengeMethodsSupported[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,8 @@ import (
|
||||||
|
|
||||||
func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
||||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
|
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "myclient",
|
ID: "myclient",
|
||||||
|
@ -44,7 +45,8 @@ func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
||||||
|
|
||||||
func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
||||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
|
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "myclient",
|
ID: "myclient",
|
||||||
|
@ -76,8 +78,9 @@ func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{c1},
|
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||||
|
Clients: []schema.OpenIDConnectClientConfiguration{c1},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
client, err := s.GetFullClient(c1.ID)
|
client, err := s.GetFullClient(c1.ID)
|
||||||
|
@ -103,8 +106,9 @@ func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{c1},
|
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||||
|
Clients: []schema.OpenIDConnectClientConfiguration{c1},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
client, err := s.GetFullClient("another-client")
|
client, err := s.GetFullClient("another-client")
|
||||||
|
@ -114,7 +118,8 @@ func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
||||||
|
|
||||||
func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
|
func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
|
||||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||||
IssuerPrivateKey: exampleIssuerPrivateKey,
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
|
IssuerPrivateKey: mustParseRSAPrivateKey(exampleIssuerPrivateKey),
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "myclient",
|
ID: "myclient",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package oidc
|
package oidc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
|
@ -21,21 +20,21 @@ func NewSession() (session *model.OpenIDSession) {
|
||||||
return &model.OpenIDSession{
|
return &model.OpenIDSession{
|
||||||
DefaultSession: &openid.DefaultSession{
|
DefaultSession: &openid.DefaultSession{
|
||||||
Claims: &jwt.IDTokenClaims{
|
Claims: &jwt.IDTokenClaims{
|
||||||
Extra: map[string]interface{}{},
|
Extra: map[string]any{},
|
||||||
},
|
},
|
||||||
Headers: &jwt.Headers{
|
Headers: &jwt.Headers{
|
||||||
Extra: map[string]interface{}{},
|
Extra: map[string]any{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Extra: map[string]interface{}{},
|
Extra: map[string]any{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSessionWithAuthorizeRequest uses details from an AuthorizeRequester to generate an OpenIDSession.
|
// NewSessionWithAuthorizeRequest uses details from an AuthorizeRequester to generate an OpenIDSession.
|
||||||
func NewSessionWithAuthorizeRequest(issuer, kid, username string, amr []string, extra map[string]interface{},
|
func NewSessionWithAuthorizeRequest(issuer, kid, username string, amr []string, extra map[string]any,
|
||||||
authTime time.Time, consent *model.OAuth2ConsentSession, requester fosite.AuthorizeRequester) (session *model.OpenIDSession) {
|
authTime time.Time, consent *model.OAuth2ConsentSession, requester fosite.AuthorizeRequester) (session *model.OpenIDSession) {
|
||||||
if extra == nil {
|
if extra == nil {
|
||||||
extra = make(map[string]interface{})
|
extra = map[string]any{}
|
||||||
}
|
}
|
||||||
|
|
||||||
session = &model.OpenIDSession{
|
session = &model.OpenIDSession{
|
||||||
|
@ -53,14 +52,14 @@ func NewSessionWithAuthorizeRequest(issuer, kid, username string, amr []string,
|
||||||
AuthenticationMethodsReferences: amr,
|
AuthenticationMethodsReferences: amr,
|
||||||
},
|
},
|
||||||
Headers: &jwt.Headers{
|
Headers: &jwt.Headers{
|
||||||
Extra: map[string]interface{}{
|
Extra: map[string]any{
|
||||||
"kid": kid,
|
"kid": kid,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Subject: consent.Subject.UUID.String(),
|
Subject: consent.Subject.UUID.String(),
|
||||||
Username: username,
|
Username: username,
|
||||||
},
|
},
|
||||||
Extra: map[string]interface{}{},
|
Extra: map[string]any{},
|
||||||
ClientID: requester.GetClient().GetID(),
|
ClientID: requester.GetClient().GetID(),
|
||||||
ChallengeID: consent.ChallengeID,
|
ChallengeID: consent.ChallengeID,
|
||||||
}
|
}
|
||||||
|
@ -122,10 +121,8 @@ type Client struct {
|
||||||
// KeyManager keeps track of all of the active/inactive rsa keys and provides them to services requiring them.
|
// KeyManager keeps track of all of the active/inactive rsa keys and provides them to services requiring them.
|
||||||
// It additionally allows us to add keys for the purpose of key rotation in the future.
|
// It additionally allows us to add keys for the purpose of key rotation in the future.
|
||||||
type KeyManager struct {
|
type KeyManager struct {
|
||||||
activeKeyID string
|
jwk *JWK
|
||||||
keys map[string]*rsa.PrivateKey
|
jwks *jose.JSONWebKeySet
|
||||||
keySet *jose.JSONWebKeySet
|
|
||||||
strategy *RS256JWTStrategy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlainTextHasher implements the fosite.Hasher interface without an actual hashing algo.
|
// PlainTextHasher implements the fosite.Hasher interface without an actual hashing algo.
|
||||||
|
|
|
@ -182,7 +182,7 @@ func (ekb ECDSAKeyBuilder) Build() (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseX509FromPEM parses PEM bytes and returns a PKCS key.
|
// ParseX509FromPEM parses PEM bytes and returns a PKCS key.
|
||||||
func ParseX509FromPEM(data []byte) (key interface{}, err error) {
|
func ParseX509FromPEM(data []byte) (key any, err error) {
|
||||||
block, _ := pem.Decode(data)
|
block, _ := pem.Decode(data)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return nil, errors.New("failed to parse PEM block containing the key")
|
return nil, errors.New("failed to parse PEM block containing the key")
|
||||||
|
|
Loading…
Reference in New Issue