package auth import ( "errors" "time" "github.com/golang-jwt/jwt/v5" ) // 过期策略:Access 15 分钟 / Refresh 7 天(旋转)/ Invite 24 小时 const ( AccessTTL = 15 * time.Minute RefreshTTL = 7 * 24 * time.Hour InviteTTL = 24 * time.Hour ) // AccessClaims 访问令牌声明 type AccessClaims struct { UID uint64 `json:"uid"` WorkspaceID uint64 `json:"wsid"` Role string `json:"role"` MemberRole string `json:"mrole"` jwt.RegisteredClaims } // RefreshClaims 刷新令牌声明 type RefreshClaims struct { UID uint64 `json:"uid"` JTI string `json:"jti"` jwt.RegisteredClaims } // InviteClaims 邀请令牌声明 type InviteClaims struct { WorkspaceID uint64 `json:"wsid"` Email string `json:"email"` Role string `json:"role"` jwt.RegisteredClaims } // IssueAccess 签发访问令牌 func IssueAccess(secret string, uid, wsid uint64, role, memberRole string) (string, error) { now := time.Now() claims := AccessClaims{ UID: uid, WorkspaceID: wsid, Role: role, MemberRole: memberRole, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(now.Add(AccessTTL)), Issuer: "everypublish", }, } return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret)) } // IssueRefresh 签发刷新令牌 func IssueRefresh(secret string, uid uint64, jti string) (string, error) { now := time.Now() claims := RefreshClaims{ UID: uid, JTI: jti, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(now.Add(RefreshTTL)), Issuer: "everypublish", }, } return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret)) } // IssueInvite 签发邀请令牌 func IssueInvite(secret string, wsid uint64, email, role string) (string, error) { now := time.Now() claims := InviteClaims{ WorkspaceID: wsid, Email: email, Role: role, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(now.Add(InviteTTL)), Issuer: "everypublish", }, } return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret)) } // ParseAccess 解析访问令牌 func ParseAccess(secret, token string) (*AccessClaims, error) { claims := &AccessClaims{} parsed, err := jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (interface{}, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.New("unexpected signing method") } return []byte(secret), nil }) if err != nil { return nil, err } if !parsed.Valid { return nil, errors.New("invalid token") } return claims, nil } // ParseRefresh 解析刷新令牌 func ParseRefresh(secret, token string) (*RefreshClaims, error) { claims := &RefreshClaims{} parsed, err := jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (interface{}, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.New("unexpected signing method") } return []byte(secret), nil }) if err != nil { return nil, err } if !parsed.Valid { return nil, errors.New("invalid token") } return claims, nil } // ParseInvite 解析邀请令牌 func ParseInvite(secret, token string) (*InviteClaims, error) { claims := &InviteClaims{} parsed, err := jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (interface{}, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.New("unexpected signing method") } return []byte(secret), nil }) if err != nil { return nil, err } if !parsed.Valid { return nil, errors.New("invalid token") } return claims, nil }