Moved config and finished JWT module

This commit is contained in:
2025-02-10 22:10:03 +11:00
parent 04049bb73a
commit e73805a02d
13 changed files with 237 additions and 69 deletions

View File

@@ -3,8 +3,8 @@ package jwt
import (
"time"
"projectreshoot/config"
"projectreshoot/db"
"projectreshoot/server"
"github.com/golang-jwt/jwt"
"github.com/google/uuid"
@@ -13,11 +13,11 @@ import (
// Generates an access token for the provided user
func GenerateAccessToken(
config *server.Config,
config *config.Config,
user *db.User,
fresh bool,
rememberMe bool,
) (string, error) {
) (tokenStr string, exp int64, err error) {
issuedAt := time.Now().Unix()
expiresAt := issuedAt + (config.AccessTokenExpiry * 60)
var freshExpiresAt int64
@@ -37,6 +37,7 @@ func GenerateAccessToken(
"iss": config.TrustedHost,
"scope": "access",
"ttl": ttl,
"jti": uuid.New(),
"iat": issuedAt,
"exp": expiresAt,
"fresh": freshExpiresAt,
@@ -45,17 +46,17 @@ func GenerateAccessToken(
signedToken, err := token.SignedString([]byte(config.SecretKey))
if err != nil {
return "", errors.Wrap(err, "token.SignedString")
return "", 0, errors.Wrap(err, "token.SignedString")
}
return signedToken, nil
return signedToken, expiresAt, nil
}
// Generates a refresh token for the provided user
func GenerateRefreshToken(
config *server.Config,
config *config.Config,
user *db.User,
rememberMe bool,
) (string, error) {
) (tokenStr string, exp int64, err error) {
issuedAt := time.Now().Unix()
expiresAt := issuedAt + (config.RefreshTokenExpiry * 60)
var ttl string
@@ -77,7 +78,7 @@ func GenerateRefreshToken(
signedToken, err := token.SignedString([]byte(config.SecretKey))
if err != nil {
return "", errors.Wrap(err, "token.SignedString")
return "", 0, errors.Wrap(err, "token.SignedString")
}
return signedToken, nil
return signedToken, expiresAt, nil
}

View File

@@ -2,9 +2,10 @@ package jwt
import (
"fmt"
"projectreshoot/server"
"time"
"projectreshoot/config"
"github.com/golang-jwt/jwt"
"github.com/google/uuid"
"github.com/pkg/errors"
@@ -13,7 +14,7 @@ import (
// Parse an access token and return a struct with all the claims. Does validation on
// all the claims, including checking if it is expired, has a valid issuer, and
// has the correct scope.
func ParseAccessToken(config *server.Config, tokenString string) (AccessToken, error) {
func ParseAccessToken(config *config.Config, tokenString string) (AccessToken, error) {
claims, err := parseToken(config.SecretKey, tokenString)
if err != nil {
return AccessToken{}, errors.Wrap(err, "parseToken")
@@ -49,6 +50,10 @@ func ParseAccessToken(config *server.Config, tokenString string) (AccessToken, e
if err != nil {
return AccessToken{}, errors.Wrap(err, "getFreshTime")
}
jti, err := getTokenJTI(claims["jti"])
if err != nil {
return AccessToken{}, errors.Wrap(err, "getTokenJTI")
}
token := AccessToken{
ISS: issuer,
@@ -57,6 +62,8 @@ func ParseAccessToken(config *server.Config, tokenString string) (AccessToken, e
IAT: issuedAt,
SUB: subject,
Fresh: fresh,
JTI: jti,
Scope: scope,
}
return token, nil
@@ -65,7 +72,7 @@ func ParseAccessToken(config *server.Config, tokenString string) (AccessToken, e
// Parse a refresh token and return a struct with all the claims. Does validation on
// all the claims, including checking if it is expired, has a valid issuer, and
// has the correct scope.
func ParseRefreshToken(config *server.Config, tokenString string) (RefreshToken, error) {
func ParseRefreshToken(config *config.Config, tokenString string) (RefreshToken, error) {
claims, err := parseToken(config.SecretKey, tokenString)
if err != nil {
return RefreshToken{}, errors.Wrap(err, "parseToken")
@@ -103,12 +110,13 @@ func ParseRefreshToken(config *server.Config, tokenString string) (RefreshToken,
}
token := RefreshToken{
ISS: issuer,
TTL: ttl,
EXP: expiry,
IAT: issuedAt,
SUB: subject,
JTI: jti,
ISS: issuer,
TTL: ttl,
EXP: expiry,
IAT: issuedAt,
SUB: subject,
JTI: jti,
Scope: scope,
}
return token, nil

31
jwt/revoke.go Normal file
View File

@@ -0,0 +1,31 @@
package jwt
import (
"database/sql"
"github.com/pkg/errors"
)
// Revoke a token by adding it to the database
func RevokeToken(conn *sql.DB, t Token) error {
jti := t.GetJTI()
exp := t.GetEXP()
query := `INSERT INTO jwtblacklist (jti, exp) VALUES (?, ?)`
_, err := conn.Exec(query, jti, exp)
if err != nil {
return errors.Wrap(err, "conn.Exec")
}
return nil
}
// Check if a token has been revoked
func CheckRevoked(conn *sql.DB, t Token) (bool, error) {
jti := t.GetJTI()
query := `SELECT 1 FROM jwtblacklist WHERE jti = ? LIMIT 1`
rows, err := conn.Query(query, jti)
if err != nil {
return false, errors.Wrap(err, "conn.Exec")
}
revoked := rows.Next()
return revoked, nil
}

View File

@@ -2,22 +2,50 @@ package jwt
import "github.com/google/uuid"
type Token interface {
GetJTI() uuid.UUID
GetEXP() int64
GetScope() string
}
// Access token
type AccessToken struct {
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
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 tokens
Fresh int64 // Time freshness expiring at
Scope string // Should be "access"
}
// Refresh token
type RefreshToken struct {
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
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 tokens
Scope string // Should be "refresh"
}
func (a AccessToken) GetJTI() uuid.UUID {
return a.JTI
}
func (r RefreshToken) GetJTI() uuid.UUID {
return r.JTI
}
func (a AccessToken) GetEXP() int64 {
return a.EXP
}
func (r RefreshToken) GetEXP() int64 {
return r.EXP
}
func (a AccessToken) GetScope() string {
return a.Scope
}
func (r RefreshToken) GetScope() string {
return r.Scope
}