You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Local runs cannot exercise authentication at all, and that removes a large slice of the app from testing. ide/auth.py short-circuits identity whenever the server runs locally:
defis_logged_in():
return ('user'insession) or (routes.is_running_locally())
defget_user_info():
ifroutes.is_running_locally():
return {'email': 'localuser@local.host'}
returnsession.get('user') or {}
Running locally you are always signed in as localuser@local.host. You cannot be anonymous, and you cannot be a second user.
What that costs, concretely
Anonymous state does not exist locally. The sign-in link, the not_logged_in API state, and any "must be signed in" guard cannot be tested. Three e2e tests had to be skipped for precisely this (test(e2e): skip anonymous-state tests when the server runs locally #207) — they now skip locally and only run against a deployed server.
Only one identity exists. Anything involving two users — sharing a program, another user's folder, permission boundaries — is untestable locally.
Local diverges from production at the auth layer, which is the layer where divergence is most expensive to discover late.
The three skipped tests are just the visible symptom; the untested surface is much larger.
The fix is configuration, not a special case
Auth is raw Google OIDC through Authlib, driven by discovery:
Because it is standard OIDC discovery, a local mock OIDC provider (mock-oauth2-server, Dex, oauth2-mock-server — all run happily in Docker beside the datastore emulator) can serve that role. Point CONF_URL plus client id/secret at it for local runs and the real OAuth code path executes locally, with real sessions, sign-out, and as many identities as the tests need.
At that point the special case can simply be deleted: is_logged_in() and get_user_info() become session-only, with no environment branch at all.
⚠️ Worth stating because it is an easy wrong turn: the Firebase Auth emulator is not the right tool here. It emulates Firebase Auth, not generic OIDC. CONF_URL is currently a module constant, so the first concrete step is making it (and the client id/secret) configurable.
The same hack is in webvpython
webvpython/flaskHost/src/auth.py carries it at three sites:
line
what
70
is_logged_in() returns true whenever local
73-74
get_user_info() returns localuser@local.host
88
login() sets session['user'] = {'email':'local@user'} and redirects
Line 88 is notable: it is already the right shape — a local login that populates the session — but the other two override it unconditionally, so it never takes effect. Whatever lands here should land there.
Done when
CONF_URL / client id / secret configurable per environment
a mock OIDC provider in the local compose setup
the local-mode branches removed from is_logged_in() and get_user_info()
Summary
Local runs cannot exercise authentication at all, and that removes a large slice of the app from testing.
ide/auth.pyshort-circuits identity whenever the server runs locally:Running locally you are always signed in as
localuser@local.host. You cannot be anonymous, and you cannot be a second user.What that costs, concretely
not_logged_inAPI state, and any "must be signed in" guard cannot be tested. Three e2e tests had to be skipped for precisely this (test(e2e): skip anonymous-state tests when the server runs locally #207) — they now skip locally and only run against a deployed server.The three skipped tests are just the visible symptom; the untested surface is much larger.
The fix is configuration, not a special case
Auth is raw Google OIDC through Authlib, driven by discovery:
Because it is standard OIDC discovery, a local mock OIDC provider (mock-oauth2-server, Dex, oauth2-mock-server — all run happily in Docker beside the datastore emulator) can serve that role. Point
CONF_URLplus client id/secret at it for local runs and the real OAuth code path executes locally, with real sessions, sign-out, and as many identities as the tests need.At that point the special case can simply be deleted:
is_logged_in()andget_user_info()become session-only, with no environment branch at all.CONF_URLis currently a module constant, so the first concrete step is making it (and the client id/secret) configurable.The same hack is in webvpython
webvpython/flaskHost/src/auth.pycarries it at three sites:is_logged_in()returns true whenever localget_user_info()returnslocaluser@local.hostlogin()setssession['user'] = {'email':'local@user'}and redirectsLine 88 is notable: it is already the right shape — a local login that populates the session — but the other two override it unconditionally, so it never takes effect. Whatever lands here should land there.
Done when
CONF_URL/ client id / secret configurable per environmentis_logged_in()andget_user_info()webvpython/flaskHost/src/auth.pyDesign notes on branch
feat/local-auth-emulator(docs/LOCAL-AUTH-DESIGN.md).