Skip to content

Commit 45a6785

Browse files
authored
feat: add the ability to set jwt header type (#737)
1 parent daf3b15 commit 45a6785

3 files changed

Lines changed: 80 additions & 25 deletions

File tree

token/jwt/header.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func NewHeaders() *Headers {
1414

1515
// ToMap will transform the headers to a map structure
1616
func (h *Headers) ToMap() map[string]interface{} {
17-
var filter = map[string]bool{"alg": true, "typ": true}
17+
var filter = map[string]bool{"alg": true}
1818
var extra = map[string]interface{}{}
1919

2020
// filter known values from extra.

token/jwt/token.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ func (t *Token) SignedString(k interface{}) (rawToken string, err error) {
109109

110110
func unsignedToken(t *Token) (string, error) {
111111
t.Header["alg"] = "none"
112-
t.Header[string(JWTHeaderType)] = JWTHeaderTypeValue
112+
if _, ok := t.Header[string(JWTHeaderType)]; !ok {
113+
t.Header[string(JWTHeaderType)] = JWTHeaderTypeValue
114+
}
113115
hbytes, err := json.Marshal(&t.Header)
114116
if err != nil {
115117
return "", errorsx.WithStack(err)

token/jwt/token_test.go

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,73 @@ import (
2222
)
2323

2424
func TestUnsignedToken(t *testing.T) {
25-
key := UnsafeAllowNoneSignatureType
26-
token := NewWithClaims(SigningMethodNone, MapClaims{
27-
"aud": "foo",
28-
"exp": time.Now().UTC().Add(time.Hour).Unix(),
29-
"iat": time.Now().UTC().Unix(),
30-
"sub": "nestor",
31-
})
32-
rawToken, err := token.SignedString(key)
33-
require.NoError(t, err)
34-
require.NotEmpty(t, rawToken)
35-
parts := strings.Split(rawToken, ".")
36-
require.Len(t, parts, 3)
37-
require.Empty(t, parts[2])
38-
tk, err := jwt.ParseSigned(rawToken)
39-
require.NoError(t, err)
40-
require.Len(t, tk.Headers, 1)
41-
require.Equal(t, "JWT", tk.Headers[0].ExtraHeaders[jose.HeaderKey("typ")])
25+
var testCases = []struct {
26+
name string
27+
jwtHeaders map[string]interface{}
28+
expectedType string
29+
}{
30+
{
31+
name: "set JWT as 'typ' when the the type is not specified in the headers",
32+
jwtHeaders: map[string]interface{}{},
33+
expectedType: "JWT",
34+
},
35+
{
36+
name: "'typ' set explicitly",
37+
jwtHeaders: map[string]interface{}{"typ": "at+jwt"},
38+
expectedType: "at+jwt",
39+
},
40+
}
41+
for _, tc := range testCases {
42+
t.Run(tc.name, func(t *testing.T) {
43+
key := UnsafeAllowNoneSignatureType
44+
token := NewWithClaims(SigningMethodNone, MapClaims{
45+
"aud": "foo",
46+
"exp": time.Now().UTC().Add(time.Hour).Unix(),
47+
"iat": time.Now().UTC().Unix(),
48+
"sub": "nestor",
49+
})
50+
token.Header = tc.jwtHeaders
51+
rawToken, err := token.SignedString(key)
52+
require.NoError(t, err)
53+
require.NotEmpty(t, rawToken)
54+
parts := strings.Split(rawToken, ".")
55+
require.Len(t, parts, 3)
56+
require.Empty(t, parts[2])
57+
tk, err := jwt.ParseSigned(rawToken)
58+
require.NoError(t, err)
59+
require.Len(t, tk.Headers, 1)
60+
require.Equal(t, tc.expectedType, tk.Headers[0].ExtraHeaders[jose.HeaderKey("typ")])
61+
})
62+
}
4263
}
4364

4465
func TestJWTHeaders(t *testing.T) {
45-
rawToken := makeSampleToken(nil, jose.RS256, gen.MustRSAKey())
46-
tk, err := jwt.ParseSigned(rawToken)
47-
require.NoError(t, err)
48-
require.Len(t, tk.Headers, 1)
49-
require.Equal(t, tk.Headers[0].Algorithm, "RS256")
50-
require.Equal(t, "JWT", tk.Headers[0].ExtraHeaders[jose.HeaderKey("typ")])
66+
var testCases = []struct {
67+
name string
68+
jwtHeaders map[string]interface{}
69+
expectedType string
70+
}{
71+
{
72+
name: "set JWT as 'typ' when the the type is not specified in the headers",
73+
jwtHeaders: map[string]interface{}{},
74+
expectedType: "JWT",
75+
},
76+
{
77+
name: "'typ' set explicitly",
78+
jwtHeaders: map[string]interface{}{"typ": "at+jwt"},
79+
expectedType: "at+jwt",
80+
},
81+
}
82+
for _, tc := range testCases {
83+
t.Run(tc.name, func(t *testing.T) {
84+
rawToken := makeSampleTokenWithCustomHeaders(nil, jose.RS256, tc.jwtHeaders, gen.MustRSAKey())
85+
tk, err := jwt.ParseSigned(rawToken)
86+
require.NoError(t, err)
87+
require.Len(t, tk.Headers, 1)
88+
require.Equal(t, tk.Headers[0].Algorithm, "RS256")
89+
require.Equal(t, tc.expectedType, tk.Headers[0].ExtraHeaders[jose.HeaderKey("typ")])
90+
})
91+
}
5192
}
5293

5394
var keyFuncError error = fmt.Errorf("error loading key")
@@ -418,6 +459,18 @@ func makeSampleToken(c MapClaims, m jose.SignatureAlgorithm, key interface{}) st
418459
return s
419460
}
420461

462+
func makeSampleTokenWithCustomHeaders(c MapClaims, m jose.SignatureAlgorithm, headers map[string]interface{}, key interface{}) string {
463+
token := NewWithClaims(m, c)
464+
token.Header = headers
465+
s, e := token.SignedString(key)
466+
467+
if e != nil {
468+
panic(e.Error())
469+
}
470+
471+
return s
472+
}
473+
421474
func parseRSAPublicKeyFromPEM(key []byte) *rsa.PublicKey {
422475
var err error
423476

0 commit comments

Comments
 (0)