|
| 1 | +package jwtgo |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "errors" |
| 6 | + "testing" |
| 7 | + |
| 8 | + "github.com/golang-jwt/jwt" |
| 9 | + "github.com/google/go-cmp/cmp" |
| 10 | +) |
| 11 | + |
| 12 | +type testingCustomClaims struct { |
| 13 | + Foo string `json:"foo"` |
| 14 | + ReturnError error |
| 15 | + jwt.StandardClaims |
| 16 | +} |
| 17 | + |
| 18 | +func (tcc *testingCustomClaims) Validate(ctx context.Context) error { |
| 19 | + return tcc.ReturnError |
| 20 | +} |
| 21 | + |
| 22 | +func equalErrors(actual error, expected string) bool { |
| 23 | + if actual == nil { |
| 24 | + return expected == "" |
| 25 | + } |
| 26 | + return actual.Error() == expected |
| 27 | +} |
| 28 | + |
| 29 | +func Test_Validate(t *testing.T) { |
| 30 | + testCases := []struct { |
| 31 | + name string |
| 32 | + signatureAlgorithm string |
| 33 | + token string |
| 34 | + keyFuncReturnError error |
| 35 | + customClaims CustomClaims |
| 36 | + expectedError string |
| 37 | + expectedContext jwt.Claims |
| 38 | + }{ |
| 39 | + { |
| 40 | + name: "happy path", |
| 41 | + token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.Rq8IxqeX7eA6GgYxlcHdPFVRNFFZc5rEI3MQTZZbK3I`, |
| 42 | + expectedContext: &jwt.StandardClaims{Subject: "1234567890"}, |
| 43 | + }, |
| 44 | + { |
| 45 | + // we want to test that when it expects RSA but we send |
| 46 | + // HMAC encrypted with the server public key it will |
| 47 | + // error |
| 48 | + name: "errors on wrong algorithm", |
| 49 | + signatureAlgorithm: "PS256", |
| 50 | + token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o`, |
| 51 | + expectedError: "could not parse the token: signing method HS256 is invalid", |
| 52 | + }, |
| 53 | + { |
| 54 | + name: "errors on wrong token format errors", |
| 55 | + expectedError: "could not parse the token: token contains an invalid number of segments", |
| 56 | + }, |
| 57 | + { |
| 58 | + name: "errors when the key func errors", |
| 59 | + token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o`, |
| 60 | + keyFuncReturnError: errors.New("key func error message"), |
| 61 | + expectedError: "could not parse the token: key func error message", |
| 62 | + }, |
| 63 | + { |
| 64 | + name: "errors when signature is invalid", |
| 65 | + token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.hDyICUnkCrwFJnkJHRSkwMZNSYZ9LI6z2EFJdtwFurA`, |
| 66 | + expectedError: "could not parse the token: signature is invalid", |
| 67 | + }, |
| 68 | + { |
| 69 | + name: "errors when custom claims errors", |
| 70 | + token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZm9vIjoiYmFyIiwiaWF0IjoxNTE2MjM5MDIyfQ.DFTWyYib4-xFdMaEZFAYx5AKMPNS7Hhl4kcyjQVinYc`, |
| 71 | + customClaims: &testingCustomClaims{ReturnError: errors.New("custom claims error message")}, |
| 72 | + expectedError: "custom claims not validated: custom claims error message", |
| 73 | + }, |
| 74 | + } |
| 75 | + |
| 76 | + for _, testCase := range testCases { |
| 77 | + t.Run(testCase.name, func(t *testing.T) { |
| 78 | + var customClaimsFunc func() CustomClaims = nil |
| 79 | + if testCase.customClaims != nil { |
| 80 | + customClaimsFunc = func() CustomClaims { return testCase.customClaims } |
| 81 | + } |
| 82 | + |
| 83 | + v, _ := New(func(token *jwt.Token) (interface{}, error) { |
| 84 | + return []byte("secret"), testCase.keyFuncReturnError |
| 85 | + }, |
| 86 | + testCase.signatureAlgorithm, |
| 87 | + WithCustomClaims(customClaimsFunc), |
| 88 | + ) |
| 89 | + actualContext, err := v.ValidateToken(context.Background(), testCase.token) |
| 90 | + if !equalErrors(err, testCase.expectedError) { |
| 91 | + t.Fatalf("wanted err:\n%s\ngot:\n%+v\n", testCase.expectedError, err) |
| 92 | + } |
| 93 | + |
| 94 | + if (testCase.expectedContext == nil && actualContext != nil) || (testCase.expectedContext != nil && actualContext == nil) { |
| 95 | + t.Fatalf("wanted user context:\n%+v\ngot:\n%+v\n", testCase.expectedContext, actualContext) |
| 96 | + } else if testCase.expectedContext != nil { |
| 97 | + if diff := cmp.Diff(testCase.expectedContext, actualContext.(jwt.Claims)); diff != "" { |
| 98 | + t.Errorf("user context mismatch (-want +got):\n%s", diff) |
| 99 | + } |
| 100 | + |
| 101 | + } |
| 102 | + }) |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +func Test_New(t *testing.T) { |
| 107 | + t.Run("happy path", func(t *testing.T) { |
| 108 | + keyFunc := func(t *jwt.Token) (interface{}, error) { return nil, nil } |
| 109 | + customClaims := func() CustomClaims { return nil } |
| 110 | + |
| 111 | + v, err := New(keyFunc, "HS256", WithCustomClaims(customClaims)) |
| 112 | + |
| 113 | + if !equalErrors(err, "") { |
| 114 | + t.Fatalf("wanted err:\n%s\ngot:\n%+v\n", "", err) |
| 115 | + } |
| 116 | + |
| 117 | + if v.keyFunc == nil { |
| 118 | + t.Log("keyFunc was nil when it should not have been") |
| 119 | + t.Fail() |
| 120 | + } |
| 121 | + |
| 122 | + if v.signatureAlgorithm != "HS256" { |
| 123 | + t.Logf("signatureAlgorithm was %q when it should have been %q", v.signatureAlgorithm, "HS256") |
| 124 | + t.Fail() |
| 125 | + } |
| 126 | + |
| 127 | + if v.customClaims == nil { |
| 128 | + t.Log("customClaims was nil when it should not have been") |
| 129 | + t.Fail() |
| 130 | + } |
| 131 | + }) |
| 132 | + |
| 133 | + t.Run("error on no keyFunc", func(t *testing.T) { |
| 134 | + _, err := New(nil, "HS256") |
| 135 | + |
| 136 | + expectedErr := "keyFunc is required but was nil" |
| 137 | + if !equalErrors(err, expectedErr) { |
| 138 | + t.Fatalf("wanted err:\n%s\ngot:\n%+v\n", expectedErr, err) |
| 139 | + } |
| 140 | + }) |
| 141 | + |
| 142 | +} |
0 commit comments