You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
synctv/server/middlewares/auth.go

130 lines
3.0 KiB
Go

package middlewares
import (
"errors"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/room"
"github.com/synctv-org/synctv/server/model"
"github.com/zijiren233/stream"
)
var (
ErrAuthFailed = errors.New("auth failed")
ErrAuthExpired = errors.New("auth expired")
)
type AuthClaims struct {
RoomId string `json:"id"`
Version uint64 `json:"v"`
Username string `json:"un"`
UserVersion uint64 `json:"uv"`
jwt.RegisteredClaims
}
func auth(Authorization string) (*AuthClaims, error) {
t, err := jwt.ParseWithClaims(strings.TrimPrefix(Authorization, `Bearer `), &AuthClaims{}, func(token *jwt.Token) (any, error) {
return stream.StringToBytes(conf.Conf.Jwt.Secret), nil
})
if err != nil {
return nil, ErrAuthFailed
}
claims, ok := t.Claims.(*AuthClaims)
if !ok || !t.Valid {
return nil, ErrAuthFailed
}
return claims, nil
}
func Auth(Authorization string, rooms *room.Rooms) (*room.User, error) {
claims, err := auth(Authorization)
if err != nil {
return nil, err
}
r, err := rooms.GetRoom(claims.RoomId)
if err != nil {
return nil, err
}
if !r.CheckVersion(claims.Version) {
return nil, ErrAuthExpired
}
user, err := r.GetUser(claims.Username)
if err != nil {
return nil, err
}
if !user.CheckVersion(claims.UserVersion) {
return nil, ErrAuthExpired
}
return user, nil
}
func AuthWithPassword(roomId, roomPassword, username, password string, rooms *room.Rooms) (*room.User, error) {
room, err := rooms.GetRoom(roomId)
if err != nil {
return nil, err
}
if !room.CheckPassword(roomPassword) {
return nil, ErrAuthFailed
}
user, err := room.GetUser(username)
if err != nil {
return nil, err
}
if !user.CheckPassword(password) {
return nil, ErrAuthFailed
}
return user, nil
}
func AuthOrNewWithPassword(roomId, roomPassword, username, password string, rooms *room.Rooms) (*room.User, error) {
room, err := rooms.GetRoom(roomId)
if err != nil {
return nil, err
}
if !room.CheckPassword(roomPassword) {
return nil, ErrAuthFailed
}
user, err := room.GetOrNewUser(username, password)
if err != nil {
return nil, err
}
if !user.CheckPassword(password) {
return nil, ErrAuthFailed
}
return user, nil
}
func AuthRoom(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := Auth(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(401, model.NewApiErrorResp(err))
return
}
ctx.Set("user", user)
ctx.Next()
}
func NewAuthToken(user *room.User) (string, error) {
claims := &AuthClaims{
RoomId: user.Room().Id(),
Version: user.Room().Version(),
Username: user.Name(),
UserVersion: user.Version(),
RegisteredClaims: jwt.RegisteredClaims{
NotBefore: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * time.Duration(conf.Conf.Jwt.Expire))),
},
}
return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret))
}