93 lines
2.9 KiB
Go
93 lines
2.9 KiB
Go
// Package auth implements parsing and validation of the JWT tokens used by
|
|
// the game server. Both the game token and the user token are HS256-signed
|
|
// with the same shared secret (the main server's secret_key_base).
|
|
package auth
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
// GameClaims is the payload of the game token issued by the main server
|
|
// when a match is created.
|
|
type GameClaims struct {
|
|
MatchID string `json:"match_id"`
|
|
Player1ID int64 `json:"player1_id"`
|
|
Player2ID int64 `json:"player2_id"`
|
|
GameServer string `json:"game_server"`
|
|
RawToken string `json:"-"`
|
|
jwt.RegisteredClaims
|
|
}
|
|
|
|
// UserClaims is the payload of the personal token of a player.
|
|
type UserClaims struct {
|
|
UserID int64 `json:"user_id"`
|
|
jwt.RegisteredClaims
|
|
}
|
|
|
|
func keyFunc(secret []byte) jwt.Keyfunc {
|
|
return func(t *jwt.Token) (any, error) {
|
|
if t.Method.Alg() != jwt.SigningMethodHS256.Alg() {
|
|
return nil, fmt.Errorf("unexpected signing method: %s", t.Method.Alg())
|
|
}
|
|
return secret, nil
|
|
}
|
|
}
|
|
|
|
// ParseGameToken verifies the signature and the expiration time of a game
|
|
// token and returns its claims.
|
|
func ParseGameToken(secret []byte, tokenString string) (*GameClaims, error) {
|
|
claims := &GameClaims{}
|
|
_, err := jwt.ParseWithClaims(tokenString, claims, keyFunc(secret),
|
|
jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}),
|
|
jwt.WithExpirationRequired(),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid game token: %w", err)
|
|
}
|
|
if claims.MatchID == "" {
|
|
return nil, fmt.Errorf("invalid game token: empty match_id")
|
|
}
|
|
if claims.Player1ID <= 0 || claims.Player2ID <= 0 {
|
|
return nil, fmt.Errorf("invalid game token: player IDs must be positive")
|
|
}
|
|
claims.RawToken = tokenString
|
|
return claims, nil
|
|
}
|
|
|
|
// ParseGameTokenUnverified extracts claims from a game token without verifying
|
|
// its signature or expiration.
|
|
func ParseGameTokenUnverified(tokenString string) (*GameClaims, error) {
|
|
claims := &GameClaims{}
|
|
_, _, err := jwt.NewParser().ParseUnverified(tokenString, claims)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid game token: %w", err)
|
|
}
|
|
if claims.MatchID == "" {
|
|
return nil, fmt.Errorf("invalid game token: empty match_id")
|
|
}
|
|
if claims.Player1ID <= 0 || claims.Player2ID <= 0 {
|
|
return nil, fmt.Errorf("invalid game token: player IDs must be positive")
|
|
}
|
|
claims.RawToken = tokenString
|
|
return claims, nil
|
|
}
|
|
|
|
// ParseUserToken verifies the signature and the expiration time of a user
|
|
// token and returns its claims.
|
|
func ParseUserToken(secret []byte, tokenString string) (*UserClaims, error) {
|
|
claims := &UserClaims{}
|
|
_, err := jwt.ParseWithClaims(tokenString, claims, keyFunc(secret),
|
|
jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}),
|
|
jwt.WithExpirationRequired(),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid user token: %w", err)
|
|
}
|
|
if claims.UserID <= 0 {
|
|
return nil, fmt.Errorf("invalid user token: user_id must be positive")
|
|
}
|
|
return claims, nil
|
|
}
|