Added TTL to tokens
This commit is contained in:
@@ -16,6 +16,7 @@ func GenerateAccessToken(
|
|||||||
config *server.Config,
|
config *server.Config,
|
||||||
user *db.User,
|
user *db.User,
|
||||||
fresh bool,
|
fresh bool,
|
||||||
|
rememberMe bool,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
issuedAt := time.Now().Unix()
|
issuedAt := time.Now().Unix()
|
||||||
expiresAt := issuedAt + (config.AccessTokenExpiry * 60)
|
expiresAt := issuedAt + (config.AccessTokenExpiry * 60)
|
||||||
@@ -25,15 +26,21 @@ func GenerateAccessToken(
|
|||||||
} else {
|
} else {
|
||||||
freshExpiresAt = issuedAt
|
freshExpiresAt = issuedAt
|
||||||
}
|
}
|
||||||
|
var ttl string
|
||||||
|
if rememberMe {
|
||||||
|
ttl = "exp"
|
||||||
|
} else {
|
||||||
|
ttl = "session"
|
||||||
|
}
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256,
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256,
|
||||||
jwt.MapClaims{
|
jwt.MapClaims{
|
||||||
"iss": config.TrustedHost,
|
"iss": config.TrustedHost,
|
||||||
"scope": "access",
|
"scope": "access",
|
||||||
|
"ttl": ttl,
|
||||||
"iat": issuedAt,
|
"iat": issuedAt,
|
||||||
"exp": expiresAt,
|
"exp": expiresAt,
|
||||||
"fresh": freshExpiresAt,
|
"fresh": freshExpiresAt,
|
||||||
"sub": user.ID,
|
"sub": user.ID,
|
||||||
"roles": []string{"user", "admin"}, // TODO: add user roles
|
|
||||||
})
|
})
|
||||||
|
|
||||||
signedToken, err := token.SignedString([]byte(config.SecretKey))
|
signedToken, err := token.SignedString([]byte(config.SecretKey))
|
||||||
@@ -47,13 +54,21 @@ func GenerateAccessToken(
|
|||||||
func GenerateRefreshToken(
|
func GenerateRefreshToken(
|
||||||
config *server.Config,
|
config *server.Config,
|
||||||
user *db.User,
|
user *db.User,
|
||||||
|
rememberMe bool,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
issuedAt := time.Now().Unix()
|
issuedAt := time.Now().Unix()
|
||||||
expiresAt := issuedAt + (config.RefreshTokenExpiry * 60)
|
expiresAt := issuedAt + (config.RefreshTokenExpiry * 60)
|
||||||
|
var ttl string
|
||||||
|
if rememberMe {
|
||||||
|
ttl = "exp"
|
||||||
|
} else {
|
||||||
|
ttl = "session"
|
||||||
|
}
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256,
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256,
|
||||||
jwt.MapClaims{
|
jwt.MapClaims{
|
||||||
"iss": config.TrustedHost,
|
"iss": config.TrustedHost,
|
||||||
"scope": "refresh",
|
"scope": "refresh",
|
||||||
|
"ttl": ttl,
|
||||||
"jti": uuid.New(),
|
"jti": uuid.New(),
|
||||||
"iat": issuedAt,
|
"iat": issuedAt,
|
||||||
"exp": expiresAt,
|
"exp": expiresAt,
|
||||||
|
|||||||
47
jwt/parse.go
47
jwt/parse.go
@@ -26,6 +26,10 @@ func ParseAccessToken(config *server.Config, tokenString string) (AccessToken, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return AccessToken{}, errors.Wrap(err, "checkTokenIssuer")
|
return AccessToken{}, errors.Wrap(err, "checkTokenIssuer")
|
||||||
}
|
}
|
||||||
|
ttl, err := getTokenTTL(claims["ttl"])
|
||||||
|
if err != nil {
|
||||||
|
return AccessToken{}, errors.Wrap(err, "getTokenTTL")
|
||||||
|
}
|
||||||
scope, err := getTokenScope(claims["scope"])
|
scope, err := getTokenScope(claims["scope"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return AccessToken{}, errors.Wrap(err, "getTokenScope")
|
return AccessToken{}, errors.Wrap(err, "getTokenScope")
|
||||||
@@ -45,19 +49,14 @@ func ParseAccessToken(config *server.Config, tokenString string) (AccessToken, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return AccessToken{}, errors.Wrap(err, "getFreshTime")
|
return AccessToken{}, errors.Wrap(err, "getFreshTime")
|
||||||
}
|
}
|
||||||
roles, err := getTokenRoles(claims["roles"])
|
|
||||||
if err != nil {
|
|
||||||
return AccessToken{}, errors.Wrap(err, "getTokenRoles")
|
|
||||||
}
|
|
||||||
|
|
||||||
token := AccessToken{
|
token := AccessToken{
|
||||||
ISS: issuer,
|
ISS: issuer,
|
||||||
Scope: scope,
|
TTL: ttl,
|
||||||
EXP: expiry,
|
EXP: expiry,
|
||||||
IAT: issuedAt,
|
IAT: issuedAt,
|
||||||
SUB: subject,
|
SUB: subject,
|
||||||
Fresh: fresh,
|
Fresh: fresh,
|
||||||
Roles: roles,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return token, nil
|
return token, nil
|
||||||
@@ -79,6 +78,10 @@ func ParseRefreshToken(config *server.Config, tokenString string) (RefreshToken,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return RefreshToken{}, errors.Wrap(err, "checkTokenIssuer")
|
return RefreshToken{}, errors.Wrap(err, "checkTokenIssuer")
|
||||||
}
|
}
|
||||||
|
ttl, err := getTokenTTL(claims["ttl"])
|
||||||
|
if err != nil {
|
||||||
|
return RefreshToken{}, errors.Wrap(err, "getTokenTTL")
|
||||||
|
}
|
||||||
scope, err := getTokenScope(claims["scope"])
|
scope, err := getTokenScope(claims["scope"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return RefreshToken{}, errors.Wrap(err, "getTokenScope")
|
return RefreshToken{}, errors.Wrap(err, "getTokenScope")
|
||||||
@@ -101,7 +104,7 @@ func ParseRefreshToken(config *server.Config, tokenString string) (RefreshToken,
|
|||||||
|
|
||||||
token := RefreshToken{
|
token := RefreshToken{
|
||||||
ISS: issuer,
|
ISS: issuer,
|
||||||
Scope: scope,
|
TTL: ttl,
|
||||||
EXP: expiry,
|
EXP: expiry,
|
||||||
IAT: issuedAt,
|
IAT: issuedAt,
|
||||||
SUB: subject,
|
SUB: subject,
|
||||||
@@ -151,7 +154,6 @@ func checkTokenExpired(expiry interface{}) (int64, error) {
|
|||||||
|
|
||||||
// Check if a token has a valid issuer. Returns the issuer if valid
|
// Check if a token has a valid issuer. Returns the issuer if valid
|
||||||
func checkTokenIssuer(trustedHost string, issuer interface{}) (string, error) {
|
func checkTokenIssuer(trustedHost string, issuer interface{}) (string, error) {
|
||||||
// Check issuer
|
|
||||||
issuerVal, ok := issuer.(string)
|
issuerVal, ok := issuer.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", errors.New("Missing or invalid 'iss' claim")
|
return "", errors.New("Missing or invalid 'iss' claim")
|
||||||
@@ -171,6 +173,18 @@ func getTokenScope(scope interface{}) (string, error) {
|
|||||||
return scopeStr, nil
|
return scopeStr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the TTL of the token, either "session" or "exp"
|
||||||
|
func getTokenTTL(ttl interface{}) (string, error) {
|
||||||
|
ttlStr, ok := ttl.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("Missing or invalid 'ttl' claim")
|
||||||
|
}
|
||||||
|
if ttlStr != "exp" && ttlStr != "session" {
|
||||||
|
return "", errors.New("TTL value is not recognised")
|
||||||
|
}
|
||||||
|
return ttlStr, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Get the time the token was issued at
|
// Get the time the token was issued at
|
||||||
func getIssuedTime(issued interface{}) (int64, error) {
|
func getIssuedTime(issued interface{}) (int64, error) {
|
||||||
// Same float64 -> int64 trick as expiry
|
// Same float64 -> int64 trick as expiry
|
||||||
@@ -200,23 +214,6 @@ func getTokenSubject(sub interface{}) (int, error) {
|
|||||||
return int(subject), nil
|
return int(subject), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the roles of the token subject
|
|
||||||
func getTokenRoles(roles interface{}) ([]string, error) {
|
|
||||||
rolesIfSlice, ok := roles.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("Missing or invalid 'roles' claim")
|
|
||||||
}
|
|
||||||
rolesSlice := []string{}
|
|
||||||
for _, role := range rolesIfSlice {
|
|
||||||
if str, ok := role.(string); ok {
|
|
||||||
rolesSlice = append(rolesSlice, str)
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("Malformed 'roles' claim")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rolesSlice, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the JTI of the token
|
// Get the JTI of the token
|
||||||
func getTokenJTI(jti interface{}) (uuid.UUID, error) {
|
func getTokenJTI(jti interface{}) (uuid.UUID, error) {
|
||||||
jtiStr, ok := jti.(string)
|
jtiStr, ok := jti.(string)
|
||||||
|
|||||||
@@ -4,21 +4,20 @@ import "github.com/google/uuid"
|
|||||||
|
|
||||||
// Access token
|
// Access token
|
||||||
type AccessToken struct {
|
type AccessToken struct {
|
||||||
ISS string
|
ISS string // Issuer, generally TrustedHost
|
||||||
Scope string
|
IAT int64 // Time issued at
|
||||||
IAT int64
|
EXP int64 // Time expiring at
|
||||||
EXP int64
|
TTL string // Time-to-live: "session" or "exp". Used with 'remember me'
|
||||||
SUB int
|
SUB int // Subject (user) ID
|
||||||
Fresh int64
|
Fresh int64 // Time freshness expiring at
|
||||||
Roles []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh token
|
// Refresh token
|
||||||
type RefreshToken struct {
|
type RefreshToken struct {
|
||||||
ISS string
|
ISS string // Issuer, generally TrustedHost
|
||||||
Scope string
|
IAT int64 // Time issued at
|
||||||
IAT int64
|
EXP int64 // Time expiring at
|
||||||
EXP int64
|
TTL string // Time-to-live: "session" or "exp". Used with 'remember me'
|
||||||
SUB int
|
SUB int // Subject (user) ID
|
||||||
JTI uuid.UUID
|
JTI uuid.UUID // UUID-4 used for identifying blacklisted refresh tokens
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user