2020-07-05 01:02:57 +02:00
|
|
|
package auth
|
2020-04-10 04:40:22 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/dgrijalva/jwt-go"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// RestrictedMode is used restrict JWT access to just the install route
|
2020-07-17 02:40:23 +02:00
|
|
|
type RestrictedMode string
|
|
|
|
|
|
|
|
const (
|
2020-08-21 01:11:24 +02:00
|
|
|
// Unrestricted is the code to allow access to all routes
|
2020-07-17 02:40:23 +02:00
|
|
|
Unrestricted RestrictedMode = "unrestricted"
|
2020-08-21 01:11:24 +02:00
|
|
|
// InstallOnly is the code to restrict access ONLY to install route
|
|
|
|
InstallOnly = "install_only"
|
2020-07-17 02:40:23 +02:00
|
|
|
)
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// Role is the role code for the user
|
2020-08-01 03:01:14 +02:00
|
|
|
type Role string
|
|
|
|
|
|
|
|
const (
|
2020-08-21 01:11:24 +02:00
|
|
|
// RoleAdmin is the code for the admin role
|
|
|
|
RoleAdmin Role = "admin"
|
|
|
|
// RoleMember is the code for the member role
|
2020-08-01 03:01:14 +02:00
|
|
|
RoleMember Role = "member"
|
|
|
|
)
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// AccessTokenClaims is the claims the access JWT token contains
|
2020-07-05 01:02:57 +02:00
|
|
|
type AccessTokenClaims struct {
|
2020-07-17 02:40:23 +02:00
|
|
|
UserID string `json:"userId"`
|
|
|
|
Restricted RestrictedMode `json:"restricted"`
|
2020-08-01 03:01:14 +02:00
|
|
|
OrgRole Role `json:"orgRole"`
|
2020-07-05 01:02:57 +02:00
|
|
|
jwt.StandardClaims
|
|
|
|
}
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// ErrExpiredToken is the error returned if the token has expired
|
2020-07-05 01:02:57 +02:00
|
|
|
type ErrExpiredToken struct{}
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// Error returns the error message for ErrExpiredToken
|
2020-07-05 01:02:57 +02:00
|
|
|
func (r *ErrExpiredToken) Error() string {
|
|
|
|
return "token is expired"
|
|
|
|
}
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// ErrMalformedToken is the error returned if the token has malformed
|
2020-07-05 01:02:57 +02:00
|
|
|
type ErrMalformedToken struct{}
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// Error returns the error message for ErrMalformedToken
|
2020-07-05 01:02:57 +02:00
|
|
|
func (r *ErrMalformedToken) Error() string {
|
|
|
|
return "token is malformed"
|
|
|
|
}
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// NewAccessToken generates a new JWT access token with the correct claims
|
2020-09-13 01:03:17 +02:00
|
|
|
func NewAccessToken(userID string, restrictedMode RestrictedMode, orgRole string, jwtKey []byte) (string, error) {
|
2020-08-01 03:01:14 +02:00
|
|
|
role := RoleMember
|
|
|
|
if orgRole == "admin" {
|
|
|
|
role = RoleAdmin
|
|
|
|
}
|
2020-04-10 04:40:22 +02:00
|
|
|
accessExpirationTime := time.Now().Add(5 * time.Second)
|
|
|
|
accessClaims := &AccessTokenClaims{
|
|
|
|
UserID: userID,
|
2020-07-17 02:40:23 +02:00
|
|
|
Restricted: restrictedMode,
|
2020-08-01 03:01:14 +02:00
|
|
|
OrgRole: role,
|
2020-04-10 04:40:22 +02:00
|
|
|
StandardClaims: jwt.StandardClaims{ExpiresAt: accessExpirationTime.Unix()},
|
|
|
|
}
|
|
|
|
|
|
|
|
accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims)
|
|
|
|
accessTokenString, err := accessToken.SignedString(jwtKey)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return accessTokenString, nil
|
|
|
|
}
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// NewAccessTokenCustomExpiration creates an access token with a custom duration
|
2020-09-13 01:03:17 +02:00
|
|
|
func NewAccessTokenCustomExpiration(userID string, dur time.Duration, jwtKey []byte) (string, error) {
|
2020-04-10 04:40:22 +02:00
|
|
|
accessExpirationTime := time.Now().Add(dur)
|
|
|
|
accessClaims := &AccessTokenClaims{
|
|
|
|
UserID: userID,
|
2020-07-17 02:40:23 +02:00
|
|
|
Restricted: Unrestricted,
|
2020-08-01 03:01:14 +02:00
|
|
|
OrgRole: RoleMember,
|
2020-04-10 04:40:22 +02:00
|
|
|
StandardClaims: jwt.StandardClaims{ExpiresAt: accessExpirationTime.Unix()},
|
|
|
|
}
|
|
|
|
|
|
|
|
accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims)
|
|
|
|
accessTokenString, err := accessToken.SignedString(jwtKey)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return accessTokenString, nil
|
|
|
|
}
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// ValidateAccessToken validates a JWT access token and returns the contained claims or an error if it's invalid
|
2020-09-13 01:03:17 +02:00
|
|
|
func ValidateAccessToken(accessTokenString string, jwtKey []byte) (AccessTokenClaims, error) {
|
2020-04-10 04:40:22 +02:00
|
|
|
accessClaims := &AccessTokenClaims{}
|
|
|
|
accessToken, err := jwt.ParseWithClaims(accessTokenString, accessClaims, func(token *jwt.Token) (interface{}, error) {
|
|
|
|
return jwtKey, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if accessToken.Valid {
|
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"token": accessTokenString,
|
|
|
|
"timeToExpire": time.Unix(accessClaims.ExpiresAt, 0),
|
2020-07-05 01:02:57 +02:00
|
|
|
}).Debug("token is valid")
|
2020-04-10 04:40:22 +02:00
|
|
|
return *accessClaims, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if ve, ok := err.(*jwt.ValidationError); ok {
|
2020-12-26 19:44:47 +01:00
|
|
|
if ve.Errors&(jwt.ValidationErrorMalformed|jwt.ValidationErrorSignatureInvalid) != 0 {
|
2020-04-10 04:40:22 +02:00
|
|
|
return AccessTokenClaims{}, &ErrMalformedToken{}
|
|
|
|
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
|
|
|
|
return AccessTokenClaims{}, &ErrExpiredToken{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return AccessTokenClaims{}, err
|
|
|
|
}
|