Compare commits

...

25 Commits

Author SHA1 Message Date
Michel-Marie MAUDET 724a4ef3df 🔒️(backend) secure native app OIDC login with exchange codes
Add one-time exchange code mechanism for native app OIDC login.
Instead of exposing session ID in redirect URL, generates a
short-lived single-use code stored in Redis. Native apps exchange
this code for the session ID via a dedicated API endpoint.

Includes NativeAppRedirect for custom URL schemes, rate limiting,
logging, and whitelist of allowed schemes.

Closes #1153

Co-Authored-By: gigi206
2026-03-20 18:35:49 +01:00
lebaudantoine 4f2c4bfaf9 ️(summary) improve accessibility of transcription download link
Update the link label to use explicit text "Download your recording"
instead of generic "following this link."

This ensures blind users understand the purpose of the link
and the behavior of opening a new window.
2026-03-20 10:17:48 +01:00
lebaudantoine dacf705329 🍱(frontend) update logo to the latest version
Replace the previous logo with the most up-to-date brand
assets to ensure consistency across the application and emails.
2026-03-20 09:54:12 +01:00
lebaudantoine 8296738347 ️(backend) improve logo access in screen recording email notification
Update the logo alternative text to include the brand name instead
of a generic "logo image" description.

Use a more descriptive value such as "Logo LaSuite Meet"
to better convey the content to screen reader users.

It closes #1092
2026-03-20 09:54:08 +01:00
Florent Chehab 04be495351 💄(custom-background) add upload indicator with preview
When uploading an image, depending on the available network
there might be a bit of wait while the image is being uploaded.
In this commit we add a preview (grayscale + spinner) to have
UI feedback that the upload is in progress.
2026-03-19 17:27:51 +01:00
Florent Chehab 43185605eb 💄(spinner) enforce spinner height
For some reason the ProgressBar adds a bit of height to the spinner
which makes it hard to center.
2026-03-19 17:27:21 +01:00
Florent Chehab cf3fb208e2 🐛(frontend) auto-select new custom background when not logged in
When not logged in and selecting a new custom personal background
the constant id was causing the new custom background not to be auto
selected.
2026-03-19 17:27:21 +01:00
Florent Chehab 4ca230eb12 🐛(frontend) disable personal custom background while deleting
Prevent users selecting a personal custom background while deleting one.
2026-03-19 17:27:20 +01:00
lebaudantoine 4b5e0cb2a3 ️(frontend) improve button descriptions for More tools actions #1184
The "Transcribe" and "Record" buttons had unclear and misleading
descriptions, both using the verb "record," which caused confusion,
especially for screen reader users.

Update descriptions to clearly reflect each action:
- Transcribe: generate a written transcript of the conversation
- Record: save the meeting as a video

This improves accessibility (RGAA 11.9) and reduces the risk of
users triggering the wrong action.

Closes #1173
2026-03-19 14:48:02 +01:00
lebaudantoine 45f374610f ️(frontend) fix more tools heading hierarchy
Side panel title is an H1, but the hierarchy skips directly to H3.
Fix the heading structure. It closes #1178.
2026-03-19 11:56:16 +01:00
lebaudantoine b419a2bfd2 ️(frontend) fix sidepanel accessibility aria-label
The aria-label only announced the presence of a sidepanel without
including its title.

Append the sidepanel title to improve accessibility and context
for screen readers.

Closes #1176.
2026-03-19 11:21:40 +01:00
lebaudantoine ee8d96bee7 🔖(minor) bump release to 1.11.0 2026-03-19 00:31:32 +01:00
lebaudantoine c65ff2d75d 🩹(frontend) disable subtitle settings when feature is unavailable
Hide or disable settings related to the subtitle feature when
the feature flag is not enabled.
2026-03-18 23:25:20 +01:00
Cyril ea1c90d8ca (feat) add default color option for captions
Add "Default" option (white text on black background) for font and background.
2026-03-18 20:44:25 +01:00
Cyril 3e963e3e6d ♻️(refactor) apply caption color customization to subtitles
Use captionFontColor and captionBackgroundColor in Transcription component.
2026-03-18 20:44:25 +01:00
Cyril 82769128a1 (feat) add caption font color and background settings
Select font color and background color in Accessibility > Captions.
2026-03-18 20:44:25 +01:00
Cyril 05f4ce6b2e 🌐(i18n) add captions color settings translations
EN, FR, DE, NL for Accessibility > Captions > Font color / Background color.
2026-03-18 20:44:25 +01:00
Cyril 508984ecfa ️(frontend) add caption font color and background to access store
Persist captionFontColor and captionBackgroundColor in user preferences.
2026-03-18 20:44:25 +01:00
lebaudantoine a05507f73d 👷(ci) run summary test suite
Configure the CI to run the summary tests' suite.
2026-03-18 20:19:52 +01:00
lebaudantoine a27def119c (summary) add tests for the task endpoint
Ensure the API correctly validates and accepts the parameters
required to register a task in the queue.
2026-03-18 20:19:52 +01:00
lebaudantoine fce94f38ce 🧑‍💻(summary) add make command to run summary test suite
Introduce a dedicated make target to execute tests for the
summary component of the stack.
2026-03-18 20:19:52 +01:00
lebaudantoine 83bd9c5ce3 (summary) add minimal test suite for health endpoint
Based on cameldev's work, introduce basic tests to ensure the
health endpoint responds correctly.
2026-03-18 20:19:52 +01:00
Florent Chehab bfbfade99a 🐛(frontend) prevent black background image
Prevent showing a black background when the image is not
accessible anymore.

This happens after the user logs out or logs in.
Or the auth is revoked.
2026-03-18 18:49:59 +01:00
Florent Chehab 16daf7b8d3 (frontend) add custom virtual background feature
Add a custom virtual background feature.

If the backend supports uploading files, backgrounds are stored
in the backend for the user.
Otherwise, only one background image can be selected.
2026-03-18 18:49:59 +01:00
lebaudantoine fcad79d662 📌(agents) unpin OpenSSL and related dependencies
The base image now includes OpenSSL 3.5.5, which resolves
CVE-2025-15467.

Remove explicit pinning of OpenSSL and its dependencies.
2026-03-18 11:34:44 +01:00
12 changed files with 375 additions and 47 deletions
+33 -33
View File
@@ -48,12 +48,12 @@ jobs:
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
-
name: Run trivy scan
uses: numerique-gouv/action-trivy-cache@main
with:
docker-build-args: '--target backend-production -f Dockerfile'
docker-image-name: '${{ env.DOCKER_CONTAINER_REGISTRY_HOSTNAME }}/${{ env.DOCKER_CONTAINER_REGISTRY_NAMESPACE }}/meet-backend:${{ github.sha }}'
# -
# name: Run trivy scan
# uses: numerique-gouv/action-trivy-cache@main
# with:
# docker-build-args: '--target backend-production -f Dockerfile'
# docker-image-name: '${{ env.DOCKER_CONTAINER_REGISTRY_HOSTNAME }}/${{ env.DOCKER_CONTAINER_REGISTRY_NAMESPACE }}/meet-backend:${{ github.sha }}'
-
name: Build and push
uses: docker/build-push-action@v6
@@ -93,12 +93,12 @@ jobs:
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
-
name: Run trivy scan
uses: numerique-gouv/action-trivy-cache@main
with:
docker-build-args: '-f src/frontend/Dockerfile --target frontend-production'
docker-image-name: '${{ env.DOCKER_CONTAINER_REGISTRY_HOSTNAME }}/${{ env.DOCKER_CONTAINER_REGISTRY_NAMESPACE }}/meet-frontend:${{ github.sha }}'
# -
# name: Run trivy scan
# uses: numerique-gouv/action-trivy-cache@main
# with:
# docker-build-args: '-f src/frontend/Dockerfile --target frontend-production'
# docker-image-name: '${{ env.DOCKER_CONTAINER_REGISTRY_HOSTNAME }}/${{ env.DOCKER_CONTAINER_REGISTRY_NAMESPACE }}/meet-frontend:${{ github.sha }}'
-
name: Build and push
uses: docker/build-push-action@v6
@@ -139,12 +139,12 @@ jobs:
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
-
name: Run trivy scan
uses: numerique-gouv/action-trivy-cache@main
with:
docker-build-args: '-f docker/dinum-frontend/Dockerfile --target frontend-production'
docker-image-name: '${{ env.DOCKER_CONTAINER_REGISTRY_HOSTNAME }}/${{ env.DOCKER_CONTAINER_REGISTRY_NAMESPACE }}/meet-frontend-dinum:${{ github.sha }}'
# -
# name: Run trivy scan
# uses: numerique-gouv/action-trivy-cache@main
# with:
# docker-build-args: '-f docker/dinum-frontend/Dockerfile --target frontend-production'
# docker-image-name: '${{ env.DOCKER_CONTAINER_REGISTRY_HOSTNAME }}/${{ env.DOCKER_CONTAINER_REGISTRY_NAMESPACE }}/meet-frontend-dinum:${{ github.sha }}'
-
name: Build and push
uses: docker/build-push-action@v6
@@ -185,13 +185,13 @@ jobs:
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
-
name: Run trivy scan
uses: numerique-gouv/action-trivy-cache@main
continue-on-error: true
with:
docker-build-args: '-f src/summary/Dockerfile --target production'
docker-image-name: '${{ env.DOCKER_CONTAINER_REGISTRY_HOSTNAME }}/${{ env.DOCKER_CONTAINER_REGISTRY_NAMESPACE }}/meet-summary:${{ github.sha }}'
# -
# name: Run trivy scan
# uses: numerique-gouv/action-trivy-cache@main
# continue-on-error: true
# with:
# docker-build-args: '-f src/summary/Dockerfile --target production'
# docker-image-name: '${{ env.DOCKER_CONTAINER_REGISTRY_HOSTNAME }}/${{ env.DOCKER_CONTAINER_REGISTRY_NAMESPACE }}/meet-summary:${{ github.sha }}'
docker-context: './src/summary'
-
name: Build and push
@@ -233,14 +233,14 @@ jobs:
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
-
name: Run trivy scan
uses: numerique-gouv/action-trivy-cache@main
continue-on-error: true
with:
docker-build-args: '-f src/agents/Dockerfile --target production'
docker-image-name: '${{ env.DOCKER_CONTAINER_REGISTRY_HOSTNAME }}/${{ env.DOCKER_CONTAINER_REGISTRY_NAMESPACE }}/meet-agents:${{ github.sha }}'
docker-context: './src/agents'
# -
# name: Run trivy scan
# uses: numerique-gouv/action-trivy-cache@main
# continue-on-error: true
# with:
# docker-build-args: '-f src/agents/Dockerfile --target production'
# docker-image-name: '${{ env.DOCKER_CONTAINER_REGISTRY_HOSTNAME }}/${{ env.DOCKER_CONTAINER_REGISTRY_NAMESPACE }}/meet-agents:${{ github.sha }}'
# docker-context: './src/agents'
-
name: Build and push
uses: docker/build-push-action@v6
-3
View File
@@ -29,7 +29,6 @@ and this project adheres to
- 🔐(backend) avoids revealing the inactive status of an application #1135
- ⚡️(helm) reduce initialDelaySeconds and add periods seconds #1139
- 🔒️(backend) avoid information exposure through exception messages #1144
- ⬆️(dependencies) update PyJWT to v2.12.0 [SECURITY] #1151
### Fixed
@@ -37,8 +36,6 @@ and this project adheres to
- 🩹(backend) add page_size to pagination for room endpoints #1131
- 🐛(backend) refactor lobby throttling to use participant id #1129
- 🩹(backend) ignore non-recording uploads in storage webhook handler #1142
- 🐛(frontend) fix dimension mismatch in BackgroundCustomProcessor #1116
## [1.10.0] - 2026-03-05
+4 -3
View File
@@ -61,10 +61,11 @@ services:
`docker compose up -d`
```
Your keycloak instance is now available on https://id.yourdomain.tld
Your keycloak instance is now available on https://doc.yourdomain.tld
> [!CAUTION]
> Version of the images are set to latest, you should pin it to the desired version to avoid unwanted upgrades when pulling latest image. You can find available versions on [Keycloak registry](https://quay.io/repository/keycloak/keycloak?tab=tags).
```
## Creating an OIDC Client for Meet Application
@@ -75,7 +76,7 @@ Your keycloak instance is now available on https://id.yourdomain.tld
3. Enter the name of the realm - `meet`.
4. Click "Create".
### Step 2: Create a New Client
#### Step 2: Create a New Client
1. Navigate to the "Clients" tab.
2. Click on the "Create client" button.
@@ -85,7 +86,7 @@ Your keycloak instance is now available on https://id.yourdomain.tld
1. Set the "Web Origins" to the URL of your meet application - e.g. `https://meet.example.com`.
1. Click "Save".
### Step 3: Get Client Credentials
#### Step 3: Get Client Credentials
1. Go to the "Credentials" tab.
2. Copy the client ID (`meet` in this example) and the client secret.
+6
View File
@@ -73,3 +73,9 @@ class CreationCallbackAnonRateThrottle(MonitoredAnonRateThrottle):
"""Throttle Anonymous user requesting room generation callback"""
scope = "creation_callback"
class SessionExchangeAnonRateThrottle(MonitoredAnonRateThrottle):
"""Throttle anonymous requests to the session exchange endpoint."""
scope = "session_exchange"
+61
View File
@@ -0,0 +1,61 @@
"""API endpoint for exchanging a one-time code for a session ID."""
import logging
from django.conf import settings
from django.core.cache import cache
from rest_framework import serializers, status
from rest_framework.decorators import api_view, permission_classes, throttle_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from core.api.throttling import SessionExchangeAnonRateThrottle
from .views import EXCHANGE_CODE_PREFIX
logger = logging.getLogger(__name__)
class SessionExchangeSerializer(serializers.Serializer):
"""Validates the exchange code request."""
code = serializers.CharField(max_length=64, min_length=16)
@api_view(["POST"])
@permission_classes([AllowAny])
@throttle_classes([SessionExchangeAnonRateThrottle])
def session_exchange(
request,
): # NOSONAR (S3752) POST-only, AllowAny is intentional: single-use code with 30s TTL and rate limiting
"""Exchange a one-time code for a session ID.
The code was generated during the OIDC callback and stored in cache
with a short TTL. This endpoint retrieves the session ID, deletes the
code (single-use), and returns the session ID to the native app.
"""
serializer = SessionExchangeSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
code = serializer.validated_data["code"]
cache_key = f"{EXCHANGE_CODE_PREFIX}{code}"
session_key = cache.get(cache_key)
if session_key is None:
logger.warning(
"Session exchange failed: invalid or expired code from %s",
request.META.get("REMOTE_ADDR"),
)
return Response(
{"detail": "Invalid or expired code."},
status=status.HTTP_400_BAD_REQUEST,
)
# Delete immediately — single use
cache.delete(cache_key)
cookie_name = getattr(settings, "SESSION_COOKIE_NAME", "sessionid")
logger.info("Session exchange successful from %s", request.META.get("REMOTE_ADDR"))
return Response({cookie_name: session_key})
+103
View File
@@ -0,0 +1,103 @@
"""Custom OIDC authentication views for native app support.
When a native app (iOS, Android, Desktop) initiates OIDC login, it sets
`returnTo` to a custom URL scheme (e.g. `visio://auth-callback`). After
the OIDC flow completes, Django sets the session cookie and redirects to
that URL. However, native apps cannot read browser cookies — they need
the session ID passed explicitly.
Instead of exposing the session ID directly in the redirect URL, this
module generates a short-lived, single-use exchange code. The native app
then exchanges this code for the session ID via a dedicated API endpoint.
"""
import uuid
from urllib.parse import urlencode, urlparse
from django.conf import settings
from django.core.cache import cache
from django.http import HttpResponseRedirect
from lasuite.oidc_login.views import (
OIDCAuthenticationCallbackView as BaseCallbackView,
OIDCAuthenticationRequestView as BaseRequestView,
)
# Cache key prefix and TTL for exchange codes
EXCHANGE_CODE_PREFIX = "auth_exchange:"
EXCHANGE_CODE_TTL = 30 # seconds
class NativeAppRedirect(HttpResponseRedirect):
"""HttpResponseRedirect subclass that allows native app custom URL schemes.
Django's HttpResponseRedirect only allows http, https, and ftp schemes.
Native apps use custom schemes (e.g. visio://) for deep links, which
Django rejects with DisallowedRedirect. This subclass extends
allowed_schemes with the configured native app schemes.
"""
allowed_schemes = HttpResponseRedirect.allowed_schemes + list(
getattr(settings, "NATIVE_APP_REDIRECT_SCHEMES", [])
)
class OIDCAuthenticationRequestView(BaseRequestView):
"""Custom authenticate view that preserves native app returnTo in session.
mozilla-django-oidc's get_next_url() rejects custom URL schemes
(e.g. visio://auth-callback) because url_has_allowed_host_and_scheme()
only allows http/https. We intercept the returnTo parameter and store
it directly in the session for whitelisted schemes, bypassing the
safety check (which is not relevant for native app deep links).
"""
def get(self, request):
redirect_field = getattr(settings, "OIDC_REDIRECT_FIELD_NAME", "returnTo")
return_to = request.GET.get(redirect_field, "")
parsed = urlparse(return_to)
allowed_schemes = getattr(settings, "NATIVE_APP_REDIRECT_SCHEMES", [])
response = super().get(request)
# Override oidc_login_next AFTER super() which set it to None
# via get_next_url() rejecting the custom scheme.
if parsed.scheme in allowed_schemes:
request.session["oidc_login_next"] = return_to
request.session.save()
return response
class OIDCAuthenticationCallbackView(BaseCallbackView):
"""Callback view that generates an exchange code for native app deep links."""
def login_success(self):
"""After successful login, append exchange code for whitelisted scheme redirects."""
# Temporarily remove native redirect from session to prevent
# super().login_success() from raising DisallowedRedirect when
# it tries HttpResponseRedirect with a custom scheme.
native_redirect = self.request.session.pop("oidc_login_next", None)
allowed_schemes = getattr(settings, "NATIVE_APP_REDIRECT_SCHEMES", [])
parsed = urlparse(native_redirect or "")
if native_redirect and parsed.scheme in allowed_schemes:
# Let super() redirect to the default URL (homepage)
super().login_success()
# Generate a short-lived, single-use exchange code
exchange_code = uuid.uuid4().hex
session_key = self.request.session.session_key
cache.set(
f"{EXCHANGE_CODE_PREFIX}{exchange_code}",
session_key,
EXCHANGE_CODE_TTL,
)
separator = "&" if parsed.query else "?"
new_url = (
f"{native_redirect}{separator}{urlencode({'code': exchange_code})}"
)
return NativeAppRedirect(new_url)
return super().login_success()
@@ -0,0 +1,143 @@
"""
Tests for the session exchange API endpoint and OIDC callback view.
"""
import uuid
from django.core.cache import cache
import pytest
from rest_framework.test import APIClient
from core.authentication.views import EXCHANGE_CODE_PREFIX, EXCHANGE_CODE_TTL
pytestmark = pytest.mark.django_db
@pytest.fixture(autouse=True)
def _clear_throttle_cache():
"""Clear cache before each test to reset throttle counters."""
cache.clear()
def test_session_exchange_valid_code():
"""A valid exchange code should return the session ID and be consumed."""
code = uuid.uuid4().hex
cache_key = f"{EXCHANGE_CODE_PREFIX}{code}"
cache.set(cache_key, "test-session-key-123", EXCHANGE_CODE_TTL)
client = APIClient()
response = client.post(
"/api/v1.0/auth/session-exchange/",
{"code": code},
format="json",
)
assert response.status_code == 200
data = response.json()
assert "test-session-key-123" in data.values()
# Code should be consumed (single-use)
assert cache.get(cache_key) is None
def test_session_exchange_invalid_code():
"""An invalid/unknown code should return 400."""
client = APIClient()
response = client.post(
"/api/v1.0/auth/session-exchange/",
{"code": uuid.uuid4().hex},
format="json",
)
assert response.status_code == 400
assert response.json()["detail"] == "Invalid or expired code."
def test_session_exchange_expired_code():
"""An expired code should return 400."""
code = uuid.uuid4().hex
cache_key = f"{EXCHANGE_CODE_PREFIX}{code}"
# Set with 0 TTL to simulate expiration
cache.set(cache_key, "expired-session", 0)
client = APIClient()
response = client.post(
"/api/v1.0/auth/session-exchange/",
{"code": code},
format="json",
)
assert response.status_code == 400
def test_session_exchange_code_too_short():
"""A code that's too short should be rejected by validation."""
client = APIClient()
response = client.post(
"/api/v1.0/auth/session-exchange/",
{"code": "short"},
format="json",
)
assert response.status_code == 400
def test_session_exchange_missing_code():
"""Missing code field should be rejected."""
client = APIClient()
response = client.post(
"/api/v1.0/auth/session-exchange/",
{},
format="json",
)
assert response.status_code == 400
def test_session_exchange_replay_attack():
"""Using the same code twice should fail the second time."""
code = uuid.uuid4().hex
cache.set(f"{EXCHANGE_CODE_PREFIX}{code}", "session-123", EXCHANGE_CODE_TTL)
client = APIClient()
# First use succeeds
response = client.post(
"/api/v1.0/auth/session-exchange/",
{"code": code},
format="json",
)
assert response.status_code == 200
# Second use fails
response = client.post(
"/api/v1.0/auth/session-exchange/",
{"code": code},
format="json",
)
assert response.status_code == 400
def test_session_exchange_returns_correct_cookie_name(settings):
"""The response key should match SESSION_COOKIE_NAME from settings."""
settings.SESSION_COOKIE_NAME = "meet_sessionid"
code = uuid.uuid4().hex
cache.set(f"{EXCHANGE_CODE_PREFIX}{code}", "my-session", EXCHANGE_CODE_TTL)
client = APIClient()
response = client.post(
"/api/v1.0/auth/session-exchange/",
{"code": code},
format="json",
)
assert response.status_code == 200
assert response.json() == {"meet_sessionid": "my-session"}
def test_session_exchange_get_not_allowed():
"""GET method should not be allowed on the exchange endpoint."""
client = APIClient()
response = client.get("/api/v1.0/auth/session-exchange/")
assert response.status_code == 405
+4
View File
@@ -7,6 +7,7 @@ from lasuite.oidc_login.urls import urlpatterns as oidc_urls
from rest_framework.routers import DefaultRouter
from core.api import get_frontend_configuration, viewsets
from core.authentication.api import session_exchange
from core.external_api import viewsets as external_viewsets
# - Main endpoints
@@ -40,6 +41,9 @@ urlpatterns = [
[
*router.urls,
*oidc_urls,
path(
"auth/session-exchange/", session_exchange, name="session_exchange"
),
path("config/", get_frontend_configuration, name="config"),
]
),
+15 -2
View File
@@ -344,6 +344,11 @@ class Base(Configuration):
environ_name="CREATION_CALLBACK_THROTTLE_RATES",
environ_prefix=None,
),
"session_exchange": values.Value(
default="5/minute",
environ_name="SESSION_EXCHANGE_THROTTLE_RATES",
environ_prefix=None,
),
},
}
MONITORED_THROTTLE_FAILURE_CALLBACK = (
@@ -459,8 +464,16 @@ class Base(Configuration):
)
# OIDC - Authorization Code Flow
OIDC_AUTHENTICATE_CLASS = "lasuite.oidc_login.views.OIDCAuthenticationRequestView"
OIDC_CALLBACK_CLASS = "lasuite.oidc_login.views.OIDCAuthenticationCallbackView"
OIDC_AUTHENTICATE_CLASS = "core.authentication.views.OIDCAuthenticationRequestView"
OIDC_CALLBACK_CLASS = "core.authentication.views.OIDCAuthenticationCallbackView"
# Custom URL schemes allowed for native app OIDC redirects.
# Only these schemes will receive an exchange code in the callback.
NATIVE_APP_REDIRECT_SCHEMES = values.ListValue(
default=["visio"],
environ_name="NATIVE_APP_REDIRECT_SCHEMES",
environ_prefix=None,
)
OIDC_CREATE_USER = values.BooleanValue(
default=True, environ_name="OIDC_CREATE_USER", environ_prefix=None
)
+1 -1
View File
@@ -52,7 +52,7 @@ dependencies = [
"nested-multipart-parser==1.6.0",
"psycopg[binary]==3.3.2",
"pydantic==2.12.4",
"PyJWT==2.12.0",
"PyJWT==2.11.0",
"python-frontmatter==1.1.0",
"python-magic==0.4.27",
"requests==2.32.5",
+4 -4
View File
@@ -1251,7 +1251,7 @@ requires-dist = [
{ name = "nested-multipart-parser", specifier = "==1.6.0" },
{ name = "psycopg", extras = ["binary"], specifier = "==3.3.2" },
{ name = "pydantic", specifier = "==2.12.4" },
{ name = "pyjwt", specifier = "==2.12.0" },
{ name = "pyjwt", specifier = "==2.11.0" },
{ name = "python-frontmatter", specifier = "==1.1.0" },
{ name = "python-magic", specifier = "==0.4.27" },
{ name = "redis", specifier = "==5.2.1" },
@@ -1754,11 +1754,11 @@ wheels = [
[[package]]
name = "pyjwt"
version = "2.12.0"
version = "2.11.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a8/10/e8192be5f38f3e8e7e046716de4cae33d56fd5ae08927a823bb916be36c1/pyjwt-2.12.0.tar.gz", hash = "sha256:2f62390b667cd8257de560b850bb5a883102a388829274147f1d724453f8fb02", size = 102511, upload-time = "2026-03-12T17:15:30.831Z" }
sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/15/70/70f895f404d363d291dcf62c12c85fdd47619ad9674ac0f53364d035925a/pyjwt-2.12.0-py3-none-any.whl", hash = "sha256:9bb459d1bdd0387967d287f5656bf7ec2b9a26645d1961628cda1764e087fd6e", size = 29700, upload-time = "2026-03-12T17:15:29.257Z" },
{ url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" },
]
[[package]]
@@ -187,7 +187,7 @@ export class BackgroundCustomProcessor implements BackgroundProcessorInterface {
0,
0,
PROCESSING_WIDTH,
PROCESSING_HEIGHT
PROCESSING_WIDTH
)
}