""" check_auth.py — offline checks for login (services/auth.py). No network, no database. uv run python scripts/check_auth.py Covers the session token (sign / verify / tamper / expiry / wrong secret), the company-domain rule, and verify_google_credential() against FAKE Google replies (wrong audience, unverified email, wrong domain, wrong hd, good). A real Google sign-in still needs a Google Client ID and is tested in the browser. """ from __future__ import annotations import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from app.config import settings # noqa: E402 from app.services import auth # noqa: E402 FAILED = 0 def check(label: str, ok: bool) -> None: global FAILED FAILED += 0 if ok else 1 print(f" [{'ok ' if ok else 'FAIL'}] {label}") class FakeResp: def __init__(self, status: int, data: dict): self.status_code = status self._data = data def json(self): return self._data def google_reply(**overrides) -> dict: base = { "aud": "client-123", "iss": "https://accounts.google.com", "email_verified": "true", "email": "ali@elipsestudio.com", "name": "Ali Khan", "picture": "http://x/p.png", "hd": "elipsestudio.com", } base.update(overrides) return base def expect_error(credential_reply: dict | None, status: int = 200) -> bool: auth.requests.get = lambda *a, **k: FakeResp(status, credential_reply or {}) # type: ignore[assignment] try: auth.verify_google_credential("tok") return False except auth.AuthError: return True def main() -> int: settings.auth_secret = "s" * 40 settings.google_client_id = "client-123" settings.allowed_email_domains = "elipsestudio.com, other.org" settings.allowed_emails = "" # ignore whatever the real .env lists, so this test is the same on every machine settings.auth_token_days = 7 print("\nsession token") tok = auth.create_token(7, "ali@elipsestudio.com", "Ali Khan") claims = auth.decode_token(tok) check("a fresh token verifies and carries the user id", claims is not None and claims["sub"] == "7") check("three dot-separated JWT parts", tok.count(".") == 2) h, p, sig = tok.split(".") check("tampered payload is rejected", auth.decode_token(f"{h}.{p[:-2]}xx.{sig}") is None) check("tampered signature is rejected", auth.decode_token(f"{h}.{p}.{sig[:-2]}xx") is None) check("an expired token is rejected", auth.decode_token(auth.create_token(7, "a@elipsestudio.com", "A", days=-1)) is None) check("garbage is rejected, not raised", auth.decode_token("not-a-token") is None and auth.decode_token("") is None) settings.auth_secret = "t" * 40 check("a token signed with another secret is rejected", auth.decode_token(tok) is None) settings.auth_secret = "s" * 40 settings.auth_secret = "" check("no AUTH_SECRET -> cannot issue a token", _raises(lambda: auth.create_token(1, "a@b.c", "A"))) check("no AUTH_SECRET -> nothing verifies", auth.decode_token(tok) is None) settings.auth_secret = "s" * 40 print("\ncompany domain") check("elipsestudio.com allowed", auth.email_domain_allowed("Ali@ElipseStudio.com")) check("second configured domain allowed", auth.email_domain_allowed("x@other.org")) check("gmail.com refused", not auth.email_domain_allowed("ali@gmail.com")) check("look-alike domain refused", not auth.email_domain_allowed("ali@elipsestudio.com.evil.io")) check("sub-domain refused", not auth.email_domain_allowed("ali@mail.elipsestudio.com")) check("no @ refused", not auth.email_domain_allowed("elipsestudio.com")) print("\napproved emails (ALLOWED_EMAILS)") settings.allowed_emails = "Boss@Gmail.com, sara@partner.io" check("a listed email is allowed, case-insensitive", auth.email_allowed("boss@gmail.com")) check("an unlisted gmail is still refused", not auth.email_allowed("other@gmail.com")) check("a company-domain email is still allowed by domain", auth.email_allowed("x@elipsestudio.com")) check("a look-alike of a listed email is refused", not auth.email_allowed("boss@gmail.com.evil.io")) settings.allowed_emails = "" print("\nverify_google_credential (fake Google replies)") real_get = auth.requests.get try: auth.requests.get = lambda *a, **k: FakeResp(200, google_reply()) # type: ignore[assignment] who = auth.verify_google_credential("tok") check("a good reply returns email/name/picture", who["email"] == "ali@elipsestudio.com" and who["name"] == "Ali Khan") check("Google says invalid (HTTP 400)", expect_error({}, 400)) check("wrong audience refused", expect_error(google_reply(aud="someone-else"))) check("wrong issuer refused", expect_error(google_reply(iss="https://evil.example"))) check("unverified email refused", expect_error(google_reply(email_verified="false"))) check("gmail address refused", expect_error(google_reply(email="ali@gmail.com", hd=""))) check("wrong hd refused even with a good-looking email", expect_error(google_reply(hd="evil.io"))) settings.google_client_id = "" check("no GOOGLE_CLIENT_ID -> clear error", expect_error(google_reply())) finally: auth.requests.get = real_get print() print("All checks passed." if not FAILED else f"{FAILED} check(s) FAILED") return 1 if FAILED else 0 def _raises(fn) -> bool: try: fn() except auth.AuthError: return True return False if __name__ == "__main__": sys.exit(main())