2019-12-16 23:22:09 +08:00
|
|
|
// Copyright (c) Mainflux
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
package jwt
|
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/dgrijalva/jwt-go"
|
|
|
|
"github.com/mainflux/mainflux/authn"
|
2020-06-03 21:16:19 +08:00
|
|
|
"github.com/mainflux/mainflux/pkg/errors"
|
2019-12-16 23:22:09 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type claims struct {
|
|
|
|
jwt.StandardClaims
|
|
|
|
Type *uint32 `json:"type,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c claims) Valid() error {
|
|
|
|
if c.Type == nil || *c.Type > authn.APIKey {
|
|
|
|
return authn.ErrMalformedEntity
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.StandardClaims.Valid()
|
|
|
|
}
|
|
|
|
|
|
|
|
type tokenizer struct {
|
|
|
|
secret string
|
|
|
|
}
|
|
|
|
|
|
|
|
// New returns new JWT Tokenizer.
|
|
|
|
func New(secret string) authn.Tokenizer {
|
|
|
|
return tokenizer{secret: secret}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (svc tokenizer) Issue(key authn.Key) (string, error) {
|
|
|
|
claims := claims{
|
|
|
|
StandardClaims: jwt.StandardClaims{
|
|
|
|
Issuer: key.Issuer,
|
|
|
|
Subject: key.Secret,
|
|
|
|
IssuedAt: key.IssuedAt.UTC().Unix(),
|
|
|
|
},
|
|
|
|
Type: &key.Type,
|
|
|
|
}
|
|
|
|
|
|
|
|
if !key.ExpiresAt.IsZero() {
|
|
|
|
claims.ExpiresAt = key.ExpiresAt.UTC().Unix()
|
|
|
|
}
|
|
|
|
if key.ID != "" {
|
|
|
|
claims.Id = key.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
|
|
return token.SignedString([]byte(svc.secret))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (svc tokenizer) Parse(token string) (authn.Key, error) {
|
|
|
|
c := claims{}
|
|
|
|
_, err := jwt.ParseWithClaims(token, &c, func(token *jwt.Token) (interface{}, error) {
|
|
|
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
|
|
return nil, authn.ErrUnauthorizedAccess
|
|
|
|
}
|
|
|
|
return []byte(svc.secret), nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if e, ok := err.(*jwt.ValidationError); ok && e.Errors == jwt.ValidationErrorExpired {
|
|
|
|
// Expired User key needs to be revoked.
|
|
|
|
if c.Type != nil && *c.Type == authn.APIKey {
|
|
|
|
return c.toKey(), nil
|
|
|
|
}
|
2020-04-10 23:43:42 +08:00
|
|
|
return authn.Key{}, errors.Wrap(authn.ErrKeyExpired, err)
|
2019-12-16 23:22:09 +08:00
|
|
|
}
|
2020-04-10 23:43:42 +08:00
|
|
|
return authn.Key{}, errors.Wrap(authn.ErrUnauthorizedAccess, err)
|
2019-12-16 23:22:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return c.toKey(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c claims) toKey() authn.Key {
|
|
|
|
key := authn.Key{
|
|
|
|
ID: c.Id,
|
|
|
|
Issuer: c.Issuer,
|
|
|
|
Secret: c.Subject,
|
|
|
|
IssuedAt: time.Unix(c.IssuedAt, 0).UTC(),
|
|
|
|
}
|
|
|
|
if c.ExpiresAt != 0 {
|
|
|
|
key.ExpiresAt = time.Unix(c.ExpiresAt, 0).UTC()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default type is 0.
|
|
|
|
if c.Type != nil {
|
|
|
|
key.Type = *c.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
return key
|
|
|
|
}
|