github.com/hellofresh/janus@v0.0.0-20230925145208-ce8de8183c67/pkg/jwt/parser_test.go (about) 1 package jwt 2 3 import ( 4 "crypto/x509" 5 "encoding/pem" 6 "errors" 7 "net/http" 8 "net/url" 9 "testing" 10 "time" 11 12 baseJWT "github.com/dgrijalva/jwt-go" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestParser_ParseFromRequest_jwtFromHeader(t *testing.T) { 18 alg := "HS256" 19 key := time.Now().Format(time.RFC3339Nano) 20 21 tokenString, err := generateToken(alg, key) 22 require.NoError(t, err) 23 24 req := &http.Request{Header: http.Header{}} 25 26 config := NewParserConfig(0, SigningMethod{Alg: alg, Key: key}) 27 parser := NewParser(config) 28 29 _, err = parser.ParseFromRequest(req) 30 assert.Error(t, err) 31 32 req.Header.Set("Authorization", "Basic "+tokenString) 33 _, err = parser.ParseFromRequest(req) 34 assert.Error(t, err) 35 36 req.Header.Set("Authorization", "Bearer "+tokenString) 37 38 assertParseToken(t, parser, req) 39 } 40 41 func TestParser_ParseFromRequest_jwtFromQuery(t *testing.T) { 42 alg := "HS256" 43 key := time.Now().Format(time.RFC3339Nano) 44 45 tokenString, err := generateToken(alg, key) 46 require.NoError(t, err) 47 48 config := NewParserConfig(0, SigningMethod{Alg: alg, Key: key}) 49 config.TokenLookup = "query:token" 50 parser := NewParser(config) 51 52 req := &http.Request{URL: &url.URL{}} 53 54 _, err = parser.ParseFromRequest(req) 55 assert.Error(t, err) 56 57 req.URL.RawQuery = "asd=qwe&token=" + tokenString 58 59 assertParseToken(t, parser, req) 60 } 61 62 func TestParser_ParseFromRequest_jwtFromCookie(t *testing.T) { 63 alg := "HS256" 64 key := time.Now().Format(time.RFC3339Nano) 65 66 tokenString, err := generateToken(alg, key) 67 require.NoError(t, err) 68 69 config := NewParserConfig(0, SigningMethod{Alg: alg, Key: key}) 70 config.TokenLookup = "cookie:token" 71 parser := NewParser(config) 72 73 req := &http.Request{Header: http.Header{}} 74 75 _, err = parser.ParseFromRequest(req) 76 assert.Error(t, err) 77 78 req.Header.Set("Cookie", "qwe=asd;token="+tokenString) 79 80 assertParseToken(t, parser, req) 81 } 82 83 func TestParser_Parse(t *testing.T) { 84 alg := "RS256" 85 86 tokenString, err := generateToken(alg, rsa2048Private) 87 require.NoError(t, err) 88 89 config := NewParserConfig(0, SigningMethod{Alg: "HS256", Key: time.Now().Format(time.RFC3339Nano)}) 90 parser := NewParser(config) 91 92 req := &http.Request{Header: http.Header{"Authorization": {"Bearer " + tokenString}}} 93 94 _, err = parser.ParseFromRequest(req) 95 require.Error(t, err) 96 97 parser.Config.SigningMethods = append(parser.Config.SigningMethods, SigningMethod{Alg: alg, Key: rsa2048Public}) 98 99 assertParseToken(t, parser, req) 100 } 101 102 func TestParser_Parse_ErrInvalidPEMBlock(t *testing.T) { 103 alg := "RS256" 104 105 tokenString, err := generateToken(alg, rsa2048Private) 106 require.NoError(t, err) 107 108 config := NewParserConfig(0, SigningMethod{Alg: alg, Key: "invalid public key"}) 109 parser := NewParser(config) 110 111 req := &http.Request{Header: http.Header{"Authorization": {"Bearer " + tokenString}}} 112 113 _, err = parser.ParseFromRequest(req) 114 assert.Error(t, err) 115 } 116 117 func TestParser_Parse_ErrNotRSAPublicKey(t *testing.T) { 118 alg := "RS256" 119 120 tokenString, err := generateToken(alg, rsa2048Private) 121 require.NoError(t, err) 122 123 config := NewParserConfig(0, SigningMethod{Alg: alg, Key: rsa2048Private}) 124 parser := NewParser(config) 125 126 req := &http.Request{Header: http.Header{"Authorization": {"Bearer " + tokenString}}} 127 128 _, err = parser.ParseFromRequest(req) 129 assert.Error(t, err) 130 } 131 132 func TestParser_Parse_ParsePKIXPublicKey(t *testing.T) { 133 alg := "RS256" 134 135 tokenString, err := generateToken(alg, rsa2048Private) 136 require.NoError(t, err) 137 138 config := NewParserConfig(0, SigningMethod{Alg: alg, Key: `-----BEGIN PUBLIC KEY----- 139 AIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHA+KjSHPzVp7HIVGrQv 140 xUNnYrfdUR5+MRn3SM/Ts7GwwfifIlTgqjRHjm+sPOhVauF+ZX5PkUmW/HBlOxsj 141 zA55mCBFymO+nyjl/DhhFNLnKu3IWL8Q3IsnM1EgE9FHgwFqf3X5Eh5h2NdsOWPk 142 FrOB6BKY1wkOI2E27bNJAat+F059HU9z+jgIwhcm/IciTbqts497x1can4+NFBOl 143 VWE+yii6tREHF8olVe9a1DA8k7mtOQ2+1bK69kxm7tIde5sWnrlG3dv0gvlF25b3 144 XhiFAMJ1RfgvQHjXbtmMaZdMxj/Kx4CvBM37eXBGUSDWt9q97g+ywIQ/NrPZWmHo 145 ewIDAQAB 146 -----END PUBLIC KEY-----`}) 147 parser := NewParser(config) 148 149 req := &http.Request{Header: http.Header{"Authorization": {"Bearer " + tokenString}}} 150 151 _, err = parser.ParseFromRequest(req) 152 assert.Error(t, err) 153 } 154 155 func TestParser_Parse_ErrBadPublicKey(t *testing.T) { 156 alg := "RS256" 157 158 tokenString, err := generateToken(alg, rsa2048Private) 159 require.NoError(t, err) 160 161 // DSA public key 162 // ssh-keygen -t dsa 163 // openssl dsa -in ~/.ssh/id_dsa -outform pem > dsa_priv.pem 164 // openssl dsa -in dsa_priv.pem -pubout -out dsa_pub.pem 165 config := NewParserConfig(0, SigningMethod{Alg: alg, Key: `-----BEGIN PUBLIC KEY----- 166 MIIBtjCCASsGByqGSM44BAEwggEeAoGBAIATdqQyUyprc8NtzuttJz8JahT+vwVK 167 4d2eVufm5IHyuqUyYroPQYpjQ1AfHOTE3ntmNFJcF+KhyqCTVdnaWwrmfiH2H6+D 168 2b+O8J50QFONKgktxy5LSBpUIIJfbhJG6vWW5GETnKOC7unoMh7yWDkDBYx+sSdg 169 ePBlI+Lq2+V1AhUA5e2ydAoXe0xa2lmoQDq09s+YsJUCgYBAMk28rTCuPLw+a7LF 170 ++ouIDVMfxc/r8+/L4RCX+B5IScsk8SyzPeYdFtnCSGklQMdMw6YXCPdHGcexK/F 171 F7i9t5vxpD98aWRrJBW5fE99CPUXuMO6Gn8kV+1flRoBeBjPCd807BH/VEgcPGB4 172 ipAeCcl1yxfxyM6xARg4Fm/L7AOBhAACgYBESpculbUlOxvLK8tnYNI55T3eKGXw 173 9oSpxhgEzczq98PhaDu+ajjOqdD7DrM/VyvQuOwvhChPDTOlhazRZwyPCX1lUWnY 174 gXWfkeyb1H3jXz0cOQe2iHCSSMSYr2sH/E7kOMknJClemVFjWC7KO1F1yFXAspPs 175 V2pT9Twi0IeXmw== 176 -----END PUBLIC KEY-----`}) 177 parser := NewParser(config) 178 179 req := &http.Request{Header: http.Header{"Authorization": {"Bearer " + tokenString}}} 180 181 _, err = parser.ParseFromRequest(req) 182 assert.Error(t, err) 183 } 184 185 func TestParser_Parse_ErrUnsupportedSigningMethod(t *testing.T) { 186 alg := "PS256" 187 188 tokenString, err := generateToken(alg, rsa2048Private) 189 require.NoError(t, err) 190 191 config := NewParserConfig(0, SigningMethod{Alg: alg, Key: rsa2048Public}) 192 parser := NewParser(config) 193 194 req := &http.Request{Header: http.Header{"Authorization": {"Bearer " + tokenString}}} 195 196 _, err = parser.ParseFromRequest(req) 197 assert.Error(t, err) 198 } 199 200 const ( 201 clientID = "test-client-id" 202 userName = "test@hellofresh.com" 203 204 rsa2048Private = `-----BEGIN RSA PRIVATE KEY----- 205 MIIEowIBAAKCAQEAvHA+KjSHPzVp7HIVGrQvxUNnYrfdUR5+MRn3SM/Ts7Gwwfif 206 IlTgqjRHjm+sPOhVauF+ZX5PkUmW/HBlOxsjzA55mCBFymO+nyjl/DhhFNLnKu3I 207 WL8Q3IsnM1EgE9FHgwFqf3X5Eh5h2NdsOWPkFrOB6BKY1wkOI2E27bNJAat+F059 208 HU9z+jgIwhcm/IciTbqts497x1can4+NFBOlVWE+yii6tREHF8olVe9a1DA8k7mt 209 OQ2+1bK69kxm7tIde5sWnrlG3dv0gvlF25b3XhiFAMJ1RfgvQHjXbtmMaZdMxj/K 210 x4CvBM37eXBGUSDWt9q97g+ywIQ/NrPZWmHoewIDAQABAoIBAFLFbtj1F89Q9AUT 211 G2gOa8lXUStQnhtKrJ1+zVsjRtdwnralMalP5Rt+OUw8i0h5uUNoZy/HqsWjsHmU 212 GTM8OZ4hYZHL4zwCUjHxMgx261XNShNWPSGWU568VOy6nr91tta5oYD5Xf1ycQJh 213 pb0TvpWmJdK9kHssFBTAV/NTRCdB3klSSQ0t9gIfsa7ILYylaQQMyEtO0u6mTDxT 214 JjAeIWhYrALU12gLQD4jndF9ouzzgut0mcFnQbNt9vhXTEC1ZRghlRL95ELlrSi0 215 8AMxgtaiMcIeRezDo4Y+SAAPkVzUprlGEts5TBWcP53/BPfo9Mf0WBBDKhjPsIcx 216 cFKjp5ECgYEA7z691lJPdj9A0xEMZia5ZcCu2yL8DCGFLDEyDAsCfts9RpIAobb5 217 X/jOvklwXSvgtvkaiZcfMbgeR3KallWYQFN/q28CX/KPFLm7iA+ON5/nEqgOYlTS 218 /dLb1JQUs2qfNPjpWAzVL4KLO+fZyUXVYo15uw+M/CFMqZxmUoh3LnMCgYEAyaKf 219 33RUYhO8vZj6oumAddAOVg3t4jqEJ7IkvrbXIyEPQT5P1DmJHWSmeca8b+Y8pvz9 220 hIeSuygqWLDS3U5y4MfBYFBsQLQqM0KntjOItW0G/1KqM9YkNBIG7Gfk7fGxH2f8 221 sOEIVA8V9i2HM62k7ZJ+9lxBFJ7BsCq5UBwzE9kCgYAzyVb6T3LX27VCesw+SF+V 222 QPIYiSgZ0B+tgzCcHr35i6dl4TC10I+GUKsf0XG7GUZZFO7Dnayo7HvRZ2NC62A7 223 fFeEWlEfR7fk+pc3Sna0X657AVmru0S4oK3pA+y/MXMo2kBYSN7Um+NbokIoKS+Z 224 V5pj/We9I9AeXrZfYx65NQKBgGd7yjdhuckYPh7Ee6XO1zofzKvHvFYGGDtTR16F 225 8kY6Ol0OwOO3n7JxLKuFHsMDVA+T+fzho6HgTFN2dNJV58mLW6i1vck7bgke5Xoy 226 WrBaQ2QYpfeyqKP8uIbuD2U7TN9EfEC/TYnusCPHXANe1C2FqRmBYXlWvStP0gnW 227 XzSJAoGBALVfBV/WXcTArvbsT2KvwJovZG9kmSiR3ba3iXIeGwvBxtuDyeodz5k8 228 9S3m+ev58TCf1lYad+FAavr1ro8fbFyZZV6HItz4v377VcljxlvN739ST6R1RY36 229 PX7Lwn6YrQ2gk9efgKAEmcxBenq6UKkNXEGeiv4vxGG/cuTepQme 230 -----END RSA PRIVATE KEY-----` 231 rsa2048Public = `-----BEGIN PUBLIC KEY----- 232 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHA+KjSHPzVp7HIVGrQv 233 xUNnYrfdUR5+MRn3SM/Ts7GwwfifIlTgqjRHjm+sPOhVauF+ZX5PkUmW/HBlOxsj 234 zA55mCBFymO+nyjl/DhhFNLnKu3IWL8Q3IsnM1EgE9FHgwFqf3X5Eh5h2NdsOWPk 235 FrOB6BKY1wkOI2E27bNJAat+F059HU9z+jgIwhcm/IciTbqts497x1can4+NFBOl 236 VWE+yii6tREHF8olVe9a1DA8k7mtOQ2+1bK69kxm7tIde5sWnrlG3dv0gvlF25b3 237 XhiFAMJ1RfgvQHjXbtmMaZdMxj/Kx4CvBM37eXBGUSDWt9q97g+ywIQ/NrPZWmHo 238 ewIDAQAB 239 -----END PUBLIC KEY-----` 240 ) 241 242 func assertParseToken(t *testing.T, parser *Parser, r *http.Request) { 243 token, err := parser.ParseFromRequest(r) 244 require.NoError(t, err) 245 246 claims, ok := parser.GetMapClaims(token) 247 assert.True(t, ok) 248 assert.Equal(t, clientID, claims["iss"]) 249 assert.Equal(t, userName, claims["username"]) 250 } 251 252 func generateToken(alg, key string) (string, error) { 253 type userClaims struct { 254 Username string `json:"username"` 255 baseJWT.StandardClaims 256 } 257 258 token := baseJWT.NewWithClaims(baseJWT.GetSigningMethod(alg), userClaims{ 259 userName, 260 baseJWT.StandardClaims{ 261 Issuer: clientID, 262 IssuedAt: time.Now().Unix(), 263 ExpiresAt: time.Now().Add(time.Hour).Unix(), 264 }, 265 }) 266 267 var signingKey interface{} 268 switch token.Method.(type) { 269 case *baseJWT.SigningMethodHMAC: 270 signingKey = []byte(key) 271 case *baseJWT.SigningMethodRSA, *baseJWT.SigningMethodRSAPSS: 272 block, _ := pem.Decode([]byte(key)) 273 if block == nil { 274 return "", ErrInvalidPEMBlock 275 } 276 if got, want := block.Type, "RSA PRIVATE KEY"; got != want { 277 return "", errors.New("invalid RSA: expected RSA PRIVATE KEY block type") 278 } 279 280 var err error 281 signingKey, err = x509.ParsePKCS1PrivateKey(block.Bytes) 282 if nil != err { 283 return "", err 284 } 285 default: 286 return "", ErrUnsupportedSigningMethod 287 } 288 289 return token.SignedString(signingKey) 290 }