mirror of
https://github.com/livekit/livekit.git
synced 2026-03-30 22:05:39 +00:00
When the instance handling the signal request did not respond to the initial connection, we will fail the connection attempt instead of having it hang forever.
168 lines
3.8 KiB
Go
168 lines
3.8 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/twitchtv/twirp"
|
|
|
|
"github.com/livekit/protocol/auth"
|
|
"github.com/livekit/protocol/livekit"
|
|
)
|
|
|
|
const (
|
|
authorizationHeader = "Authorization"
|
|
bearerPrefix = "Bearer "
|
|
accessTokenParam = "access_token"
|
|
)
|
|
|
|
type grantsKey struct{}
|
|
|
|
var (
|
|
ErrPermissionDenied = errors.New("permissions denied")
|
|
ErrMissingAuthorization = errors.New("invalid authorization header. Must start with " + bearerPrefix)
|
|
ErrInvalidAuthorizationToken = errors.New("invalid authorization token")
|
|
)
|
|
|
|
// 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) {
|
|
if r.URL != nil && r.URL.Path == "/rtc/validate" {
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
}
|
|
|
|
authHeader := r.Header.Get(authorizationHeader)
|
|
var authToken string
|
|
|
|
if authHeader != "" {
|
|
if !strings.HasPrefix(authHeader, bearerPrefix) {
|
|
handleError(w, http.StatusUnauthorized, ErrMissingAuthorization)
|
|
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, ErrInvalidAuthorizationToken)
|
|
return
|
|
}
|
|
|
|
secret := m.provider.GetSecret(v.APIKey())
|
|
if secret == "" {
|
|
handleError(w, http.StatusUnauthorized, errors.New("invalid API key: "+v.APIKey()))
|
|
return
|
|
}
|
|
|
|
grants, err := v.Verify(secret)
|
|
if err != nil {
|
|
handleError(w, http.StatusUnauthorized, errors.New("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 {
|
|
val := ctx.Value(grantsKey{})
|
|
claims, ok := val.(*auth.ClaimGrants)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return claims
|
|
}
|
|
|
|
func WithGrants(ctx context.Context, grants *auth.ClaimGrants) context.Context {
|
|
return context.WithValue(ctx, grantsKey{}, grants)
|
|
}
|
|
|
|
func SetAuthorizationToken(r *http.Request, token string) {
|
|
r.Header.Set(authorizationHeader, bearerPrefix+token)
|
|
}
|
|
|
|
func EnsureJoinPermission(ctx context.Context) (name livekit.RoomName, err error) {
|
|
claims := GetGrants(ctx)
|
|
if claims == nil || claims.Video == nil {
|
|
err = ErrPermissionDenied
|
|
return
|
|
}
|
|
|
|
if claims.Video.RoomJoin {
|
|
name = livekit.RoomName(claims.Video.Room)
|
|
} else {
|
|
err = ErrPermissionDenied
|
|
}
|
|
return
|
|
}
|
|
|
|
func EnsureAdminPermission(ctx context.Context, room livekit.RoomName) error {
|
|
claims := GetGrants(ctx)
|
|
if claims == nil || claims.Video == nil {
|
|
return ErrPermissionDenied
|
|
}
|
|
|
|
if !claims.Video.RoomAdmin || room != livekit.RoomName(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
|
|
}
|
|
|
|
func EnsureRecordPermission(ctx context.Context) error {
|
|
claims := GetGrants(ctx)
|
|
if claims == nil || !claims.Video.RoomRecord {
|
|
return ErrPermissionDenied
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// wraps authentication errors around Twirp
|
|
func twirpAuthError(err error) error {
|
|
return twirp.NewError(twirp.Unauthenticated, err.Error())
|
|
}
|