Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JWT header type verification as described by JWT-SVID spec #160

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions v2/svid/jwtsvid/svid.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package jwtsvid

import (
"fmt"
"strings"
"time"

"github.com/spiffe/go-spiffe/v2/bundle/jwtbundle"
Expand Down Expand Up @@ -93,7 +94,7 @@ func parse(token string, audience []string, getClaims tokenValidator) (*SVID, er
}

// Validates supported token signed algorithm
if err := validateTokenAlgorithm(tok); err != nil {
if err := validateTokenHeader(tok); err != nil {
return nil, err
}

Expand Down Expand Up @@ -146,15 +147,67 @@ func parse(token string, audience []string, getClaims tokenValidator) (*SVID, er
}, nil
}

// validateTokenAlgorithm json web token have only one header, and it is signed for a supported algorithm
func validateTokenAlgorithm(tok *jwt.JSONWebToken) error {
// validateTokenHeader check if JWT have only one header, it's signed for a
// supported algorithm, and it has a valid header type.
func validateTokenHeader(tok *jwt.JSONWebToken) error {
// Only one header is expected
if len(tok.Headers) != 1 {
return fmt.Errorf("expected a single token header; got %d", len(tok.Headers))
}

hdr := &tok.Headers[0]
err := validateTokenHeaderType(hdr)
if err != nil {
return err
}

err = validateTokenAlgorithm(hdr)
if err != nil {
return err
}

return nil
}

// validateTokenHeaderType check if JWT has a valid header type.
func validateTokenHeaderType(hdr *jose.Header) error {
// The typ header is optional. If set, its value MUST be either JWT or JOSE.
hdrType, present := hdr.ExtraHeaders[jose.HeaderType]
if !present {
return nil
}

// jose.HeaderType it's supposed to be a string, using a noop type
// assertion here just to be safe.
hdrTypeString, _ := hdrType.(string)

// RFC7519 says:
// While media type names are not case sensitive, it is RECOMMENDED
// that "JWT" always be spelled using uppercase characters for
// compatibility with legacy implementations.
//
// And JWT-SVID spec says: If set, its value MUST be either JWT or JOSE.
// Not sure if we should be flexible or strict on the verification.
hdrTypeString = strings.ToUpper(hdrTypeString)
switch hdrTypeString {
case "JWT":
// All SPIRE issued JWT SVIDs seems to allways have type "JWT" set.
// TODO: Waiting for PR discussions

case "JOSE":
// TODO: Waiting for PR discussions

default:
return fmt.Errorf(`unsupported header type %#v, expecting "JWT" or "JOSE"`, hdrType)
}

return nil
}

// validateTokenAlgorithm json web token is signed for a supported algorithm
func validateTokenAlgorithm(hdr *jose.Header) error {
// Make sure it has an algorithm supported by JWT-SVID
alg := tok.Headers[0].Algorithm
alg := hdr.Algorithm
switch jose.SignatureAlgorithm(alg) {
case jose.RS256, jose.RS384, jose.RS512,
jose.ES256, jose.ES384, jose.ES512,
Expand Down