|  | package jwt | 
|  |  | 
|  | import ( | 
|  | "encoding/json" | 
|  | "time" | 
|  |  | 
|  | "github.com/SermoDigital/jose" | 
|  | ) | 
|  |  | 
|  | // Claims implements a set of JOSE Claims with the addition of some helper | 
|  | // methods, similar to net/url.Values. | 
|  | type Claims map[string]interface{} | 
|  |  | 
|  | // Validate validates the Claims per the claims found in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1 | 
|  | func (c Claims) Validate(now time.Time, expLeeway, nbfLeeway time.Duration) error { | 
|  | if exp, ok := c.Expiration(); ok { | 
|  | if now.After(exp.Add(expLeeway)) { | 
|  | return ErrTokenIsExpired | 
|  | } | 
|  | } | 
|  |  | 
|  | if nbf, ok := c.NotBefore(); ok { | 
|  | if !now.After(nbf.Add(-nbfLeeway)) { | 
|  | return ErrTokenNotYetValid | 
|  | } | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // Get retrieves the value corresponding with key from the Claims. | 
|  | func (c Claims) Get(key string) interface{} { | 
|  | if c == nil { | 
|  | return nil | 
|  | } | 
|  | return c[key] | 
|  | } | 
|  |  | 
|  | // Set sets Claims[key] = val. It'll overwrite without warning. | 
|  | func (c Claims) Set(key string, val interface{}) { | 
|  | c[key] = val | 
|  | } | 
|  |  | 
|  | // Del removes the value that corresponds with key from the Claims. | 
|  | func (c Claims) Del(key string) { | 
|  | delete(c, key) | 
|  | } | 
|  |  | 
|  | // Has returns true if a value for the given key exists inside the Claims. | 
|  | func (c Claims) Has(key string) bool { | 
|  | _, ok := c[key] | 
|  | return ok | 
|  | } | 
|  |  | 
|  | // MarshalJSON implements json.Marshaler for Claims. | 
|  | func (c Claims) MarshalJSON() ([]byte, error) { | 
|  | if c == nil || len(c) == 0 { | 
|  | return nil, nil | 
|  | } | 
|  | return json.Marshal(map[string]interface{}(c)) | 
|  | } | 
|  |  | 
|  | // Base64 implements the jose.Encoder interface. | 
|  | func (c Claims) Base64() ([]byte, error) { | 
|  | b, err := c.MarshalJSON() | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | return jose.Base64Encode(b), nil | 
|  | } | 
|  |  | 
|  | // UnmarshalJSON implements json.Unmarshaler for Claims. | 
|  | func (c *Claims) UnmarshalJSON(b []byte) error { | 
|  | if b == nil { | 
|  | return nil | 
|  | } | 
|  |  | 
|  | b, err := jose.DecodeEscaped(b) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | // Since json.Unmarshal calls UnmarshalJSON, | 
|  | // calling json.Unmarshal on *p would be infinitely recursive | 
|  | // A temp variable is needed because &map[string]interface{}(*p) is | 
|  | // invalid Go. (Address of unaddressable object and all that...) | 
|  |  | 
|  | tmp := map[string]interface{}(*c) | 
|  | if err = json.Unmarshal(b, &tmp); err != nil { | 
|  | return err | 
|  | } | 
|  | *c = Claims(tmp) | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // Issuer retrieves claim "iss" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.1 | 
|  | func (c Claims) Issuer() (string, bool) { | 
|  | v, ok := c.Get("iss").(string) | 
|  | return v, ok | 
|  | } | 
|  |  | 
|  | // Subject retrieves claim "sub" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.2 | 
|  | func (c Claims) Subject() (string, bool) { | 
|  | v, ok := c.Get("sub").(string) | 
|  | return v, ok | 
|  | } | 
|  |  | 
|  | // Audience retrieves claim "aud" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.3 | 
|  | func (c Claims) Audience() ([]string, bool) { | 
|  | // Audience claim must be stringy. That is, it may be one string | 
|  | // or multiple strings but it should not be anything else. E.g. an int. | 
|  | switch t := c.Get("aud").(type) { | 
|  | case string: | 
|  | return []string{t}, true | 
|  | case []string: | 
|  | return t, true | 
|  | case []interface{}: | 
|  | return stringify(t...) | 
|  | case interface{}: | 
|  | return stringify(t) | 
|  | } | 
|  | return nil, false | 
|  | } | 
|  |  | 
|  | func stringify(a ...interface{}) ([]string, bool) { | 
|  | if len(a) == 0 { | 
|  | return nil, false | 
|  | } | 
|  |  | 
|  | s := make([]string, len(a)) | 
|  | for i := range a { | 
|  | str, ok := a[i].(string) | 
|  | if !ok { | 
|  | return nil, false | 
|  | } | 
|  | s[i] = str | 
|  | } | 
|  | return s, true | 
|  | } | 
|  |  | 
|  | // Expiration retrieves claim "exp" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.4 | 
|  | func (c Claims) Expiration() (time.Time, bool) { | 
|  | return c.GetTime("exp") | 
|  | } | 
|  |  | 
|  | // NotBefore retrieves claim "nbf" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.5 | 
|  | func (c Claims) NotBefore() (time.Time, bool) { | 
|  | return c.GetTime("nbf") | 
|  | } | 
|  |  | 
|  | // IssuedAt retrieves claim "iat" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.6 | 
|  | func (c Claims) IssuedAt() (time.Time, bool) { | 
|  | return c.GetTime("iat") | 
|  | } | 
|  |  | 
|  | // JWTID retrieves claim "jti" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.7 | 
|  | func (c Claims) JWTID() (string, bool) { | 
|  | v, ok := c.Get("jti").(string) | 
|  | return v, ok | 
|  | } | 
|  |  | 
|  | // RemoveIssuer deletes claim "iss" from c. | 
|  | func (c Claims) RemoveIssuer() { c.Del("iss") } | 
|  |  | 
|  | // RemoveSubject deletes claim "sub" from c. | 
|  | func (c Claims) RemoveSubject() { c.Del("sub") } | 
|  |  | 
|  | // RemoveAudience deletes claim "aud" from c. | 
|  | func (c Claims) RemoveAudience() { c.Del("aud") } | 
|  |  | 
|  | // RemoveExpiration deletes claim "exp" from c. | 
|  | func (c Claims) RemoveExpiration() { c.Del("exp") } | 
|  |  | 
|  | // RemoveNotBefore deletes claim "nbf" from c. | 
|  | func (c Claims) RemoveNotBefore() { c.Del("nbf") } | 
|  |  | 
|  | // RemoveIssuedAt deletes claim "iat" from c. | 
|  | func (c Claims) RemoveIssuedAt() { c.Del("iat") } | 
|  |  | 
|  | // RemoveJWTID deletes claim "jti" from c. | 
|  | func (c Claims) RemoveJWTID() { c.Del("jti") } | 
|  |  | 
|  | // SetIssuer sets claim "iss" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.1 | 
|  | func (c Claims) SetIssuer(issuer string) { | 
|  | c.Set("iss", issuer) | 
|  | } | 
|  |  | 
|  | // SetSubject sets claim "iss" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.2 | 
|  | func (c Claims) SetSubject(subject string) { | 
|  | c.Set("sub", subject) | 
|  | } | 
|  |  | 
|  | // SetAudience sets claim "aud" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.3 | 
|  | func (c Claims) SetAudience(audience ...string) { | 
|  | if len(audience) == 1 { | 
|  | c.Set("aud", audience[0]) | 
|  | } else { | 
|  | c.Set("aud", audience) | 
|  | } | 
|  | } | 
|  |  | 
|  | // SetExpiration sets claim "exp" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.4 | 
|  | func (c Claims) SetExpiration(expiration time.Time) { | 
|  | c.SetTime("exp", expiration) | 
|  | } | 
|  |  | 
|  | // SetNotBefore sets claim "nbf" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.5 | 
|  | func (c Claims) SetNotBefore(notBefore time.Time) { | 
|  | c.SetTime("nbf", notBefore) | 
|  | } | 
|  |  | 
|  | // SetIssuedAt sets claim "iat" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.6 | 
|  | func (c Claims) SetIssuedAt(issuedAt time.Time) { | 
|  | c.SetTime("iat", issuedAt) | 
|  | } | 
|  |  | 
|  | // SetJWTID sets claim "jti" per its type in | 
|  | // https://tools.ietf.org/html/rfc7519#section-4.1.7 | 
|  | func (c Claims) SetJWTID(uniqueID string) { | 
|  | c.Set("jti", uniqueID) | 
|  | } | 
|  |  | 
|  | // GetTime returns a Unix timestamp for the given key. | 
|  | // | 
|  | // It converts an int, int32, int64, uint, uint32, uint64 or float64 into a Unix | 
|  | // timestamp (epoch seconds). float32 does not have sufficient precision to | 
|  | // store a Unix timestamp. | 
|  | // | 
|  | // Numeric values parsed from JSON will always be stored as float64 since | 
|  | // Claims is a map[string]interface{}. However, the values may be stored directly | 
|  | // in the claims as a different type. | 
|  | func (c Claims) GetTime(key string) (time.Time, bool) { | 
|  | switch t := c.Get(key).(type) { | 
|  | case int: | 
|  | return time.Unix(int64(t), 0), true | 
|  | case int32: | 
|  | return time.Unix(int64(t), 0), true | 
|  | case int64: | 
|  | return time.Unix(int64(t), 0), true | 
|  | case uint: | 
|  | return time.Unix(int64(t), 0), true | 
|  | case uint32: | 
|  | return time.Unix(int64(t), 0), true | 
|  | case uint64: | 
|  | return time.Unix(int64(t), 0), true | 
|  | case float64: | 
|  | return time.Unix(int64(t), 0), true | 
|  | default: | 
|  | return time.Time{}, false | 
|  | } | 
|  | } | 
|  |  | 
|  | // SetTime stores a UNIX time for the given key. | 
|  | func (c Claims) SetTime(key string, t time.Time) { | 
|  | c.Set(key, t.Unix()) | 
|  | } | 
|  |  | 
|  | var ( | 
|  | _ json.Marshaler   = (Claims)(nil) | 
|  | _ json.Unmarshaler = (*Claims)(nil) | 
|  | ) |