Files
thejayman77 a2765af3fc Fix: capture Google avatar on returning sign-in (+ userinfo fallback)
find_or_create_user returned early when the identity already existed, so a
returning Google sign-in never refreshed the profile picture (the name had been
set earlier, at link time — which is why name worked but avatar stayed null).
Now profile bits refresh on every sign-in. Also fall back to the OIDC userinfo
endpoint for the picture if the ID token omits it. 119 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:57:44 +00:00

81 lines
3.1 KiB
Python

import sqlite3
from datetime import timedelta
from goodnews import auth
from goodnews.db import connect, init_db
def _db():
c = connect(":memory:")
init_db(c)
# an article to save/history against later flows
c.execute("INSERT INTO sources (id,name,feed_url,trust_score) VALUES (1,'S','http://s/f',5)")
c.execute("INSERT INTO articles (id,source_id,canonical_url,title,url_hash) "
"VALUES (1,1,'http://s/1','t1','h1')")
c.commit()
return c
def test_normalize_email():
assert auth.normalize_email(" Foo@Bar.COM ") == "foo@bar.com"
def test_find_or_create_links_by_email_and_dedupes_identity():
c = _db()
uid = auth.find_or_create_user(c, "a@b.com", "email", "a@b.com")
# same identity again → same user, no duplicate
assert auth.find_or_create_user(c, "a@b.com", "email", "a@b.com") == uid
# a different provider with the SAME verified email links to the same user
uid2 = auth.find_or_create_user(c, "A@B.com", "google", "google-sub-123", display_name="A")
assert uid2 == uid
assert c.execute("SELECT COUNT(*) FROM users").fetchone()[0] == 1
assert c.execute("SELECT COUNT(*) FROM identities WHERE user_id=?", (uid,)).fetchone()[0] == 2
assert auth.get_user(c, uid)["display_name"] == "A"
def test_returning_identity_refreshes_avatar():
c = _db()
uid = auth.find_or_create_user(c, "a@b.com", "google", "gsub", display_name="A", avatar_url="http://pic/1")
assert auth.get_user(c, uid)["avatar_url"] == "http://pic/1"
# a repeat sign-in with the SAME identity must still refresh the picture
assert auth.find_or_create_user(c, "a@b.com", "google", "gsub", avatar_url="http://pic/2") == uid
assert auth.get_user(c, uid)["avatar_url"] == "http://pic/2"
def test_magic_link_token_single_use():
c = _db()
raw = auth.create_login_token(c, "a@b.com")
assert auth.consume_login_token(c, raw) == "a@b.com" # first use works
assert auth.consume_login_token(c, raw) is None # reuse rejected
assert auth.consume_login_token(c, "nonsense") is None # unknown rejected
def test_magic_link_token_expiry():
c = _db()
raw = auth.create_login_token(c, "a@b.com")
# backdate expiry into the past
c.execute("UPDATE login_tokens SET expires_at = ?",
(auth._iso(auth._now() - timedelta(minutes=1)),))
assert auth.consume_login_token(c, raw) is None
def test_session_lifecycle():
c = _db()
uid = auth.find_or_create_user(c, "a@b.com", "email", "a@b.com")
tok = auth.create_session(c, uid, user_agent="pytest")
user = auth.resolve_session(c, tok)
assert user and user["id"] == uid and user["email"] == "a@b.com"
assert auth.resolve_session(c, None) is None
assert auth.resolve_session(c, "bogus") is None
auth.revoke_session(c, tok)
assert auth.resolve_session(c, tok) is None
def test_session_expiry():
c = _db()
uid = auth.find_or_create_user(c, "a@b.com", "email", "a@b.com")
tok = auth.create_session(c, uid)
c.execute("UPDATE sessions SET expires_at = ?",
(auth._iso(auth._now() - timedelta(seconds=1)),))
assert auth.resolve_session(c, tok) is None