mirror of
https://github.com/suitenumerique/meet.git
synced 2026-08-29 19:57:14 +00:00
wip rely on cookie for the session service
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
/* global Office */
|
/* global Office */
|
||||||
|
|
||||||
const BASE_URL = "https://meet.127.0.0.1.nip.io";
|
const BASE_URL = "https://meet.127.0.0.1.nip.io"; // todo - use env variable
|
||||||
|
|
||||||
// ─── Session Storage ──────────────────────────────────────────────────────
|
// ─── Session Storage ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,72 @@ function setStatus(msg) {
|
|||||||
document.getElementById("status").textContent = msg;
|
document.getElementById("status").textContent = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Polling ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function startPolling(session_id, { onSuccess, onTimeout, onError }) {
|
||||||
|
let pollCount = 0;
|
||||||
|
const pollInterval = setInterval(() => {
|
||||||
|
// ─── Timeout after 3 minutes ──────────────────────────────
|
||||||
|
if (pollCount++ > 180) {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
onTimeout?.();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetch(`${BASE_URL}/api/v1.0/addons/sessions/wip/`, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ session_id }),
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((sessionData) => {
|
||||||
|
console.log("Polling:", sessionData);
|
||||||
|
if (sessionData.state === "authenticated" && sessionData.access_token) {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
onSuccess?.(sessionData);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
onError?.(err);
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return pollInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Transit Dialog ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function openTransitDialog(session_id, { onCancel, onError }) {
|
||||||
|
const meetUrl = `${BASE_URL}/addons/transit/?session_id=${session_id}`;
|
||||||
|
|
||||||
|
Office.context.ui.displayDialogAsync(
|
||||||
|
meetUrl,
|
||||||
|
{ height: 60, width: 50, displayInIframe: false },
|
||||||
|
(asyncResult) => {
|
||||||
|
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
|
||||||
|
onError?.(asyncResult.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialog = asyncResult.value;
|
||||||
|
|
||||||
|
dialog.addEventHandler(Office.EventType.DialogMessageReceived, () => {
|
||||||
|
onCancel?.();
|
||||||
|
dialog.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.addEventHandler(Office.EventType.DialogEventReceived, (arg) => {
|
||||||
|
if (arg.error === 12006) {
|
||||||
|
setStatus("Dialog fermé. En attente d'authentification...");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Auth Flow ────────────────────────────────────────────────────────────
|
// ─── Auth Flow ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function connect() {
|
function connect() {
|
||||||
@@ -28,68 +94,28 @@ function connect() {
|
|||||||
const session_id = data.session_id;
|
const session_id = data.session_id;
|
||||||
setStatus("En attente d'authentification...");
|
setStatus("En attente d'authentification...");
|
||||||
|
|
||||||
let dialog = null;
|
const pollInterval = startPolling(session_id, {
|
||||||
let pollInterval = null;
|
onSuccess: (sessionData) => {
|
||||||
let pollCount = 0;
|
saveSession(sessionData);
|
||||||
|
setStatus("Connecté !");
|
||||||
pollInterval = setInterval(() => {
|
showView("auth");
|
||||||
|
},
|
||||||
// ─── Timeout after 3 minutes ──────────────────────────────
|
onTimeout: () => {
|
||||||
if (pollCount++ > 180) {
|
|
||||||
clearInterval(pollInterval);
|
|
||||||
if (dialog) dialog.close();
|
|
||||||
setStatus("Délai d'authentification dépassé. Veuillez réessayer.");
|
setStatus("Délai d'authentification dépassé. Veuillez réessayer.");
|
||||||
showView("unauth");
|
showView("unauth");
|
||||||
return;
|
},
|
||||||
}
|
onError: (err) => {
|
||||||
|
setStatus(`Erreur de polling: ${err.message}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
fetch(`${BASE_URL}/api/v1.0/addons/sessions/${session_id}`, {
|
openTransitDialog(session_id, {
|
||||||
method: "GET",
|
onCancel: () => clearInterval(pollInterval),
|
||||||
headers: { "Content-Type": "application/json" },
|
onError: (err) => {
|
||||||
})
|
clearInterval(pollInterval);
|
||||||
.then((res) => res.json())
|
setStatus(`Erreur dialog: ${err.message}`);
|
||||||
.then((sessionData) => {
|
},
|
||||||
console.log("Polling:", sessionData);
|
});
|
||||||
if (sessionData.state === "authenticated" && sessionData.access_token) {
|
|
||||||
clearInterval(pollInterval);
|
|
||||||
// if (dialog) dialog.close();
|
|
||||||
saveSession(sessionData);
|
|
||||||
setStatus("Connecté !");
|
|
||||||
showView("auth");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
clearInterval(pollInterval);
|
|
||||||
setStatus(`Erreur de polling: ${err.message}`);
|
|
||||||
});
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
// ─── Open transit dialog ──────────────────────────────────────
|
|
||||||
const meetUrl = `${BASE_URL}/addons/transit/?session_id=${session_id}`;
|
|
||||||
Office.context.ui.displayDialogAsync(
|
|
||||||
meetUrl,
|
|
||||||
{ height: 60, width: 50, displayInIframe: false },
|
|
||||||
(asyncResult) => {
|
|
||||||
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
|
|
||||||
clearInterval(pollInterval);
|
|
||||||
setStatus(`Erreur dialog: ${asyncResult.error.message}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog = asyncResult.value;
|
|
||||||
|
|
||||||
dialog.addEventHandler(Office.EventType.DialogMessageReceived, () => {
|
|
||||||
clearInterval(pollInterval);
|
|
||||||
dialog.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog.addEventHandler(Office.EventType.DialogEventReceived, (arg) => {
|
|
||||||
if (arg.error === 12006) {
|
|
||||||
setStatus("Dialog fermé. En attente d'authentification...");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setStatus(`Erreur de connexion: ${err.message}`);
|
setStatus(`Erreur de connexion: ${err.message}`);
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ from core.services.jwt_token import JwtTokenService
|
|||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SessionOwnershipError(Exception):
|
||||||
|
"""Raised when the claimed session_id does not match the result_token binding."""
|
||||||
|
|
||||||
class SessionState(str, Enum):
|
class SessionState(str, Enum):
|
||||||
"""Add-on authentication session states."""
|
"""Add-on authentication session states."""
|
||||||
|
|
||||||
@@ -37,14 +40,20 @@ class TokenExchangeService:
|
|||||||
token_type=settings.ADDONS_JWT_TOKEN_TYPE,
|
token_type=settings.ADDONS_JWT_TOKEN_TYPE,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_cache_key(self, session_id: str) -> str:
|
def _session_cache_key(self, session_id: str) -> str:
|
||||||
"""Generate cache key for a session ID."""
|
"""Generate cache key for a session ID."""
|
||||||
return f"{settings.ADDONS_SESSION_KEY_PREFIX}_{session_id}"
|
return f"{settings.ADDONS_SESSION_KEY_PREFIX}_{session_id}"
|
||||||
|
|
||||||
def init_session(self) -> str:
|
def _token_cache_key(self, result_token: str) -> str:
|
||||||
|
"""Wip."""
|
||||||
|
return f"{settings.ADDONS_SESSION_TOKEN_PREFIX}_{result_token}"
|
||||||
|
|
||||||
|
def init_session(self) -> tuple[str, str]:
|
||||||
"""Create a new pending authentication session and return its ID."""
|
"""Create a new pending authentication session and return its ID."""
|
||||||
|
|
||||||
session_id = secrets.token_urlsafe(settings.ADDONS_SESSION_ID_LENGTH)
|
session_id = secrets.token_urlsafe(settings.ADDONS_SESSION_ID_LENGTH)
|
||||||
|
result_token = secrets.token_urlsafe(32) # separate, never in any UR
|
||||||
|
|
||||||
expires_at = datetime.now(timezone.utc) + timedelta(
|
expires_at = datetime.now(timezone.utc) + timedelta(
|
||||||
seconds=settings.ADDONS_SESSION_TIMEOUT
|
seconds=settings.ADDONS_SESSION_TIMEOUT
|
||||||
)
|
)
|
||||||
@@ -54,41 +63,80 @@ class TokenExchangeService:
|
|||||||
"expires_at": expires_at.isoformat(),
|
"expires_at": expires_at.isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
cache_key = self._get_cache_key(session_id)
|
# Store the session itself
|
||||||
cache.set(
|
cache.set(
|
||||||
cache_key,
|
self._session_cache_key(session_id),
|
||||||
session_data,
|
session_data,
|
||||||
timeout=settings.ADDONS_SESSION_TIMEOUT,
|
timeout=settings.ADDONS_SESSION_TIMEOUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
return session_id
|
# Store the token → session_id binding (same TTL)
|
||||||
|
cache.set(
|
||||||
|
self._token_cache_key(result_token),
|
||||||
|
session_id,
|
||||||
|
timeout=settings.ADDONS_SESSION_TIMEOUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
print('$$ init session_id')
|
||||||
|
print(session_id)
|
||||||
|
|
||||||
|
return session_id, result_token
|
||||||
|
|
||||||
|
# todo - wip
|
||||||
def get_session(self, session_id: str) -> dict:
|
def get_session(self, session_id: str) -> dict:
|
||||||
"""Retrieve session data and clear it if authenticated."""
|
"""Retrieve session data and clear it if authenticated."""
|
||||||
|
|
||||||
cache_key = self._get_cache_key(session_id)
|
return self._get_and_maybe_clear(session_id)
|
||||||
|
|
||||||
|
def get_session_by_token(self, result_token: str, claimed_session_id: str) -> dict:
|
||||||
|
"""Resolve result_token → session_id → session data.
|
||||||
|
|
||||||
|
Verifies that the claimed_session_id matches the token binding,
|
||||||
|
proving the caller initiated this session (ownership check).
|
||||||
|
Clears the session once authenticated (one-time read).
|
||||||
|
"""
|
||||||
|
session_id = cache.get(self._token_cache_key(result_token))
|
||||||
|
if not session_id:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
print("$$$ session_id")
|
||||||
|
print(session_id)
|
||||||
|
|
||||||
|
print("$$$ claimed_session_id")
|
||||||
|
print(claimed_session_id)
|
||||||
|
|
||||||
|
if not secrets.compare_digest(session_id, claimed_session_id):
|
||||||
|
raise SessionOwnershipError("Session ID does not match token binding.")
|
||||||
|
|
||||||
|
return self._get_and_maybe_clear(session_id)
|
||||||
|
|
||||||
|
def _get_and_maybe_clear(self, session_id: str) -> dict:
|
||||||
|
"""Wip."""
|
||||||
|
|
||||||
|
cache_key = self._session_cache_key(session_id)
|
||||||
data = cache.get(cache_key)
|
data = cache.get(cache_key)
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if data.get("state") == SessionState.AUTHENTICATED:
|
if data.get("state") == SessionState.AUTHENTICATED:
|
||||||
|
# One-time read: clear both the session and the token binding
|
||||||
self.clear_session(session_id)
|
self.clear_session(session_id)
|
||||||
|
|
||||||
# Return copy without internal fields
|
# Return copy without internal fields
|
||||||
internal_fields = {"expires_at"}
|
internal_fields = {"expires_at"}
|
||||||
return {k: v for k, v in data.items() if k not in internal_fields}
|
return {k: v for k, v in data.items() if k not in internal_fields}
|
||||||
|
|
||||||
def clear_session(self, session_id: str) -> None:
|
def clear_session(self, session_id: str, result_token: str | None = None) -> None:
|
||||||
"""Remove session data from cache."""
|
"""Wip."""
|
||||||
|
cache.delete(self._session_cache_key(session_id))
|
||||||
cache_key = self._get_cache_key(session_id)
|
if result_token:
|
||||||
cache.delete(cache_key)
|
cache.delete(self._token_cache_key(result_token))
|
||||||
|
|
||||||
def set_access_token(self, user: User, session_id: str):
|
def set_access_token(self, user: User, session_id: str):
|
||||||
"""Generate and store access token for an authenticated user session."""
|
"""Generate and store access token for an authenticated user session."""
|
||||||
|
|
||||||
cache_key = self._get_cache_key(session_id)
|
cache_key = self._session_cache_key(session_id)
|
||||||
existing_data = cache.get(cache_key)
|
existing_data = cache.get(cache_key)
|
||||||
|
|
||||||
if not existing_data:
|
if not existing_data:
|
||||||
@@ -122,3 +170,7 @@ class TokenExchangeService:
|
|||||||
}
|
}
|
||||||
|
|
||||||
cache.set(cache_key, new_data, timeout=remaining_seconds)
|
cache.set(cache_key, new_data, timeout=remaining_seconds)
|
||||||
|
|
||||||
|
def token_to_session(self, result_token):
|
||||||
|
"""wip."""
|
||||||
|
return None
|
||||||
|
|||||||
@@ -2,13 +2,17 @@
|
|||||||
|
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import SuspiciousOperation
|
||||||
|
|
||||||
from rest_framework import (
|
from rest_framework import (
|
||||||
response as drf_response,
|
response as drf_response,
|
||||||
)
|
)
|
||||||
|
from rest_framework import decorators
|
||||||
from rest_framework import status as drf_status
|
from rest_framework import status as drf_status
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
from core.addons.service import TokenExchangeService
|
from core.addons.service import TokenExchangeService, SessionOwnershipError
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
@@ -21,15 +25,61 @@ class AuthSessionViewSet(viewsets.ViewSet):
|
|||||||
throttle_classes = []
|
throttle_classes = []
|
||||||
|
|
||||||
def create(self, request):
|
def create(self, request):
|
||||||
"""Create a new pending authentication session."""
|
"""Create a pending session.
|
||||||
session_id = TokenExchangeService().init_session()
|
|
||||||
return drf_response.Response(
|
Returns session_id in the body (client forwards it to the 3rd-party view).
|
||||||
|
Sets result_token as an HttpOnly cookie (the only poll credential).
|
||||||
|
"""
|
||||||
|
session_id, result_token = TokenExchangeService().init_session()
|
||||||
|
response = drf_response.Response(
|
||||||
{"session_id": session_id}, status=drf_status.HTTP_201_CREATED
|
{"session_id": session_id}, status=drf_status.HTTP_201_CREATED
|
||||||
)
|
)
|
||||||
|
response.set_cookie(
|
||||||
|
key=settings.ADDONS_RESULT_TOKEN_COOKIE_NAME,
|
||||||
|
value=result_token,
|
||||||
|
max_age=6000,
|
||||||
|
httponly=True,
|
||||||
|
secure=True,
|
||||||
|
samesite="None",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
def retrieve(self, request, pk=None):
|
@decorators.action(
|
||||||
"""Retrieve authentication session data by session ID."""
|
detail=False,
|
||||||
data = TokenExchangeService().get_session(pk)
|
methods=["post"],
|
||||||
|
url_name="wip",
|
||||||
|
url_path="wip",
|
||||||
|
permission_classes=[],
|
||||||
|
authentication_classes=[],
|
||||||
|
)
|
||||||
|
def long_poll(self, request):
|
||||||
|
"""Long-poll endpoint — only the cookie is accepted, never a session_id.
|
||||||
|
|
||||||
|
pk is intentionally ignored; the session is resolved from the cookie.
|
||||||
|
"""
|
||||||
|
|
||||||
|
result_token = request.COOKIES.get(settings.ADDONS_RESULT_TOKEN_COOKIE_NAME)
|
||||||
|
session_id = request.data.get("session_id")
|
||||||
|
|
||||||
|
if not result_token:
|
||||||
|
return drf_response.Response(
|
||||||
|
{"detail": "Missing result token."},
|
||||||
|
status=drf_status.HTTP_401_UNAUTHORIZED,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not session_id:
|
||||||
|
return drf_response.Response(
|
||||||
|
{"detail": "Missing result session id."},
|
||||||
|
status=drf_status.HTTP_401_UNAUTHORIZED,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = TokenExchangeService().get_session_by_token(
|
||||||
|
result_token=result_token,
|
||||||
|
claimed_session_id=session_id,
|
||||||
|
)
|
||||||
|
except SessionOwnershipError as e:
|
||||||
|
raise SuspiciousOperation(str(e)) from e
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return drf_response.Response(
|
return drf_response.Response(
|
||||||
@@ -37,11 +87,27 @@ class AuthSessionViewSet(viewsets.ViewSet):
|
|||||||
status=drf_status.HTTP_404_NOT_FOUND,
|
status=drf_status.HTTP_404_NOT_FOUND,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if data.get("state") == "pending":
|
||||||
|
return drf_response.Response(
|
||||||
|
{"state": "pending"},
|
||||||
|
status=drf_status.HTTP_202_ACCEPTED,
|
||||||
|
)
|
||||||
|
|
||||||
return drf_response.Response(data, status=drf_status.HTTP_200_OK)
|
return drf_response.Response(data, status=drf_status.HTTP_200_OK)
|
||||||
|
|
||||||
def destroy(self, request, pk=None):
|
def destroy(self, request, pk=None):
|
||||||
"""Delete an authentication session by session ID."""
|
"""Explicit session teardown, resolves via cookie, not pk."""
|
||||||
TokenExchangeService().clear_session(pk)
|
|
||||||
return drf_response.Response(
|
result_token = request.COOKIES.get(settings.ADDONS_RESULT_TOKEN_COOKIE_NAME)
|
||||||
{"status": "ok"}, status=drf_status.HTTP_204_NO_CONTENT
|
if not result_token:
|
||||||
)
|
return drf_response.Response(status=drf_status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
# We need the session_id to clear both keys — resolve it first
|
||||||
|
session_id = TokenExchangeService().token_to_session(result_token)
|
||||||
|
if session_id:
|
||||||
|
TokenExchangeService().clear_session(session_id, result_token)
|
||||||
|
|
||||||
|
response = drf_response.Response(status=drf_status.HTTP_204_NO_CONTENT)
|
||||||
|
response.delete_cookie(settings.ADDONS_RESULT_TOKEN_COOKIE_NAME)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ urlpatterns = [
|
|||||||
[
|
[
|
||||||
*router.urls,
|
*router.urls,
|
||||||
*oidc_urls,
|
*oidc_urls,
|
||||||
*addons_urls,
|
*addons_urls, # should be in external api
|
||||||
path("config/", get_frontend_configuration, name="config"),
|
path("config/", get_frontend_configuration, name="config"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -808,12 +808,18 @@ class Base(Configuration):
|
|||||||
environ_name="ADDONS_SESSION_ID_LENGTH",
|
environ_name="ADDONS_SESSION_ID_LENGTH",
|
||||||
environ_prefix=None,
|
environ_prefix=None,
|
||||||
)
|
)
|
||||||
# Used in cache key generation
|
# Cache key for the session itself (session_id → session data)
|
||||||
ADDONS_SESSION_KEY_PREFIX = values.Value(
|
ADDONS_SESSION_KEY_PREFIX = values.Value(
|
||||||
"addons_session_id",
|
"addons_session_id",
|
||||||
environ_name="ADDONS_SESSION_KEY_PREFIX",
|
environ_name="ADDONS_SESSION_KEY_PREFIX",
|
||||||
environ_prefix=None,
|
environ_prefix=None,
|
||||||
)
|
)
|
||||||
|
# Cache key for the token → session binding (result_token → session_id)
|
||||||
|
ADDONS_SESSION_TOKEN_PREFIX = values.Value(
|
||||||
|
"addons_token_id",
|
||||||
|
environ_name="ADDONS_SESSION_TOKEN_PREFIX",
|
||||||
|
environ_prefix=None,
|
||||||
|
)
|
||||||
# Used as the Django session key in transit page
|
# Used as the Django session key in transit page
|
||||||
ADDONS_SESSION_KEY_AUTH = values.Value(
|
ADDONS_SESSION_KEY_AUTH = values.Value(
|
||||||
"addons_session_id",
|
"addons_session_id",
|
||||||
@@ -823,6 +829,27 @@ class Base(Configuration):
|
|||||||
ADDONS_SESSION_TIMEOUT = values.PositiveIntegerValue(
|
ADDONS_SESSION_TIMEOUT = values.PositiveIntegerValue(
|
||||||
600, environ_name="ADDONS_SESSION_TIMEOUT", environ_prefix=None
|
600, environ_name="ADDONS_SESSION_TIMEOUT", environ_prefix=None
|
||||||
)
|
)
|
||||||
|
ADDONS_RESULT_TOKEN_COOKIE_NAME = values.Value(
|
||||||
|
"wip",
|
||||||
|
environ_name="ADDONS_RESULT_TOKEN_COOKIE_NAME",
|
||||||
|
environ_prefix=None,
|
||||||
|
)
|
||||||
|
ADDONS_RESULT_TOKEN_COOKIE_SECURE = values.BooleanValue(
|
||||||
|
True,
|
||||||
|
environ_name="ADDONS_RESULT_TOKEN_COOKIE_SECURE",
|
||||||
|
environ_prefix=None,
|
||||||
|
)
|
||||||
|
ADDONS_RESULT_TOKEN_COOKIE_HTTP_ONLY = values.BooleanValue(
|
||||||
|
True,
|
||||||
|
environ_name="ADDONS_RESULT_TOKEN_COOKIE_HTTP_ONLY",
|
||||||
|
environ_prefix=None,
|
||||||
|
)
|
||||||
|
ADDONS_RESULT_TOKEN_COOKIE_SAMESITE = values.Value(
|
||||||
|
"strict",
|
||||||
|
environ_name="ADDONS_RESULT_TOKEN_COOKIE_SAMESITE",
|
||||||
|
environ_prefix=None,
|
||||||
|
)
|
||||||
|
|
||||||
ADDONS_JWT_SECRET_KEY = SecretFileValue(
|
ADDONS_JWT_SECRET_KEY = SecretFileValue(
|
||||||
None, environ_name="ADDONS_JWT_SECRET_KEY", environ_prefix=None
|
None, environ_name="ADDONS_JWT_SECRET_KEY", environ_prefix=None
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user