Initial commit

This commit is contained in:
Magnus Åhall 2025-05-04 08:48:06 +02:00
commit f26e6c10b3
3 changed files with 118 additions and 0 deletions

5
go.mod Normal file
View file

@ -0,0 +1,5 @@
module jwtsession
go 1.24.2
require github.com/golang-jwt/jwt/v5 v5.2.2

2
go.sum Normal file
View file

@ -0,0 +1,2 @@
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=

111
pkg.go Normal file
View file

@ -0,0 +1,111 @@
/*
The JWT claims contains:
cid - Session UUUID
uid - User ID
iat - Issued at
exp - Expires
login - Username
name - User name
auth - Session authenticated
perm - User permissions
*/
package jwtsession
import (
// External
"github.com/golang-jwt/jwt/v5"
// Standard
"encoding/hex"
"fmt"
"time"
)
type Manager struct {
secret []byte
ExpireDays int
Initialized bool
}
func NewManager(secret string, expireDays int) (mngr Manager, err error) { // {{{
mngr.secret, err = hex.DecodeString(secret)
mngr.ExpireDays = expireDays
mngr.Initialized = true
return
} // }}}
func (mngr *Manager) NewToken(sessionID string, userID int, username, name string) (token string) { // {{{
// A new token is generated with the information.
data := make(map[string]any)
data["cid"] = sessionID
data["uid"] = userID
data["login"] = username
data["name"] = name
token = mngr.GenerateToken(data)
return
} // }}}
func (mngr *Manager) GenerateToken(data map[string]any) (signedString string) { // {{{
// Create a new token object, specifying signing method and the claims
// you would like it to contain.
now := time.Now()
data["iat"] = now.Unix()
data["exp"] = now.Add(time.Hour * 24 * time.Duration(mngr.ExpireDays)).Unix()
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(data))
// Sign and get the complete encoded token as a string using the secret.
signedString, _ = token.SignedString(mngr.secret)
return
} // }}}
func (mngr *Manager) ParseToken(tokenString string) (jwt.MapClaims, error) { // {{{
// Parse takes the token string and a function for looking up the key. The latter is especially
// useful if you use multiple keys for your application. The standard is to use 'kid' in the
// head of the token to identify which key to use, but the parsed token (head and claims) is provided
// to the callback, providing flexibility.
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
// Don't forget to validate the alg is what you expect:
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
// hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
return mngr.secret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(jwt.MapClaims); ok {
err = validateTokenTimestamps(claims)
if err != nil {
return nil, err
}
return claims, nil
} else {
return nil, err
}
} // }}}
func (mngr *Manager) GetTokenClaims(tokenString string) (jwt.MapClaims, error) { // {{{
return nil, nil
} // }}}
func validateTokenTimestamps(claims jwt.MapClaims) error { // {{{
now := time.Now()
if issuedAt, ok := claims["iat"].(float64); ok {
if now.Unix() < int64(issuedAt) {
return fmt.Errorf("Token is not valid yet")
}
} else {
return fmt.Errorf("Token is missing iat")
}
if expires, ok := claims["exp"].(float64); ok {
if now.Unix() > int64(expires) {
return fmt.Errorf("Token has expired")
}
}
return nil
} // }}}