mirror of
https://github.com/suitenumerique/meet.git
synced 2026-08-17 22:17:51 +00:00
de73870a34
Some integrators render our videoconference inside an iframe, where our cookie-based authentication does not work: our cookies are SameSite=Lax/Strict, so the iframe drops them. We looked at what Jitsi offers: a shared secret used to sign JWTs that authenticate users coming from external services. Since we already expose an external API where third parties authenticate as a given user, it was simpler for us to add an exchange mechanism on top of that. Flow: * Through the external API, mint a short-lived, single-use exchange code for a user. * The third party hands that code to the frontend as a URL fragment. * The frontend exchanges the code for a longer-lived JWT that can be used to query the regular API viewsets. Known limitations and follow-ups: * At some point it would be nice to shorten the JWT lifetime and add a refresh mechanism. This will be handled in a follow-up PR when actually needed. * CSP rules to control which origins are allowed to embed the app in an iframe still need to be added. * This alternative authentication cannot easily be scoped to a subset of endpoints without adding a lot of complexity, so it is accepted globally on the API for now.
69 lines
2.8 KiB
Python
69 lines
2.8 KiB
Python
"""User access JWT authentication for the Meet core API.
|
|
|
|
Allows an embedded frontend (e.g. rendered in an iframe, where third-party
|
|
session cookies are blocked) to authenticate requests on the core API with
|
|
a JWT, obtained by exchanging a single-use transit code (see
|
|
core.services.transit_code and the users exchange-access-token endpoint)
|
|
and passed as a Bearer header. The JWT itself never appears in any URL.
|
|
|
|
Similar to lib-jitsi-meet's token authentication, the token is bound to a
|
|
user, not to a resource: once authenticated, the request is treated
|
|
exactly like a session-authenticated one, and the existing role-based
|
|
permissions apply unchanged.
|
|
"""
|
|
|
|
import logging
|
|
|
|
from django.conf import settings
|
|
|
|
from rest_framework import exceptions
|
|
|
|
from core.external_api.authentication import BaseJWTAuthentication
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
USER_ACCESS_TOKEN_TYPE_CLAIM = "user_access" # noqa: S105
|
|
|
|
|
|
class UserAccessJWTAuthentication(BaseJWTAuthentication):
|
|
"""JWT authentication for user access tokens.
|
|
|
|
Validates user access tokens issued by the users exchange-access-token
|
|
endpoint and authenticates the user they were issued for. A bearer
|
|
token that does not verify against the user access token secret is
|
|
deferred to the next authentication backend; a token that does verify
|
|
but carries wrong claims is rejected.
|
|
|
|
When the feature is disabled (USER_ACCESS_TOKEN_ENABLED=False), the
|
|
backend is entirely inert: `BaseJWTAuthentication.authenticate`
|
|
returns None before reading the Authorization header, deferring every
|
|
request to the next authentication backend.
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""Initialize the backend with user access token settings."""
|
|
super().__init__(
|
|
secret_key=settings.USER_ACCESS_TOKEN_SECRET_KEY,
|
|
algorithm=settings.USER_ACCESS_TOKEN_ALG,
|
|
issuer=settings.USER_ACCESS_TOKEN_ISSUER,
|
|
audience=settings.USER_ACCESS_TOKEN_AUDIENCE,
|
|
expiration_seconds=settings.USER_ACCESS_TOKEN_TTL,
|
|
token_type=settings.USER_ACCESS_TOKEN_TYPE,
|
|
is_enabled=settings.USER_ACCESS_TOKEN_ENABLED,
|
|
)
|
|
|
|
def validate_payload(self, payload):
|
|
"""Validate the token type and the issuance-audit claim.
|
|
|
|
Raises:
|
|
AuthenticationFailed: If the token verified against the user
|
|
access token secret but does not carry the expected claims.
|
|
"""
|
|
if payload.get("token_type") != USER_ACCESS_TOKEN_TYPE_CLAIM:
|
|
logger.warning("Wrong 'token_type' in user access token payload")
|
|
raise exceptions.AuthenticationFailed("Invalid token type.")
|
|
|
|
if not payload.get("client_id"):
|
|
logger.warning("Missing 'client_id' in user access token payload")
|
|
raise exceptions.AuthenticationFailed("Invalid token claims.")
|