Add guest handling

This commit is contained in:
Zoe Roux 2025-04-04 22:30:00 +02:00
parent 431055ec49
commit c5a676b2a5
No known key found for this signature in database
5 changed files with 67 additions and 20 deletions

View File

@ -12,6 +12,8 @@ EXTRA_CLAIMS='{}'
# json object with the claims to add to every jwt of the FIRST user (this can be used to mark the first user as admin).
# Those claims are merged with the `EXTRA_CLAIMS`.
FIRST_USER_CLAIMS='{}'
# If this is not empty, calls to `/jwt` without an `Authorization` header will still create a jwt (with `null` in `sub`)
GUEST_CLAIMS=""
# The url you can use to reach your kyoo instance. This is used during oidc to redirect users to your instance.
PUBLIC_URL=http://localhost:8901

View File

@ -10,6 +10,7 @@
- Username/password login
- OIDC (login via Google, Discord, Authentik, whatever)
- Custom jwt claims (for your role/permissions handling or something else)
- Guest handling (only if using `GUEST_CLAIMS`)
- Api keys support
- Optionally [Federated](#federated)

View File

@ -21,6 +21,7 @@ type Configuration struct {
PublicUrl string
DefaultClaims jwt.MapClaims
FirstUserClaims jwt.MapClaims
GuestClaims jwt.MapClaims
ExpirationDelay time.Duration
}
@ -55,6 +56,14 @@ func LoadConfiguration(db *dbc.Queries) (*Configuration, error) {
ret.FirstUserClaims = ret.DefaultClaims
}
claims = os.Getenv("GUEST_CLAIMS")
if claims != "" {
err := json.Unmarshal([]byte(claims), &ret.GuestClaims)
if err != nil {
return nil, err
}
}
rsa_pk_path := os.Getenv("RSA_PRIVATE_KEY_PATH")
if rsa_pk_path != "" {
privateKeyData, err := os.ReadFile(rsa_pk_path)

View File

@ -29,22 +29,52 @@ type Jwt struct {
// @Router /jwt [get]
func (h *Handler) CreateJwt(c echo.Context) error {
auth := c.Request().Header.Get("Authorization")
var jwt *string
if !strings.HasPrefix(auth, "Bearer ") {
return c.JSON(http.StatusOK, Jwt{Token: nil})
}
token := auth[len("Bearer "):]
jwt = h.createGuestJwt()
} else {
token := auth[len("Bearer "):]
jwt, err := h.createJwt(token)
if err != nil {
return err
tkn, err := h.createJwt(token)
if err != nil {
return err
}
jwt = &tkn
}
c.Response().Header().Add("Authorization", fmt.Sprintf("Bearer %s", jwt))
if jwt != nil {
c.Response().Header().Add("Authorization", fmt.Sprintf("Bearer %s", *jwt))
}
return c.JSON(http.StatusOK, Jwt{
Token: &jwt,
Token: jwt,
})
}
func (h *Handler) createGuestJwt() *string {
if h.config.GuestClaims == nil {
return nil
}
claims := maps.Clone(h.config.GuestClaims)
claims["username"] = "guest"
claims["sub"] = "guest"
claims["sid"] = "guest"
claims["iss"] = h.config.PublicUrl
claims["iat"] = &jwt.NumericDate{
Time: time.Now().UTC(),
}
claims["exp"] = &jwt.NumericDate{
Time: time.Now().UTC().Add(time.Hour),
}
jwt := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
t, err := jwt.SignedString(h.config.JwtPrivateKey)
if err != nil {
return nil
}
return &t
}
func (h *Handler) createJwt(token string) (string, error) {
session, err := h.db.GetUserFromToken(context.Background(), token)
if err != nil {

View File

@ -128,23 +128,28 @@ type Handler struct {
func (h *Handler) TokenToJwt(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
auth := c.Request().Header.Get("Authorization")
var jwt *string
if auth == "" || !strings.HasPrefix(auth, "Bearer ") {
return next(c)
}
token := auth[len("Bearer "):]
jwt = h.createGuestJwt()
} else {
token := auth[len("Bearer "):]
// this is only used to check if it is a session token or a jwt
_, err := base64.RawURLEncoding.DecodeString(token)
if err != nil {
return next(c)
}
// this is only used to check if it is a session token or a jwt
_, err := base64.RawURLEncoding.DecodeString(token)
if err != nil {
return next(c)
tkn, err := h.createJwt(token)
if err != nil {
return err
}
jwt = &tkn
}
jwt, err := h.createJwt(token)
if err != nil {
return err
if jwt != nil {
c.Request().Header.Set("Authorization", *jwt)
}
c.Request().Header.Set("Authorization", jwt)
return next(c)
}
}