Added TTL to tokens

This commit is contained in:
2025-02-10 17:36:13 +11:00
parent e46a55e76d
commit 04049bb73a
3 changed files with 55 additions and 44 deletions

View File

@@ -16,6 +16,7 @@ func GenerateAccessToken(
config *server.Config,
user *db.User,
fresh bool,
rememberMe bool,
) (string, error) {
issuedAt := time.Now().Unix()
expiresAt := issuedAt + (config.AccessTokenExpiry * 60)
@@ -25,15 +26,21 @@ func GenerateAccessToken(
} else {
freshExpiresAt = issuedAt
}
var ttl string
if rememberMe {
ttl = "exp"
} else {
ttl = "session"
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256,
jwt.MapClaims{
"iss": config.TrustedHost,
"scope": "access",
"ttl": ttl,
"iat": issuedAt,
"exp": expiresAt,
"fresh": freshExpiresAt,
"sub": user.ID,
"roles": []string{"user", "admin"}, // TODO: add user roles
})
signedToken, err := token.SignedString([]byte(config.SecretKey))
@@ -47,13 +54,21 @@ func GenerateAccessToken(
func GenerateRefreshToken(
config *server.Config,
user *db.User,
rememberMe bool,
) (string, error) {
issuedAt := time.Now().Unix()
expiresAt := issuedAt + (config.RefreshTokenExpiry * 60)
var ttl string
if rememberMe {
ttl = "exp"
} else {
ttl = "session"
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256,
jwt.MapClaims{
"iss": config.TrustedHost,
"scope": "refresh",
"ttl": ttl,
"jti": uuid.New(),
"iat": issuedAt,
"exp": expiresAt,

View File

@@ -26,6 +26,10 @@ func ParseAccessToken(config *server.Config, tokenString string) (AccessToken, e
if err != nil {
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"])
if err != nil {
return AccessToken{}, errors.Wrap(err, "getTokenScope")
@@ -45,19 +49,14 @@ func ParseAccessToken(config *server.Config, tokenString string) (AccessToken, e
if err != nil {
return AccessToken{}, errors.Wrap(err, "getFreshTime")
}
roles, err := getTokenRoles(claims["roles"])
if err != nil {
return AccessToken{}, errors.Wrap(err, "getTokenRoles")
}
token := AccessToken{
ISS: issuer,
Scope: scope,
TTL: ttl,
EXP: expiry,
IAT: issuedAt,
SUB: subject,
Fresh: fresh,
Roles: roles,
}
return token, nil
@@ -79,6 +78,10 @@ func ParseRefreshToken(config *server.Config, tokenString string) (RefreshToken,
if err != nil {
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"])
if err != nil {
return RefreshToken{}, errors.Wrap(err, "getTokenScope")
@@ -101,7 +104,7 @@ func ParseRefreshToken(config *server.Config, tokenString string) (RefreshToken,
token := RefreshToken{
ISS: issuer,
Scope: scope,
TTL: ttl,
EXP: expiry,
IAT: issuedAt,
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
func checkTokenIssuer(trustedHost string, issuer interface{}) (string, error) {
// Check issuer
issuerVal, ok := issuer.(string)
if !ok {
return "", errors.New("Missing or invalid 'iss' claim")
@@ -171,6 +173,18 @@ func getTokenScope(scope interface{}) (string, error) {
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
func getIssuedTime(issued interface{}) (int64, error) {
// Same float64 -> int64 trick as expiry
@@ -200,23 +214,6 @@ func getTokenSubject(sub interface{}) (int, error) {
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
func getTokenJTI(jti interface{}) (uuid.UUID, error) {
jtiStr, ok := jti.(string)

View File

@@ -4,21 +4,20 @@ import "github.com/google/uuid"
// Access token
type AccessToken struct {
ISS string
Scope string
IAT int64
EXP int64
SUB int
Fresh int64
Roles []string
ISS string // Issuer, generally TrustedHost
IAT int64 // Time issued at
EXP int64 // Time expiring at
TTL string // Time-to-live: "session" or "exp". Used with 'remember me'
SUB int // Subject (user) ID
Fresh int64 // Time freshness expiring at
}
// Refresh token
type RefreshToken struct {
ISS string
Scope string
IAT int64
EXP int64
SUB int
JTI uuid.UUID
ISS string // Issuer, generally TrustedHost
IAT int64 // Time issued at
EXP int64 // Time expiring at
TTL string // Time-to-live: "session" or "exp". Used with 'remember me'
SUB int // Subject (user) ID
JTI uuid.UUID // UUID-4 used for identifying blacklisted refresh tokens
}