Compare commits

..

3 Commits

Author SHA1 Message Date
d4adc1b538 feat: refactoring 2025-05-21 21:59:50 +02:00
edfa3e63b9 feat: generate refresh token 2025-05-21 21:59:31 +02:00
7c58473ff1 feat: define ApiClaims 2025-05-21 21:59:15 +02:00
3 changed files with 48 additions and 15 deletions

View File

@ -86,30 +86,55 @@ func (h *AuthHandler) login(w http.ResponseWriter, r *http.Request) {
return return
} }
claims := types.UserClaims{ accessClaims := types.UserClaims{
UserID: user.ID.String(), UserEmail: user.Email,
RegisteredClaims: jwt.RegisteredClaims{ RegisteredClaims: jwt.RegisteredClaims{
Issuer: "hspguard", Issuer: "hspguard",
Subject: user.Email, Subject: user.ID.String(),
IssuedAt: jwt.NewNumericDate(time.Now()), IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
}, },
} }
token, err := SignJwtToken(claims) accessToken, err := SignJwtToken(accessClaims)
if err != nil { if err != nil {
web.Error(w, fmt.Sprintf("failed to generate access token: %v", err), http.StatusBadRequest) web.Error(w, fmt.Sprintf("failed to generate access token: %v", err), http.StatusBadRequest)
return return
} }
refreshClaims := types.UserClaims{
UserEmail: user.Email,
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "hspguard",
Subject: user.ID.String(),
IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(30 * 24 * time.Hour)),
},
}
refreshToken, err := SignJwtToken(refreshClaims)
if err != nil {
web.Error(w, fmt.Sprintf("failed to generate refresh token: %v", err), http.StatusBadRequest)
return
}
encoder := json.NewEncoder(w) encoder := json.NewEncoder(w)
type Response struct { type Response struct {
Token string `json:"token"` AccessToken string `json:"access"`
RefreshToken string `json:"refresh"`
// fields required for UI in account selector, e.g. email, full name and avatar
FullName string `json:"full_name"`
Email string `json:"email"`
// Avatar
} }
if err := encoder.Encode(Response{ if err := encoder.Encode(Response{
Token: token, AccessToken: accessToken,
RefreshToken: refreshToken,
FullName: user.FullName,
Email: user.Email,
// Avatar
}); err != nil { }); err != nil {
web.Error(w, "failed to encode response", http.StatusInternalServerError) web.Error(w, "failed to encode response", http.StatusInternalServerError)
} }

View File

@ -32,7 +32,7 @@ func AuthMiddleware(next http.Handler) http.Handler {
return return
} }
ctx := context.WithValue(r.Context(), types.UserIdKey, userClaims.UserID) ctx := context.WithValue(r.Context(), types.UserIdKey, userClaims.Subject)
next.ServeHTTP(w, r.WithContext(ctx)) next.ServeHTTP(w, r.WithContext(ctx))
}) })
} }
@ -50,4 +50,3 @@ func WithSkipper(mw func(http.Handler) http.Handler, excludedPaths ...string) fu
}) })
} }
} }

View File

@ -3,8 +3,17 @@ package types
import "github.com/golang-jwt/jwt/v5" import "github.com/golang-jwt/jwt/v5"
type UserClaims struct { type UserClaims struct {
UserID string `json:"user_id"` UserEmail string `json:"user_email"`
// Role
jwt.RegisteredClaims jwt.RegisteredClaims
} }
type ApiClaims struct {
UserID string `json:"user_id"`
// Permissions are guard's defined permissions
// Examples:
// 1. User MetaData (specifically some fields like email, profile picture and name)
// 2. Actions on User, e.g. home permissions fetching, notifications emitting
Permissions []string `json:"permissions"`
// Subject is an API ID defined in guard's DB after registration
jwt.RegisteredClaims
}