@@ -28,5 +28,5 @@ SECRETS_TURNSTILE_SECRET=0x4AAAAAACI5pgVONOZ0rzyAYsdUcoOBF8w
|
||||
|
||||
TTL_MAGIC_LINK_TTL=10m
|
||||
TTL_ACCESS_TTL=15s
|
||||
TTL_REFRESH_TTL=7d
|
||||
TTL_REFRESH_TTL=168h
|
||||
TTL_CHECKIN_COSE_TTL=10m
|
||||
|
||||
@@ -51,7 +51,7 @@ type secrets struct {
|
||||
|
||||
type ttl struct {
|
||||
MagicLinkTTL string `yaml:"magic_link_ttl"`
|
||||
JwtTTL string `yaml:"jwt_ttl"`
|
||||
AccessTTL string `yaml:"access_ttl"`
|
||||
RefreshTTL string `yaml:"refresh_ttl"`
|
||||
CheckinCodeTTL string `yaml:"checkin_code_ttl"`
|
||||
}
|
||||
|
||||
@@ -4,19 +4,16 @@ import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"nixcn-cms/data"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
UserID uuid.UUID
|
||||
Application string
|
||||
}
|
||||
|
||||
@@ -26,9 +23,9 @@ type JwtClaims struct {
|
||||
}
|
||||
|
||||
// Generate jwt clames
|
||||
func (self *Token) NewClaims() JwtClaims {
|
||||
func (self *Token) NewClaims(userId uuid.UUID) JwtClaims {
|
||||
return JwtClaims{
|
||||
UserID: self.UserID,
|
||||
UserID: userId,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(viper.GetDuration("ttl.access_ttl"))),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
@@ -38,8 +35,8 @@ func (self *Token) NewClaims() JwtClaims {
|
||||
}
|
||||
|
||||
// Generate access token
|
||||
func (self *Token) GenerateAccessToken() (string, error) {
|
||||
claims := self.NewClaims()
|
||||
func (self *Token) GenerateAccessToken(userId uuid.UUID) (string, error) {
|
||||
claims := self.NewClaims(userId)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
secret := viper.GetString("secrets.jwt_secret")
|
||||
signedToken, err := token.SignedString([]byte(secret))
|
||||
@@ -59,9 +56,9 @@ func (self *Token) GenerateRefreshToken() (string, error) {
|
||||
}
|
||||
|
||||
// Issue both access and refresh token
|
||||
func (self *Token) IssueTokens() (string, string, error) {
|
||||
func (self *Token) IssueTokens(userId uuid.UUID) (string, string, error) {
|
||||
// Gen atk
|
||||
access, err := self.GenerateAccessToken()
|
||||
access, err := self.GenerateAccessToken(userId)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
@@ -80,14 +77,14 @@ func (self *Token) IssueTokens() (string, string, error) {
|
||||
if err := data.Redis.Set(
|
||||
ctx,
|
||||
"refresh:"+refresh,
|
||||
self.UserID.String(),
|
||||
userId.String(),
|
||||
ttl,
|
||||
).Err(); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// user -> refresh tokens
|
||||
userSetKey := "user:" + self.UserID.String() + ":refresh_tokens"
|
||||
userSetKey := "user:" + userId.String() + ":refresh_tokens"
|
||||
|
||||
if err := data.Redis.SAdd(
|
||||
ctx,
|
||||
@@ -109,49 +106,52 @@ func (self *Token) RefreshAccessToken(refreshToken string) (string, error) {
|
||||
ctx := context.Background()
|
||||
key := "refresh:" + refreshToken
|
||||
|
||||
userIDStr, err := data.Redis.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
return "", errors.New("invalid refresh token")
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
userIdStr, err := data.Redis.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
self.UserID = userID
|
||||
userId, err := uuid.Parse(userIdStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Generate access token
|
||||
return self.GenerateAccessToken()
|
||||
return self.GenerateAccessToken(userId)
|
||||
}
|
||||
|
||||
func (self *Token) RenewRefreshToken(refreshToken string) (string, error) {
|
||||
err := self.RevokeRefreshToken(refreshToken)
|
||||
ctx := context.Background()
|
||||
ttl := viper.GetDuration("ttl.refresh_ttl")
|
||||
|
||||
key := "refresh:" + refreshToken
|
||||
userIdStr, err := data.Redis.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
refresh, err := self.GenerateRefreshToken()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Store to redis
|
||||
ctx := context.Background()
|
||||
ttl := viper.GetDuration("ttl.refresh_ttl")
|
||||
err = self.RevokeRefreshToken(refreshToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// refresh -> user
|
||||
if err := data.Redis.Set(
|
||||
ctx,
|
||||
"refresh:"+refresh,
|
||||
self.UserID.String(),
|
||||
userIdStr,
|
||||
ttl,
|
||||
).Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// user -> refresh tokens
|
||||
userSetKey := "user:" + self.UserID.String() + ":refresh_tokens"
|
||||
userSetKey := "user:" + userIdStr + ":refresh_tokens"
|
||||
|
||||
if err := data.Redis.SAdd(
|
||||
ctx,
|
||||
|
||||
@@ -99,10 +99,9 @@ func VerifyMagicLink(c *gin.Context) {
|
||||
|
||||
// Generate jwt
|
||||
JwtTool := cryptography.Token{
|
||||
UserID: user.UserId,
|
||||
Application: viper.GetString("server.application"),
|
||||
}
|
||||
accessToken, refreshToken, err := JwtTool.IssueTokens()
|
||||
accessToken, refreshToken, err := JwtTool.IssueTokens(user.UserId)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{
|
||||
"status": "error generating tokens",
|
||||
|
||||
@@ -13,7 +13,7 @@ func Refresh(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, gin.H{"error": "invalid request"})
|
||||
c.JSON(400, gin.H{"status": "invalid request"})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -23,18 +23,13 @@ func Refresh(c *gin.Context) {
|
||||
|
||||
access, err := JwtTool.RefreshAccessToken(req.RefreshToken)
|
||||
if err != nil {
|
||||
c.JSON(401, gin.H{"error": "invalid refresh token"})
|
||||
c.JSON(401, gin.H{"status": "invalid refresh token"})
|
||||
return
|
||||
}
|
||||
|
||||
err = JwtTool.RevokeRefreshToken(req.RefreshToken)
|
||||
refresh, err := JwtTool.RenewRefreshToken(req.RefreshToken)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"status": "cannot revoke refresh token"})
|
||||
}
|
||||
|
||||
refresh, err := JwtTool.GenerateRefreshToken()
|
||||
if err != nil {
|
||||
c.JSON(401, gin.H{"status": "cannot generate new refresh token"})
|
||||
c.JSON(500, gin.H{"statis": "error renew refresh token"})
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
package event
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
import (
|
||||
"nixcn-cms/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Handler(r *gin.RouterGroup) {
|
||||
r.Use(middleware.JWTAuth())
|
||||
r.GET("/info", Info)
|
||||
r.GET("/query", Query)
|
||||
}
|
||||
|
||||
36
service/event/query.go
Normal file
36
service/event/query.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"nixcn-cms/data"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func Query(c *gin.Context) {
|
||||
userId, ok := c.Get("user_id")
|
||||
if !ok {
|
||||
c.JSON(400, gin.H{"status": "could not found user_id"})
|
||||
return
|
||||
}
|
||||
eventId, ok := c.GetQuery("event_id")
|
||||
if !ok {
|
||||
c.JSON(400, gin.H{"status": "could not found event_id"})
|
||||
return
|
||||
}
|
||||
|
||||
data := new(data.User)
|
||||
err := data.GetByUserId(userId.(uuid.UUID))
|
||||
if err != nil {
|
||||
c.JSON(404, gin.H{"status": "cannot found user"})
|
||||
return
|
||||
}
|
||||
if data.Checkin[eventId] == nil {
|
||||
c.JSON(404, gin.H{"status": "cannot found user checked in"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"checkin_time": data.Checkin[eventId],
|
||||
})
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
func Handler(r *gin.RouterGroup) {
|
||||
r.Use(middleware.JWTAuth())
|
||||
r.GET("/info", Info)
|
||||
r.POST("/checkin", Checkin)
|
||||
r.GET("/checkin", Checkin)
|
||||
r.POST("/checkin/submit", CheckinSubmit)
|
||||
r.PATCH("/update", Update)
|
||||
r.GET("/list", List)
|
||||
|
||||
Reference in New Issue
Block a user