package jwt_test import ( "context" "reflect" "testing" "time" "github.com/lestrrat-go/jwx/internal/json" "github.com/lestrrat-go/jwx/jwt" "github.com/stretchr/testify/assert" ) const ( tokenTime = 233431200 ) var zeroval reflect.Value var expectedTokenTime = time.Unix(tokenTime, 0).UTC() func TestHeader(t *testing.T) { t.Parallel() values := map[string]interface{}{ jwt.AudienceKey: []string{"developers", "secops", "tac"}, jwt.ExpirationKey: expectedTokenTime, jwt.IssuedAtKey: expectedTokenTime, jwt.IssuerKey: "http://www.example.com", jwt.JwtIDKey: "e9bc097a-ce51-4036-9562-d2ade882db0d", jwt.NotBeforeKey: expectedTokenTime, jwt.SubjectKey: "unit test", } t.Run("Roundtrip", func(t *testing.T) { t.Parallel() h := jwt.New() for k, v := range values { if !assert.NoError(t, h.Set(k, v), `h.Set should succeed for key %#v`, k) { return } got, ok := h.Get(k) if !assert.True(t, ok, `h.Get should succeed for key %#v`, k) { return } if !reflect.DeepEqual(v, got) { t.Fatalf("Values do not match: (%v, %v)", v, got) } } }) t.Run("RoundtripError", func(t *testing.T) { t.Parallel() type dummyStruct struct { dummy1 int dummy2 float64 } dummy := &dummyStruct{1, 3.4} values := map[string]interface{}{ jwt.AudienceKey: dummy, jwt.ExpirationKey: dummy, jwt.IssuedAtKey: dummy, jwt.IssuerKey: dummy, jwt.JwtIDKey: dummy, jwt.NotBeforeKey: dummy, jwt.SubjectKey: dummy, } h := jwt.New() for k, v := range values { err := h.Set(k, v) if err == nil { t.Fatalf("Setting %s value should have failed", k) } } err := h.Set("default", dummy) // private params if err != nil { t.Fatalf("Setting %s value failed", "default") } for k := range values { _, ok := h.Get(k) if ok { t.Fatalf("Getting %s value should have failed", k) } } _, ok := h.Get("default") if !ok { t.Fatal("Failed to get default value") } }) t.Run("GetError", func(t *testing.T) { t.Parallel() h := jwt.New() issuer := h.Issuer() if issuer != "" { t.Fatalf("Get Issuer should return empty string") } jwtID := h.JwtID() if jwtID != "" { t.Fatalf("Get JWT Id should return empty string") } }) } func TestTokenMarshal(t *testing.T) { t.Parallel() t1 := jwt.New() err := t1.Set(jwt.JwtIDKey, "AbCdEfG") if err != nil { t.Fatalf("Failed to set JWT ID: %s", err.Error()) } err = t1.Set(jwt.SubjectKey, "foobar@example.com") if err != nil { t.Fatalf("Failed to set Subject: %s", err.Error()) } // Silly fix to remove monotonic element from time.Time obtained // from time.Now(). Without this, the equality comparison goes // ga-ga for golang tip (1.9) now := time.Unix(time.Now().Unix(), 0) err = t1.Set(jwt.IssuedAtKey, now.Unix()) if err != nil { t.Fatalf("Failed to set IssuedAt: %s", err.Error()) } err = t1.Set(jwt.NotBeforeKey, now.Add(5*time.Second)) if err != nil { t.Fatalf("Failed to set NotBefore: %s", err.Error()) } err = t1.Set(jwt.ExpirationKey, now.Add(10*time.Second).Unix()) if err != nil { t.Fatalf("Failed to set Expiration: %s", err.Error()) } err = t1.Set(jwt.AudienceKey, []string{"devops", "secops", "tac"}) if err != nil { t.Fatalf("Failed to set audience: %s", err.Error()) } err = t1.Set("custom", "MyValue") if err != nil { t.Fatalf(`Failed to set private claim "custom": %s`, err.Error()) } jsonbuf1, err := json.MarshalIndent(t1, "", " ") if err != nil { t.Fatalf("JSON Marshal failed: %s", err.Error()) } t2 := jwt.New() if !assert.NoError(t, json.Unmarshal(jsonbuf1, t2), `json.Unmarshal should succeed`) { return } if !assert.Equal(t, t1, t2, "tokens should match") { return } _, err = json.MarshalIndent(t2, "", " ") if err != nil { t.Fatalf("JSON marshal error: %s", err.Error()) } } func TestToken(t *testing.T) { tok := jwt.New() def := map[string]struct { Value interface{} Method string }{ jwt.AudienceKey: { Method: "Audience", Value: []string{"developers", "secops", "tac"}, }, jwt.ExpirationKey: { Method: "Expiration", Value: expectedTokenTime, }, jwt.IssuedAtKey: { Method: "IssuedAt", Value: expectedTokenTime, }, jwt.IssuerKey: { Method: "Issuer", Value: "http://www.example.com", }, jwt.JwtIDKey: { Method: "JwtID", Value: "e9bc097a-ce51-4036-9562-d2ade882db0d", }, jwt.NotBeforeKey: { Method: "NotBefore", Value: expectedTokenTime, }, jwt.SubjectKey: { Method: "Subject", Value: "unit test", }, "myClaim": { Value: "hello, world", }, } t.Run("Set", func(t *testing.T) { for k, kdef := range def { if !assert.NoError(t, tok.Set(k, kdef.Value), `tok.Set(%s) should succeed`, k) { return } } }) t.Run("Get", func(t *testing.T) { rv := reflect.ValueOf(tok) for k, kdef := range def { getval, ok := tok.Get(k) if !assert.True(t, ok, `tok.Get(%s) should succeed`, k) { return } if mname := kdef.Method; mname != "" { method := rv.MethodByName(mname) if !assert.NotEqual(t, zeroval, method, `method %s should not be zero value`, mname) { return } retvals := method.Call(nil) if !assert.Len(t, retvals, 1, `should have exactly one return value`) { return } if !assert.Equal(t, getval, retvals[0].Interface(), `values should match`) { return } } } }) t.Run("Roundtrip", func(t *testing.T) { buf, err := json.Marshal(tok) if !assert.NoError(t, err, `json.Marshal should succeed`) { return } newtok, err := jwt.Parse(buf) if !assert.NoError(t, err, `jwt.Parse should succeed`) { return } m1, err := tok.AsMap(context.TODO()) if !assert.NoError(t, err, `tok.AsMap should succeed`) { return } m2, err := newtok.AsMap(context.TODO()) if !assert.NoError(t, err, `tok.AsMap should succeed`) { return } if !assert.Equal(t, m1, m2, `tokens should match`) { return } }) t.Run("Set/Remove", func(t *testing.T) { ctx := context.TODO() newtok, err := tok.Clone() if !assert.NoError(t, err, `tok.Clone should succeed`) { return } for iter := tok.Iterate(ctx); iter.Next(ctx); { pair := iter.Pair() newtok.Remove(pair.Key.(string)) } m, err := newtok.AsMap(ctx) if !assert.NoError(t, err, `tok.AsMap should succeed`) { return } if !assert.Len(t, m, 0, `toks should have 0 tok`) { return } for iter := tok.Iterate(ctx); iter.Next(ctx); { pair := iter.Pair() if !assert.NoError(t, newtok.Set(pair.Key.(string), pair.Value), `newtok.Set should succeed`) { return } } }) }