open-nomad/lib/auth/jwt/validator_test.go
Michael Schurter 3b3b02b741
dep: update from jwt/v4 to jwt/v5 (#17062)
Their release notes are here: https://github.com/golang-jwt/jwt/releases

Seemed wise to upgrade before we do even more with JWTs. For example
this upgrade *would* have mattered if we already implemented common JWT
claims such as expiration. Since we didn't rely on any claim
verification this upgrade is a noop...

...except for 1 test that called `Claims.Valid()`! Removing that
assertion *seems* scary, but it didn't actually do anything because we
didn't implement any of the standard claims it validated:

https://github.com/golang-jwt/jwt/blob/v4.5.0/map_claims.go#L120-L151

So functionally this major upgrade is a noop.
2023-05-03 11:17:38 -07:00

146 lines
3.8 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package jwt
import (
"context"
"crypto/rand"
"crypto/rsa"
"strconv"
"testing"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/hashicorp/cap/oidc"
"github.com/shoenig/test/must"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
)
func TestValidate(t *testing.T) {
iat := time.Now().Unix()
nbf := time.Now().Unix()
exp := time.Now().Add(time.Hour).Unix()
claims := jwt.MapClaims{
"foo": "bar",
"issuer": "test suite",
"float": 3.14,
"iat": iat,
"nbf": nbf,
"exp": exp,
}
wantedClaims := map[string]any{
"foo": "bar",
"issuer": "test suite",
"float": 3.14,
"iat": float64(iat),
"nbf": float64(nbf),
"exp": float64(exp),
}
// appended to JWKS test server URL
wellKnownJWKS := "/.well-known/jwks.json"
// generate a key pair, so that we can use it for consistent signing and
// set it as our test server key
rsaKey, err := rsa.GenerateKey(rand.Reader, 4096)
must.NoError(t, err)
token, _, err := mock.SampleJWTokenWithKeys(claims, rsaKey)
must.NoError(t, err)
tokenWithNoClaims, pubKeyPem, err := mock.SampleJWTokenWithKeys(nil, rsaKey)
must.NoError(t, err)
// make an expired token...
expired := time.Now().Add(-time.Hour).Unix()
expiredClaims := jwt.MapClaims{"iat": iat, "nbf": nbf, "exp": expired}
expiredToken, _, err := mock.SampleJWTokenWithKeys(expiredClaims, rsaKey)
must.NoError(t, err)
// ...and one with invalid issuer, too
invalidIssuer := jwt.MapClaims{"iat": iat, "nbf": nbf, "exp": exp, "iss": "hashicorp vault"}
invalidIssuerToken, _, err := mock.SampleJWTokenWithKeys(invalidIssuer, rsaKey)
must.NoError(t, err)
testServer := oidc.StartTestProvider(t)
defer testServer.Stop()
keyID := strconv.Itoa(int(time.Now().Unix()))
testServer.SetSigningKeys(rsaKey, rsaKey.Public(), oidc.RS256, keyID)
tokenSignedWithRemoteServerKeys, _, err := mock.SampleJWTokenWithKeys(claims, rsaKey)
must.NoError(t, err)
tests := []struct {
name string
token string
conf *structs.ACLAuthMethodConfig
want map[string]interface{}
wantErr bool
}{
{
name: "valid signature, local verification",
token: token,
conf: &structs.ACLAuthMethodConfig{JWTValidationPubKeys: []string{pubKeyPem}},
want: wantedClaims,
wantErr: false,
},
{
name: "valid signature, local verification, no claims",
token: tokenWithNoClaims,
conf: &structs.ACLAuthMethodConfig{JWTValidationPubKeys: []string{pubKeyPem}},
want: nil,
wantErr: true,
},
{
name: "valid signature, JWKS verification",
token: tokenSignedWithRemoteServerKeys,
conf: &structs.ACLAuthMethodConfig{
JWKSURL: testServer.Addr() + wellKnownJWKS,
JWKSCACert: testServer.CACert(),
},
want: wantedClaims,
wantErr: false,
},
{
name: "valid signature, OIDC verification",
token: tokenSignedWithRemoteServerKeys,
conf: &structs.ACLAuthMethodConfig{
OIDCDiscoveryURL: testServer.Addr(),
DiscoveryCaPem: []string{testServer.CACert()},
},
want: wantedClaims,
wantErr: false,
},
{
name: "expired token, local verification",
token: expiredToken,
conf: &structs.ACLAuthMethodConfig{JWTValidationPubKeys: []string{pubKeyPem}},
want: nil,
wantErr: true,
},
{
name: "invalid issuer, local verification",
token: invalidIssuerToken,
conf: &structs.ACLAuthMethodConfig{
JWTValidationPubKeys: []string{pubKeyPem},
BoundIssuer: []string{"test suite"},
},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Validate(context.Background(), tt.token, tt.conf)
if !tt.wantErr {
must.Nil(t, err, must.Sprint(err))
}
must.Eq(t, got, tt.want)
})
}
}