Reply token

This commit is contained in:
LittleSheep 2024-12-21 21:29:59 +08:00
parent 7733abde5d
commit 5fae613da5
4 changed files with 73 additions and 12 deletions

View File

@ -7,8 +7,6 @@ import (
"git.solsynth.dev/hypernet/messaging/pkg/internal/http/exts" "git.solsynth.dev/hypernet/messaging/pkg/internal/http/exts"
"git.solsynth.dev/hypernet/messaging/pkg/internal/models" "git.solsynth.dev/hypernet/messaging/pkg/internal/models"
"git.solsynth.dev/hypernet/messaging/pkg/internal/services" "git.solsynth.dev/hypernet/messaging/pkg/internal/services"
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/google/uuid" "github.com/google/uuid"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
@ -20,13 +18,23 @@ import (
// It did not support all the features of the message event // It did not support all the features of the message event
// But it just works // But it just works
func quickReply(c *fiber.Ctx) error { func quickReply(c *fiber.Ctx) error {
if err := sec.EnsureAuthenticated(c); err != nil { replyTk := c.Query("replyToken")
return err if len(replyTk) == 0 {
return fiber.NewError(fiber.StatusBadRequest, "reply token is required")
} }
user := c.Locals("user").(authm.Account)
claims, err := services.ParseReplyToken(replyTk)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("reply token is invaild: %v", err))
}
channelId, _ := c.ParamsInt("channelId", 0) channelId, _ := c.ParamsInt("channelId", 0)
eventId, _ := c.ParamsInt("eventId", 0) eventId, _ := c.ParamsInt("eventId", 0)
if claims.EventID != uint(eventId) {
return fiber.NewError(fiber.StatusBadRequest, "reply token is invaild, event id mismatch")
}
var data struct { var data struct {
Type string `json:"type" validate:"required"` Type string `json:"type" validate:"required"`
Body models.EventMessageBody `json:"body"` Body models.EventMessageBody `json:"body"`
@ -43,7 +51,7 @@ func quickReply(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "empty message was not allowed") return fiber.NewError(fiber.StatusBadRequest, "empty message was not allowed")
} }
channel, member, err := services.GetChannelIdentityWithID(uint(channelId), user.ID) channel, member, err := services.GetChannelIdentityWithID(uint(channelId), claims.UserID)
if err != nil { if err != nil {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("channel / member not found: %v", err.Error())) return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("channel / member not found: %v", err.Error()))
} }

View File

@ -180,6 +180,7 @@ func NotifyMessageEvent(members []models.ChannelMember, event models.Event) {
} }
displayTitle := fmt.Sprintf("%s (%s)", event.Sender.Nick, event.Channel.DisplayText()) displayTitle := fmt.Sprintf("%s (%s)", event.Sender.Nick, event.Channel.DisplayText())
replyToken, err := CreateReplyToken(event.ID, event.Sender.AccountID)
metadata := map[string]any{ metadata := map[string]any{
"avatar": event.Sender.Avatar, "avatar": event.Sender.Avatar,
@ -188,6 +189,7 @@ func NotifyMessageEvent(members []models.ChannelMember, event models.Event) {
"user_nick": event.Sender.Nick, "user_nick": event.Sender.Nick,
"channel_id": event.ChannelID, "channel_id": event.ChannelID,
"event_id": event.ID, "event_id": event.ID,
"reply_token": replyToken,
} }
if len(pendingUsers) > 0 { if len(pendingUsers) > 0 {

View File

@ -0,0 +1,50 @@
package services
import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/spf13/viper"
)
type ReplyClaims struct {
UserID uint `json:"user_id"`
EventID uint `json:"event_id"`
jwt.RegisteredClaims
}
func CreateReplyToken(eventId uint, userId uint) (string, error) {
claims := ReplyClaims{
UserID: userId,
EventID: eventId,
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "messaging",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 7)),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)
tks, err := token.SignedString([]byte(viper.GetString("security.reply_token_secret")))
if err != nil {
return "", fmt.Errorf("failed to sign token: %v", err)
}
return tks, nil
}
func ParseReplyToken(tk string) (ReplyClaims, error) {
var claims ReplyClaims
token, err := jwt.ParseWithClaims(tk, &claims, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Method)
}
return []byte(viper.GetString("security.reply_token_secret")), nil
})
if err != nil {
return claims, err
}
if !token.Valid {
return claims, fmt.Errorf("invalid token")
}
return claims, nil
}

View File

@ -19,3 +19,4 @@ max_participants = 20
[security] [security]
internal_public_key = "keys/internal_public_key.pem" internal_public_key = "keys/internal_public_key.pem"
reply_token_secret = "QSu8iYYLDkGj10H3FtNI7OabpGl8N7Rg6rBagofQN4Uza3nIrXpVzDEfAiU1G2Yn"