Skip to content

🔒️(backend) secure native app OIDC login with one-time exchange codes#1170

Open
mmaudet wants to merge 1 commit intomainfrom
feat/oidc-native-app-session-exchange
Open

🔒️(backend) secure native app OIDC login with one-time exchange codes#1170
mmaudet wants to merge 1 commit intomainfrom
feat/oidc-native-app-session-exchange

Conversation

@mmaudet
Copy link
Copy Markdown
Collaborator

@mmaudet mmaudet commented Mar 18, 2026

Summary

  • Replaces feat: pass session ID to native apps via OIDC redirect URL #1153 with a more secure approach addressing the security concerns raised in review
  • Instead of passing the session ID directly in the redirect URL, generates a short-lived (30s), single-use exchange code stored in Redis
  • Native apps exchange this code for the session ID via a dedicated API endpoint
  • Adds a whitelist of allowed custom URL schemes (NATIVE_APP_REDIRECT_SCHEMES setting, defaults to ["visio"])

Changes

  • src/backend/core/authentication/views.py — Custom OIDC callback view that generates a UUID exchange code for whitelisted custom-scheme redirects, stored in cache with 30s TTL
  • src/backend/core/authentication/api.py — New POST /api/v1.0/auth/session-exchange/ endpoint: validates exchange code, returns session ID, deletes code (single-use)
  • src/backend/core/urls.py — Route registration for the exchange endpoint
  • src/backend/meet/settings.pyOIDC_CALLBACK_CLASS pointed to custom view + new NATIVE_APP_REDIRECT_SCHEMES setting (configurable via env var)
  • src/backend/core/tests/authentication/test_session_exchange.py — 8 tests

Security improvements over #1153

Concern #1153 This PR
Session ID in URL Raw session_key in redirect One-time exchange code (UUID)
Scheme whitelist Any non-HTTP(S) scheme accepted Only NATIVE_APP_REDIRECT_SCHEMES (default: ["visio"])
Replay protection None — session ID reusable Code deleted on first use
Time-bound Session lifetime (12h) 30 second TTL

Tests

8 tests covering:

  • Valid code exchange returns session ID
  • Invalid/unknown code returns 400
  • Expired code (TTL=0) returns 400
  • Code too short rejected by validation
  • Missing code field rejected
  • Replay attack: second use of same code fails
  • Response key matches SESSION_COOKIE_NAME setting
  • GET method returns 405 (POST only)

All 74 existing authentication + user tests pass with no regression.

Context

Native mobile/desktop apps (Visio Mobile) authenticate via OIDC using the system browser. The browser cannot share cookies back to the app. This change provides a secure way to pass authentication back:

  1. Server redirects to visio://auth-callback?code=<uuid> (instead of ?session_id=<key>)
  2. App POSTs {"code": "<uuid>"} to /api/v1.0/auth/session-exchange/
  3. Server returns {"meet_sessionid": "<session_key>"} and deletes the code

Test plan

  • Web browser OIDC login still works (returnTo=https://...) — no change in behavior
  • Native app OIDC login receives exchange code in callback URL
  • Exchange endpoint returns valid session ID
  • Exchange code cannot be reused
  • Exchange code expires after 30 seconds
  • Non-whitelisted schemes are not intercepted

Supersedes #1153

Comment thread src/backend/core/authentication/views.py Fixed
@mmaudet mmaudet force-pushed the feat/oidc-native-app-session-exchange branch from ff5e8ec to e148d6b Compare March 20, 2026 17:14
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
@mmaudet mmaudet force-pushed the feat/oidc-native-app-session-exchange branch from cb32adb to 724a4ef Compare March 20, 2026 17:36
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant