mirror of
https://github.com/livekit/livekit.git
synced 2026-03-31 02:25:39 +00:00
147 lines
3.1 KiB
Go
147 lines
3.1 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/twitchtv/twirp"
|
|
|
|
"github.com/livekit/protocol/auth"
|
|
)
|
|
|
|
const (
|
|
authorizationHeader = "Authorization"
|
|
bearerPrefix = "Bearer "
|
|
grantsKey = "grants"
|
|
accessTokenParam = "access_token"
|
|
)
|
|
|
|
var (
|
|
ErrPermissionDenied = errors.New("permissions denied")
|
|
)
|
|
|
|
// authentication middleware
|
|
type APIKeyAuthMiddleware struct {
|
|
provider auth.KeyProvider
|
|
}
|
|
|
|
func NewAPIKeyAuthMiddleware(provider auth.KeyProvider) *APIKeyAuthMiddleware {
|
|
return &APIKeyAuthMiddleware{
|
|
provider: provider,
|
|
}
|
|
}
|
|
|
|
func (m *APIKeyAuthMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
|
authHeader := r.Header.Get(authorizationHeader)
|
|
var authToken string
|
|
|
|
if authHeader != "" {
|
|
if !strings.HasPrefix(authHeader, bearerPrefix) {
|
|
handleError(w, http.StatusUnauthorized, "invalid authorization header. Must start with "+bearerPrefix)
|
|
return
|
|
}
|
|
|
|
authToken = authHeader[len(bearerPrefix):]
|
|
} else {
|
|
// attempt to find from request header
|
|
authToken = r.FormValue(accessTokenParam)
|
|
}
|
|
|
|
if authToken != "" {
|
|
v, err := auth.ParseAPIToken(authToken)
|
|
if err != nil {
|
|
handleError(w, http.StatusUnauthorized, "invalid authorization token")
|
|
return
|
|
}
|
|
|
|
secret := m.provider.GetSecret(v.APIKey())
|
|
if secret == "" {
|
|
handleError(w, http.StatusUnauthorized, "invalid API key")
|
|
return
|
|
}
|
|
|
|
grants, err := v.Verify(secret)
|
|
if err != nil {
|
|
handleError(w, http.StatusUnauthorized, "invalid token: "+authToken+", error: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// set grants in context
|
|
ctx := r.Context()
|
|
r = r.WithContext(context.WithValue(ctx, grantsKey, grants))
|
|
}
|
|
|
|
next.ServeHTTP(w, r)
|
|
}
|
|
|
|
func GetGrants(ctx context.Context) *auth.ClaimGrants {
|
|
claims, ok := ctx.Value(grantsKey).(*auth.ClaimGrants)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return claims
|
|
}
|
|
|
|
func SetAuthorizationToken(r *http.Request, token string) {
|
|
r.Header.Set(authorizationHeader, bearerPrefix+token)
|
|
}
|
|
|
|
func EnsureJoinPermission(ctx context.Context) (name string, err error) {
|
|
claims := GetGrants(ctx)
|
|
if claims == nil || claims.Video == nil {
|
|
err = ErrPermissionDenied
|
|
return
|
|
}
|
|
|
|
if claims.Video.RoomJoin {
|
|
name = claims.Video.Room
|
|
} else {
|
|
err = ErrPermissionDenied
|
|
}
|
|
return
|
|
}
|
|
|
|
func EnsureAdminPermission(ctx context.Context, room string) error {
|
|
claims := GetGrants(ctx)
|
|
if claims == nil || claims.Video == nil {
|
|
return ErrPermissionDenied
|
|
}
|
|
|
|
if !claims.Video.RoomAdmin || room != claims.Video.Room {
|
|
return ErrPermissionDenied
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func EnsureCreatePermission(ctx context.Context) error {
|
|
claims := GetGrants(ctx)
|
|
if claims == nil {
|
|
return ErrPermissionDenied
|
|
}
|
|
|
|
if claims.Video.RoomCreate {
|
|
return nil
|
|
}
|
|
return ErrPermissionDenied
|
|
}
|
|
|
|
func EnsureListPermission(ctx context.Context) error {
|
|
claims := GetGrants(ctx)
|
|
if claims == nil {
|
|
return ErrPermissionDenied
|
|
}
|
|
|
|
if claims.Video.RoomList {
|
|
return nil
|
|
}
|
|
return ErrPermissionDenied
|
|
}
|
|
|
|
// wraps authentication errors around Twirp
|
|
func twirpAuthError(err error) error {
|
|
return twirp.NewError(twirp.Unauthenticated, err.Error())
|
|
}
|