package handlers import ( "context" "crypto/ecdsa" "crypto/rsa" "crypto/sha256" "crypto/x509" "database/sql" "encoding/base64" "encoding/pem" "fmt" "io" "net/http" "net/url" "strings" "testing" "time" "github.com/golang-jwt/jwt/v4" "github.com/golang/mock/gomock" "github.com/google/uuid" "github.com/ory/fosite" "github.com/stretchr/testify/suite" "github.com/valyala/fasthttp" "github.com/authelia/authelia/v4/internal/authorization" "github.com/authelia/authelia/v4/internal/configuration/schema" "github.com/authelia/authelia/v4/internal/mocks" "github.com/authelia/authelia/v4/internal/model" "github.com/authelia/authelia/v4/internal/oidc" ) func TestClientAuthenticationStrategySuite(t *testing.T) { suite.Run(t, &ClientAuthenticationStrategySuite{}) } type ClientAuthenticationStrategySuite struct { suite.Suite issuerURL *url.URL ctrl *gomock.Controller store *mocks.MockStorage provider *oidc.OpenIDConnectProvider } func (s *ClientAuthenticationStrategySuite) GetIssuerURL() *url.URL { if s.issuerURL == nil { s.issuerURL = MustParseRequestURI("https://auth.example.com") } return s.issuerURL } func (s *ClientAuthenticationStrategySuite) GetTokenURL() *url.URL { return s.GetIssuerURL().JoinPath(oidc.EndpointPathToken) } func (s *ClientAuthenticationStrategySuite) GetBaseRequest(body io.Reader) (r *http.Request) { var err error r, err = http.NewRequest(http.MethodPost, s.GetTokenURL().String(), body) s.Require().NoError(err) s.Require().NotNil(r) r.Header.Set(fasthttp.HeaderContentType, "application/x-www-form-urlencoded") return r } func (s *ClientAuthenticationStrategySuite) GetRequest(values *url.Values) (r *http.Request) { var body io.Reader if values != nil { body = strings.NewReader(values.Encode()) } r = s.GetBaseRequest(body) s.Require().NoError(r.ParseForm()) return r } func (s *ClientAuthenticationStrategySuite) GetAssertionValues(token string) *url.Values { values := &url.Values{} values.Set(oidc.FormParameterClientAssertionType, oidc.ClientAssertionJWTBearerType) if token != "" { values.Set(oidc.FormParameterClientAssertion, token) } return values } func (s *ClientAuthenticationStrategySuite) GetClientValues(id string) *url.Values { values := &url.Values{} values.Set(oidc.FormParameterClientID, id) return values } func (s *ClientAuthenticationStrategySuite) GetClientValuesPost(id, secret string) *url.Values { values := s.GetClientValues(id) values.Set(oidc.FormParameterClientSecret, secret) return values } func (s *ClientAuthenticationStrategySuite) GetClientSecretBasicRequest(id, secret string) (r *http.Request) { values := s.GetClientValues(id) r = s.GetRequest(values) r.SetBasicAuth(id, secret) return r } func (s *ClientAuthenticationStrategySuite) GetClientSecretPostRequest(id, secret string) (r *http.Request) { values := s.GetClientValuesPost(id, secret) return s.GetRequest(values) } func (s *ClientAuthenticationStrategySuite) GetAssertionRequest(token string) (r *http.Request) { values := s.GetAssertionValues(token) return s.GetRequest(values) } func (s *ClientAuthenticationStrategySuite) GetCtx() oidc.OpenIDConnectContext { return &oidc.MockOpenIDConnectContext{ Context: context.Background(), MockIssuerURL: s.GetIssuerURL(), } } func (s *ClientAuthenticationStrategySuite) SetupTest() { s.ctrl = gomock.NewController(s.T()) s.store = mocks.NewMockStorage(s.ctrl) var err error secret := MustDecodeSecret("$plaintext$client-secret") s.provider, err = oidc.NewOpenIDConnectProvider(&schema.OpenIDConnectConfiguration{ IssuerCertificateChain: schema.X509CertificateChain{}, IssuerPrivateKey: MustParseRSAPrivateKey(exampleRSAPrivateKey), HMACSecret: "abc123", Clients: []schema.OpenIDConnectClientConfiguration{ { ID: "hs256", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA256, }, { ID: "hs384", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA384, }, { ID: "hs512", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA512, }, { ID: "rs256", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA256, }, { ID: "rs384", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA384, }, { ID: "rs512", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA512, }, { ID: "ps256", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA256, }, { ID: "ps384", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA384, }, { ID: "ps512", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA512, }, { ID: "es256", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP256AndSHA256, }, { ID: "es384", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP384AndSHA384, }, { ID: "es512", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP521AndSHA512, }, { ID: "hs5122", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA512, }, { ID: "hashed", Secret: MustDecodeSecret("$pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng"), Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretJWT, TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA512, }, { ID: oidc.ClientAuthMethodClientSecretBasic, Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretBasic, TokenEndpointAuthSigningAlg: oidc.SigningAlgNone, }, { ID: oidc.ClientAuthMethodNone, Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodNone, TokenEndpointAuthSigningAlg: oidc.SigningAlgNone, }, { ID: oidc.ClientAuthMethodClientSecretPost, Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretPost, TokenEndpointAuthSigningAlg: oidc.SigningAlgNone, }, { ID: "bad_method", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, TokenEndpointAuthMethod: "bad_method", TokenEndpointAuthSigningAlg: oidc.SigningAlgNone, }, { ID: "base", Secret: secret, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, }, { ID: "public", Public: true, Policy: authorization.OneFactor.String(), TokenEndpointAuthMethod: oidc.ClientAuthMethodNone, RedirectURIs: []string{ "https://client.example.com", }, }, { ID: "public-nomethod", Public: true, Policy: authorization.OneFactor.String(), RedirectURIs: []string{ "https://client.example.com", }, }, { ID: "public-basic", Public: true, Policy: authorization.OneFactor.String(), TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretBasic, RedirectURIs: []string{ "https://client.example.com", }, }, }, }, s.store, nil) s.Require().NoError(err) } func (s *ClientAuthenticationStrategySuite) TestShouldValidateAssertionHS256() { assertion := NewAssertion("hs256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS256, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) ctx := s.GetCtx() gomock.InOrder( s.store. EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). Return(nil, sql.ErrNoRows), s.store. EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). Return(nil), ) client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.NoError(ErrorToRFC6749ErrorTest(err)) s.Require().NotNil(client) s.Equal("hs256", client.GetID()) } func (s *ClientAuthenticationStrategySuite) TestShouldValidateAssertionHS384() { assertion := NewAssertion("hs384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS384, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) ctx := s.GetCtx() gomock.InOrder( s.store. EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). Return(nil, sql.ErrNoRows), s.store. EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). Return(nil), ) client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.NoError(ErrorToRFC6749ErrorTest(err)) s.Require().NotNil(client) s.Equal("hs384", client.GetID()) } func (s *ClientAuthenticationStrategySuite) TestShouldValidateAssertionHS512() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) ctx := s.GetCtx() gomock.InOrder( s.store. EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). Return(nil, sql.ErrNoRows), s.store. EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). Return(nil), ) client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.NoError(ErrorToRFC6749ErrorTest(err)) s.Require().NotNil(client) s.Equal("hs512", client.GetID()) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnMismatchedAlg() { assertion := NewAssertion("rs256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The 'client_assertion' uses signing algorithm 'HS512' but the requested OAuth 2.0 Client enforces signing algorithm 'RS256'.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnMismatchedAlgSameMethod() { assertion := NewAssertion("hs256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The 'client_assertion' uses signing algorithm 'HS512' but the requested OAuth 2.0 Client enforces signing algorithm 'HS256'.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS256() { assertion := NewAssertion("rs256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion) token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey)) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS384() { assertion := NewAssertion("rs384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS384, assertion) token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey)) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS512() { assertion := NewAssertion("rs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS512, assertion) token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey)) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS256() { assertion := NewAssertion("ps256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS256, assertion) token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey)) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS384() { assertion := NewAssertion("ps384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS384, assertion) token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey)) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS512() { assertion := NewAssertion("ps512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS512, assertion) token, err := assertionJWT.SignedString(MustParseRSAPrivateKey(exampleRSAPrivateKey)) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES256() { assertion := NewAssertion("es256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES256, assertion) token, err := assertionJWT.SignedString(MustParseECPrivateKey(exampleECP256PrivateKey)) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES384() { assertion := NewAssertion("es384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES384, assertion) token, err := assertionJWT.SignedString(MustParseECPrivateKey(exampleECP384PrivateKey)) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES512() { assertion := NewAssertion("es512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES512, assertion) token, err := assertionJWT.SignedString(MustParseECPrivateKey(exampleECP521PrivateKey)) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnJTIKnown() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) ctx := s.GetCtx() gomock.InOrder( s.store. EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). Return(nil, sql.ErrNoRows), s.store. EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). Return(fosite.ErrJTIKnown), ) client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "The jti was already used.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldValidateJWTWithArbitraryClaims() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) a := assertion.ToMapClaims() a["aaa"] = "abc" assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, a) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) ctx := s.GetCtx() gomock.InOrder( s.store. EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). Return(nil, sql.ErrNoRows), s.store. EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). Return(nil), ) client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.NoError(ErrorToRFC6749ErrorTest(err)) s.Require().NotNil(client) s.Equal("hs512", client.GetID()) } func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMissingSubClaim() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) a := assertion.ToMapClaims() delete(a, oidc.ClaimSubject) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, a) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The claim 'sub' from the client_assertion JSON Web Token is undefined.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailWithInvalidExpClaim() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) a := assertion.ToMapClaims() a[oidc.ClaimExpirationTime] = "not a number" assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, a) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The token is expired.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMissingIssClaim() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) a := assertion.ToMapClaims() delete(a, oidc.ClaimIssuer) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, a) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'iss' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailWithInvalidAudClaim() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertion.Audience = []string{"notvalid"} assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) ctx := s.GetCtx() gomock.InOrder( s.store. EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). Return(nil, sql.ErrNoRows), s.store. EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). Return(nil), ) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'audience' from 'client_assertion' must match the authorization server's token endpoint 'https://auth.example.com/api/oidc/token'.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailWithInvalidAssertionType() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) v := s.GetAssertionValues(token) v.Set(oidc.FormParameterClientAssertionType, "not_valid") r := s.GetRequest(v) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Unknown client_assertion_type 'not_valid'.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMissingJTIClaim() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) a := assertion.ToMapClaims() delete(a, oidc.ClaimJWTID) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, a) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'jti' from 'client_assertion' must be set but is not.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMismatchedIssClaim() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertion.Issuer = "hs256" assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'iss' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldValidateClientSecretPost() { r := s.GetClientSecretPostRequest(oidc.ClientAuthMethodClientSecretPost, "client-secret") client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.NoError(err) s.Require().NotNil(client) s.Equal(oidc.ClientAuthMethodClientSecretPost, client.GetID()) } func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretPostOnClientSecretBasicClient() { r := s.GetClientSecretPostRequest(oidc.ClientAuthMethodClientSecretBasic, "client-secret") client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client supports client authentication method 'client_secret_basic', but method 'client_secret_post' was requested. You must configure the OAuth 2.0 client's 'token_endpoint_auth_method' value to accept 'client_secret_post'.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretPostWrongSecret() { r := s.GetClientSecretPostRequest(oidc.ClientAuthMethodClientSecretPost, "client-secret-bad") client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The provided client secret did not match the registered client secret.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldValidateClientSecretBasic() { r := s.GetClientSecretBasicRequest(oidc.ClientAuthMethodClientSecretBasic, "client-secret") client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.NoError(err) s.Require().NotNil(client) s.Equal(oidc.ClientAuthMethodClientSecretBasic, client.GetID()) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnClientSecretPostWithoutClientID() { r := s.GetRequest(&url.Values{oidc.FormParameterClientSecret: []string{"client-secret"}}) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(err, "invalid_request") s.EqualError(ErrorToRFC6749ErrorTest(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Client credentials missing or malformed in both HTTP Authorization header and HTTP POST body.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnClientSecretBasicWithMalformedClientID() { r := s.GetRequest(&url.Values{oidc.FormParameterRequestURI: []string{"not applicable"}}) r.Header.Set(fasthttp.HeaderAuthorization, fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte("abc@#%!@#(*%)#@!:client-secret")))) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(err, "invalid_request") s.EqualError(ErrorToRFC6749ErrorTest(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The client id in the HTTP authorization header could not be decoded from 'application/x-www-form-urlencoded'. invalid URL escape '%!@'") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnClientSecretBasicWithMalformedClientSecret() { r := s.GetRequest(&url.Values{oidc.FormParameterRequestURI: []string{"not applicable"}}) r.Header.Set(fasthttp.HeaderAuthorization, fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte("hs512:abc@#%!@#(*%)#@!")))) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(err, "invalid_request") s.EqualError(ErrorToRFC6749ErrorTest(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The client secret in the HTTP authorization header could not be decoded from 'application/x-www-form-urlencoded'. invalid URL escape '%!@'") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretBasicOnClientSecretPostClient() { r := s.GetClientSecretBasicRequest(oidc.ClientAuthMethodClientSecretPost, "client-secret") client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client supports client authentication method 'client_secret_post', but method 'client_secret_basic' was requested. You must configure the OAuth 2.0 client's 'token_endpoint_auth_method' value to accept 'client_secret_basic'.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretBasicWrongSecret() { r := s.GetClientSecretBasicRequest(oidc.ClientAuthMethodClientSecretBasic, "client-secret-bad") client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The provided client secret did not match the registered client secret.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretBasicOnPublic() { r := s.GetClientSecretBasicRequest("public", "client-secret") client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client supports client authentication method 'none', but method 'client_secret_basic' was requested. You must configure the OAuth 2.0 client's 'token_endpoint_auth_method' value to accept 'client_secret_basic'.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretBasicOnPublicWithBasic() { r := s.GetClientSecretBasicRequest("public-basic", "client-secret") client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client supports client authentication method 'client_secret_basic', but method 'none' was requested. You must configure the OAuth 2.0 client's 'token_endpoint_auth_method' value to accept 'none'.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretBasicOnInvalidClient() { r := s.GetClientSecretBasicRequest("not-a-client", "client-secret") client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). invalid_client") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldValidatePublic() { v := s.GetClientValues("public") r := s.GetRequest(v) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.NoError(err) s.Require().NotNil(client) s.Equal("public", client.GetID()) } func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMismatchedFormClientID() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertion.Issuer = "hs5122" assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) values := s.GetAssertionValues(token) values.Set(oidc.FormParameterClientID, "hs5122") r := s.GetRequest(values) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'sub' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMismatchedFormClientIDWithIss() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) values := s.GetAssertionValues(token) values.Set(oidc.FormParameterClientID, "hs5122") r := s.GetRequest(values) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'iss' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMissingClient() { assertion := NewAssertion("noclient", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). invalid_client") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailBadSecret() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret-wrong")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The signature is invalid.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailMethodNone() { assertion := NewAssertion(oidc.ClientAuthMethodNone, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This requested OAuth 2.0 client does not support client authentication, however 'client_assertion' was provided in the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailAssertionMethodClientSecretPost() { assertion := NewAssertion(oidc.ClientAuthMethodClientSecretPost, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This requested OAuth 2.0 client only supports client authentication method 'client_secret_post', however 'client_assertion' was provided in the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailAssertionMethodBad() { assertion := NewAssertion("bad_method", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This requested OAuth 2.0 client only supports client authentication method 'bad_method', however that method is not supported by this server.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailAssertionBaseClient() { assertion := NewAssertion("base", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_request") s.EqualError(ErrorToRFC6749ErrorTest(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The client configuration does not support OpenID Connect specific authentication methods.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailAssertionMethodClientSecretBasic() { assertion := NewAssertion(oidc.ClientAuthMethodClientSecretBasic, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This requested OAuth 2.0 client only supports client authentication method 'client_secret_basic', however 'client_assertion' was provided in the request.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailHashedSecret() { assertion := NewAssertion("hashed", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This client does not support authentication method 'client_secret_jwt' as the client secret is not in plaintext.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailExpiredToken() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Minute*-3), time.Unix(time.Now().Add(time.Minute*-1).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The token is expired.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailNotYetValid() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Minute*-3), time.Unix(time.Now().Add(time.Minute*1).Unix(), 0)) assertion.NotBefore = jwt.NewNumericDate(time.Now().Add(time.Second * 10)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) s.NoError(r.ParseForm()) ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The token isn't valid yet.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailTokenUsedBeforeIssued() { assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Minute*3), time.Unix(time.Now().Add(time.Minute*8).Unix(), 0)) assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) token, err := assertionJWT.SignedString([]byte("client-secret")) s.Require().NoError(err) s.Require().NotEqual("", token) r := s.GetAssertionRequest(token) ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The token was used before it was issued.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailMalformed() { r := s.GetAssertionRequest("bad token") ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_client") s.EqualError(ErrorToRFC6749ErrorTest(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The token is malformed.") s.Nil(client) } func (s *ClientAuthenticationStrategySuite) TestShouldFailMissingAssertion() { r := s.GetAssertionRequest("") ctx := s.GetCtx() client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) s.EqualError(err, "invalid_request") s.EqualError(ErrorToRFC6749ErrorTest(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The client_assertion request parameter must be set when using client_assertion_type of 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'.") s.Nil(client) } type RegisteredClaims struct { jwt.RegisteredClaims } func (r *RegisteredClaims) ToMapClaims() jwt.MapClaims { claims := jwt.MapClaims{} if r.ID != "" { claims[oidc.ClaimJWTID] = r.ID } if r.Subject != "" { claims[oidc.ClaimSubject] = r.Subject } if r.Issuer != "" { claims[oidc.ClaimIssuer] = r.Issuer } if len(r.Audience) != 0 { claims[oidc.ClaimAudience] = r.Audience } if r.NotBefore != nil { claims[oidc.ClaimNotBefore] = r.NotBefore } if r.ExpiresAt != nil { claims[oidc.ClaimExpirationTime] = r.ExpiresAt } if r.IssuedAt != nil { claims[oidc.ClaimIssuedAt] = r.IssuedAt } return claims } func NewAssertion(clientID string, tokenURL *url.URL, iat, exp time.Time) RegisteredClaims { return RegisteredClaims{ jwt.RegisteredClaims{ ID: uuid.Must(uuid.NewRandom()).String(), Issuer: clientID, Audience: []string{ tokenURL.String(), }, Subject: clientID, IssuedAt: jwt.NewNumericDate(iat), ExpiresAt: jwt.NewNumericDate(exp), }, } } type RFC6749ErrorTest struct { *fosite.RFC6749Error } func (err *RFC6749ErrorTest) Error() string { return err.WithExposeDebug(true).GetDescription() } func ErrorToRFC6749ErrorTest(err error) (rfc error) { if err == nil { return nil } ferr := fosite.ErrorToRFC6749Error(err) return &RFC6749ErrorTest{ferr} } func MustDecodeSecret(value string) *schema.PasswordDigest { if secret, err := schema.DecodePasswordDigest(value); err != nil { panic(err) } else { return secret } } func MustParseRequestURI(input string) *url.URL { if requestURI, err := url.ParseRequestURI(input); err != nil { panic(err) } else { return requestURI } } func MustParseRSAPrivateKey(data string) *rsa.PrivateKey { block, _ := pem.Decode([]byte(data)) if block == nil || block.Bytes == nil || len(block.Bytes) == 0 { panic("not pem encoded") } if block.Type != "RSA PRIVATE KEY" { panic("not private key") } key, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { panic(err) } return key } func MustParseECPrivateKey(data string) *ecdsa.PrivateKey { block, _ := pem.Decode([]byte(data)) if block == nil || block.Bytes == nil || len(block.Bytes) == 0 { panic("not pem encoded") } if block.Type != "EC PRIVATE KEY" { panic("not private key") } key, err := x509.ParseECPrivateKey(block.Bytes) if err != nil { panic(err) } return key } const exampleRSAPrivateKey = ` -----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEA60Vuz1N1wUHiCDIlbz8gE0dWPCmHEWnXKchEEISqIJ6j5Eah Q/GwX3WK0UV5ATRvWhg6o7/WfrLYcAsi4w79TgMjJHLWIY/jzAS3quEtzOLlLSWZ 9FR9SomQm3T/ETOS8IvSGrksIj0WgX35jB1NnbqSTRnYx7Cg/TBJjmiaqd0b9G/8 LlReaihwGf8tvPgnteWIdon3EI2MKDBkaesRjpL98Cz7VvD7dajseAlUh9jQWVge sN8qnm8pNPFAYsgxf//Jf0RfsND6H70zKKybDmyct4T4o/8qjivw4ly0XkArDCUj Qx2KUF7nN+Bo9wwnNppjdnsOPUbus8o1a9vY1QIDAQABAoIBAQDl1SBY3PlN36SF yScUtCALdUbi4taVxkVxBbioQlFIKHGGkRD9JN/dgSApK6r36FdXNhAi40cQ4nnZ iqd8FKqTSTFNa/mPM9ee+ITMI8nwOz8SiYcKTndPF2/yzapXDYDgCFcpz/czQ2X2 /i+IFyA5k4dUVomVGhFLBZ71xW5BvGUBMUH0XkeR5+c4gLvgR209BlpBHlkX4tUQ +RQoxbKpkntl0mjqf91zcOe4LJVsXZFyN+NVSzLEbGC3lVSSiyjVQH3s7ExnTaHi PpwSoXzu5QJj5xRit/1B3/LEGpIlPGFrkhMzBDTN+HYV/VLbCHJzjg5GVJawA82E h2BY6YWJAoGBAPmGaZL5ggnTVR2XVBLDKbwL/sesqiPZk45B+I5eObHl+v236JH9 RPMjdE10jOR1TzfQdmE2/RboKhiVn+osS+2W6VXSo7sMsSM1bLBPYhnwrNIqzrX8 Vgi2bCl2S8ZhVo2R8c5WUaD0Gpxs6hwPIMOQWWwxDlsbg/UoLrhD3X4XAoGBAPFg VSvaWQdDVAqjM42ObhZtWxeLfEAcxRQDMQq7btrTwBZSrtP3S3Egu66cp/4PT4VD Hc8tYyT2rNETiqT6b2Rm1MgeoJ8wRqte6ZXSQVVQUOd42VG04O3aaleAGhXjEkM2 avctRdKHDhQdIt+riPgaNj4FdYpmQ5zIrcZtBr/zAoGBAOBXzBX7xMHmwxEe3NUd qSlMM579C9+9oF/3ymzeJMtgtcBmGHEhoFtmVgvJrV8+ZaIOCFExam2tASQnaqbV etK7q0ChaNok+CJqxzThupcN/6PaHw4aOJQOx8KjfE95dqNEQ367txqaPk7D0dy2 cUPDRdLzbC/X1lWV8iNzyPGzAoGBAN4R2epRpYz4Fa7/vWNkAcaib6c2zmaR0YN6 +Di+ftvW6yfehDhBkWgQTHv2ZtxoK6oYOKmuQUP1qsNkbi8gtTEzJlrDStWKbcom tVMAsNkT3otHdPEmL7bFNwcvtVAjrF6oBztHrLBnTr2UnMwZnhdczkC7dwuQ0G3D d5VSI16fAoGAY7eeVDkic73GbZmtZibuodvPJ/z85RIBOrzf3ColO4jGI6Ej/EnD rMEe/mRC27CJzS9L9Jc0Kt66mGSvodDGl0nBsXGNfPog0cGwweCVN0Eo2VJZbRTT UoU05/Pvu2h3/E8gGTBY0/WPSo06YUsICjVDWNuOIa/7IY7SyE6Xxn0= -----END RSA PRIVATE KEY-----` const exampleECP256PrivateKey = ` -----BEGIN EC PRIVATE KEY----- MHcCAQEEID1fSsJ8qyEqj2DVkrshaNiXqaSDX7qViASRkyGGJFbEoAoGCCqGSM49 AwEHoUQDQgAENnBG+bBJIaIa+bRlHaLiXD86RAy+Ef9CVdAfpPGoNRfkOTcrrIV7 2wv3Y5e0he63Tn9iVAFYRFexK1mjFw7TfA== -----END EC PRIVATE KEY-----` const exampleECP384PrivateKey = ` -----BEGIN EC PRIVATE KEY----- MIGkAgEBBDBPoOfapxtgZ8XNE7Wwdlw+9oDc6x4m57MITZyWzN62jkFUAYsvPJDF 9+g+e8CT5yqgBwYFK4EEACKhZANiAAQ2uZ0HIIxIavyjGyX13tIZVOaRB4+D64dF s3DXDrpXcuDTSohw9xBW5sLDqRVu2LkBsCUFXtEJUHgC+O7wToNw8nh+KdDrcu/J miNqbvEHuvlSlHWyx9HH8kAEuu1+SZg= -----END EC PRIVATE KEY-----` const exampleECP521PrivateKey = ` -----BEGIN EC PRIVATE KEY----- MIHcAgEBBEIBT07AnitDd1Z01bl5W5VW8/vTWyu7w3MSqEmCeKcM19p/TAJAeS8L 6UOig2fTUeuMeA2PoOUjI2Bid927VsWcxE2gBwYFK4EEACOhgYkDgYYABAGnV9mu xY0E7/k8b+glOOMaN0+Qt70H9OmSz6tC8tU3EayRwFlNPch9TlvEpbCS3MsDE9dN 78EpFx45MUqzzdZcOgAu+EUC9Zas1YVK+WMo0GFy+XtFq3kxubOclBb52M/63mcd zZnA8aAu9iTK9YPfcw1YWTJliNdKUoxmGVV5Ca1W4w== -----END EC PRIVATE KEY-----`