Skip to content

Commit

Permalink
chore: converge on go-jose (#1297)
Browse files Browse the repository at this point in the history
  • Loading branch information
RTann committed Jun 18, 2024
1 parent ec1aab8 commit ab9769c
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 56 deletions.
6 changes: 3 additions & 3 deletions auth/claimrule/claim_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import (
"encoding/json"
"testing"

"gopkg.in/square/go-jose.v2"

"github.com/go-jose/go-jose/v4"
"github.com/stretchr/testify/assert"
)

Expand All @@ -16,7 +15,8 @@ type dataSet struct {
}

func getRawToken(t *testing.T, jwt map[string]interface{}) string {
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: "HS256", Key: []byte("secret")}, (&jose.SignerOptions{}).WithType("JWT"))
key := []byte(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`)
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, (&jose.SignerOptions{}).WithType("JWT"))
if err != nil {
t.Fatal(err)
}
Expand Down
101 changes: 53 additions & 48 deletions auth/tokenizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import (
"time"

"github.com/coreos/go-oidc/v3/oidc"
"github.com/dgrijalva/jwt-go"
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/pkg/errors"
"github.com/stackrox/infra/auth/claimrule"
Expand All @@ -15,13 +16,9 @@ import (
"golang.org/x/oauth2"
)

// clockDriftLeeway is used to account for minor clock drift between our host,
// and OIDC.
//
// See this issue for context:
// https://github.com/dgrijalva/jwt-go/issues/314#issuecomment-494585527
const (
clockDriftLeeway = int64(10 * time.Second)
// clockDriftLeeway is used to account for minor clock drift between our host and OIDC.
clockDriftLeeway = 10 * time.Second

emailSuffixRedHat = "@redhat.com"
)
Expand All @@ -34,7 +31,7 @@ func createHumanUser(profile oidcClaims) *v1.User {
Name: profile.Name,
Email: profile.Email,
Picture: profile.PictureURL,
Expiry: &timestamp.Timestamp{Seconds: profile.ExpiresAt},
Expiry: &timestamp.Timestamp{Seconds: int64(*profile.Expiry)},
}
}

Expand Down Expand Up @@ -63,24 +60,18 @@ func NewStateTokenizer(lifetime time.Duration, secret string) *stateTokenizer {
// Generate generates a state JWT.
func (t stateTokenizer) Generate() (string, error) {
now := time.Now()
claims := jwt.StandardClaims{
ExpiresAt: now.Add(t.lifetime).Unix(),
NotBefore: now.Unix(),
IssuedAt: now.Unix(),
nowDate := jwt.NewNumericDate(now)
claims := jwt.Claims{
Expiry: jwt.NewNumericDate(now.Add(t.lifetime)),
NotBefore: nowDate,
IssuedAt: nowDate,
}

// Generate new token object, containing the wrapped data.
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

// Sign and get the complete encoded token as a string using the secret
return token.SignedString(t.secret)
return signedToken(t.secret, claims)
}

// Validate validates a state JWT.
func (t stateTokenizer) Validate(token string) error {
_, err := jwt.Parse(token, func(_ *jwt.Token) (interface{}, error) {
return t.secret, nil
})
_, err := jwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.HS256})
return err
}

Expand Down Expand Up @@ -109,7 +100,7 @@ func NewOidcTokenizer(verifier *oidc.IDTokenVerifier) *oidcTokenizer {
// oidcClaims facilitates the unmarshalling of JWTs containing OIDC user
// profile data.
type oidcClaims struct {
jwt.StandardClaims
jwt.Claims
FamilyName string `json:"family_name"`
GivenName string `json:"given_name"`
Name string `json:"name"`
Expand Down Expand Up @@ -139,10 +130,9 @@ func (c oidcClaims) Valid() error {
log.AuditLog(logging.INFO, "oidc-claim-validation", errMsg, "email", c.Email)
return errors.Errorf(errMsg)
default:
c.StandardClaims.IssuedAt -= clockDriftLeeway
valid := c.StandardClaims.Valid()
c.StandardClaims.IssuedAt += clockDriftLeeway
return valid
// Use an empty jwt.Expected to skip non-time-related validation and use time.Now()
// for the validation.
return c.ValidateWithLeeway(jwt.Expected{}, clockDriftLeeway)
}
}

Expand Down Expand Up @@ -193,37 +183,38 @@ func NewUserTokenizer(lifetime time.Duration, secret string) *userTokenizer {
// userClaims facilitates the arshalling/unmarshalling of JWTs containing v1
// .User data.
type userClaims struct {
jwt.Claims
User v1.User `json:"user"`
jwt.StandardClaims
}

// Generate generates a user JWT containing a v1.User struct.
func (t userTokenizer) Generate(user *v1.User) (string, error) {
now := time.Now()
nowDate := jwt.NewNumericDate(now)
claims := userClaims{
User: *user,
StandardClaims: jwt.StandardClaims{
ExpiresAt: now.Add(t.lifetime).Unix(),
NotBefore: now.Unix(),
IssuedAt: now.Unix(),
Claims: jwt.Claims{
Expiry: jwt.NewNumericDate(now.Add(t.lifetime)),
NotBefore: nowDate,
IssuedAt: nowDate,
},
}

// Generate new token object, containing the wrapped data.
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

// Sign and get the complete encoded token as a string using the secret
return token.SignedString(t.secret)
return signedToken(t.secret, claims)
}

// Validate validates a user JWT and returns the contained v1.User struct.
func (t userTokenizer) Validate(token string) (*v1.User, error) {
parsedToken, err := jwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.HS256})
if err != nil {
return nil, err
}

var claims userClaims
if _, err := jwt.ParseWithClaims(token, &claims, func(_ *jwt.Token) (interface{}, error) {
return t.secret, nil
}); err != nil {
err = parsedToken.Claims(t.secret, &claims)
if err != nil {
return nil, err
}

return &claims.User, nil
}

Expand Down Expand Up @@ -276,20 +267,20 @@ func (t serviceAccountTokenizer) Generate(svcacct v1.ServiceAccount) (string, er
return "", errors.Wrap(err, "invalid service account")
}

// Generate new token object, containing the wrapped data.
token := jwt.NewWithClaims(jwt.SigningMethodHS256, svc)

// Sign and get the complete encoded token as a string using the secret
return token.SignedString(t.secret)
return signedToken(t.secret, svc)
}

// Validate validates a service account JWT and returns the contained
// v1.ServiceAccount.
func (t serviceAccountTokenizer) Validate(token string) (v1.ServiceAccount, error) {
parsedToken, err := jwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.HS256})
if err != nil {
return v1.ServiceAccount{}, err
}

var claims serviceAccountValidator
if _, err := jwt.ParseWithClaims(token, &claims, func(_ *jwt.Token) (interface{}, error) {
return t.secret, nil
}); err != nil {
err = parsedToken.Claims(t.secret, &claims)
if err != nil {
return v1.ServiceAccount{}, err
}

Expand Down Expand Up @@ -323,3 +314,17 @@ func (t accessTokenizer) Validate(_ context.Context, rawToken *oauth2.Token) err

return t.claimRules.Validate(rawAccessToken.(string))
}

func signedToken(key []byte, claims any) (string, error) {
sigKey := jose.SigningKey{
Algorithm: jose.HS256,
Key: key,
}
// See https://pkg.go.dev/github.com/go-jose/go-jose/v4/jwt#example-Signed for an example.
sig, err := jose.NewSigner(sigKey, (&jose.SignerOptions{}).WithType("JWT"))
if err != nil {
return "", err
}

return jwt.Signed(sig).Claims(claims).Serialize()
}
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ require (
github.com/argoproj/argo-workflows/v3 v3.5.5
github.com/buger/jsonparser v1.1.1
github.com/coreos/go-oidc/v3 v3.9.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
github.com/go-jose/go-jose/v4 v4.0.2
github.com/gogo/protobuf v1.3.2
github.com/golang/protobuf v1.5.4
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
Expand All @@ -31,7 +31,6 @@ require (
google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c
google.golang.org/grpc v1.62.1
google.golang.org/protobuf v1.33.0
gopkg.in/square/go-jose.v2 v2.6.0
k8s.io/api v0.29.2
k8s.io/apimachinery v0.29.2
k8s.io/client-go v0.29.2
Expand Down
5 changes: 2 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
Expand Down Expand Up @@ -113,6 +112,8 @@ github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ER
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
Expand Down Expand Up @@ -762,8 +763,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down

0 comments on commit ab9769c

Please sign in to comment.