Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

feat: add JWT Explicit typing basic support #3600

Merged
Show file tree
Hide file tree
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
31 changes: 29 additions & 2 deletions component/models/jwt/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,10 @@ func checkHeaders(headers map[string]interface{}) error {
}

typ, ok := headers[jose.HeaderType]
if ok && typ != TypeJWT {
return errors.New("typ is not JWT")
if ok {
if err := checkTypHeader(typ); err != nil {
return err
}
}

cty, ok := headers[jose.HeaderContentType]
Expand All @@ -282,6 +284,31 @@ func checkHeaders(headers map[string]interface{}) error {
return nil
}

func checkTypHeader(typ interface{}) error {
typStr, ok := typ.(string)
if !ok {
return errors.New("invalid typ header format")
}

chunks := strings.Split(typStr, "+")
if len(chunks) > 1 {
// Explicit typing.
// https://www.rfc-editor.org/rfc/rfc8725.html#name-use-explicit-typing
if strings.ToUpper(chunks[1]) != TypeJWT {
return errors.New("invalid typ header")
}

return nil
}

if typStr != TypeJWT {
// https://www.rfc-editor.org/rfc/rfc7519#section-5.1
return errors.New("typ is not JWT")
}

return nil
}

// PayloadToMap transforms interface to map.
func PayloadToMap(i interface{}) (map[string]interface{}, error) {
if reflect.ValueOf(i).Kind() == reflect.Map {
Expand Down
95 changes: 95 additions & 0 deletions component/models/jwt/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,3 +538,98 @@ func createClaims() *CustomClaim {
PrivateClaim1: "private claim",
}
}

func Test_checkHeaders(t *testing.T) {
type args struct {
headers map[string]interface{}
}

tests := []struct {
name string
args args
wantErr assert.ErrorAssertionFunc
}{
{
name: "OK",
args: args{
headers: map[string]interface{}{
jose.HeaderAlgorithm: "EdDSA",
jose.HeaderType: "JWT",
jose.HeaderContentType: "application/example;part=\"1/2\"",
},
},
wantErr: assert.NoError,
},
{
name: "OK Explicit type",
args: args{
headers: map[string]interface{}{
jose.HeaderAlgorithm: "EdDSA",
jose.HeaderType: "openid4vci-proof+jwt",
jose.HeaderContentType: "application/example;part=\"1/2\"",
},
},
wantErr: assert.NoError,
},
{
name: "alg missed",
args: args{
headers: map[string]interface{}{
jose.HeaderType: "JWT",
jose.HeaderContentType: "application/example;part=\"1/2\"",
},
},
wantErr: assert.Error,
},
{
name: "invalid typ format",
args: args{
headers: map[string]interface{}{
jose.HeaderAlgorithm: "EdDSA",
jose.HeaderType: 123,
jose.HeaderContentType: "application/example;part=\"1/2\"",
},
},
wantErr: assert.Error,
},
{
name: "Explicit type - invalid prefix",
args: args{
headers: map[string]interface{}{
jose.HeaderAlgorithm: "EdDSA",
jose.HeaderType: "jose+json",
jose.HeaderContentType: "application/example;part=\"1/2\"",
},
},
wantErr: assert.Error,
},
{
name: "invalid typ",
args: args{
headers: map[string]interface{}{
jose.HeaderAlgorithm: "EdDSA",
jose.HeaderType: "jwt",
jose.HeaderContentType: "application/example;part=\"1/2\"",
},
},
wantErr: assert.Error,
},
{
name: "invalid cty",
args: args{
headers: map[string]interface{}{
jose.HeaderAlgorithm: "EdDSA",
jose.HeaderType: "JWT",
jose.HeaderContentType: "JWT",
},
},
wantErr: assert.Error,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.wantErr(t, checkHeaders(tt.args.headers), fmt.Sprintf("checkHeaders(%v)", tt.args.headers))
})
}
}