Skip to content

feat: real OAuth login (Discord/GitHub/Twitch) + JWT session - #7

Open
tecrodrigocastro wants to merge 4 commits into
mainfrom
feat/login-oauth
Open

tecrodrigocastro wants to merge 4 commits into
mainfrom
feat/login-oauth

Conversation

@tecrodrigocastro

Copy link
Copy Markdown
Contributor

Summary

This connects to the real backend — unlike the Home/Timeline mockups, this is live: login actually authenticates against the heartdevs.com mobile API (JWT), per docs/plans/2026-09-22-api-mobile-jwt.md on that repo.

  • New Login screen — mobile-native design (gradient brand hero, not a literal port of the web split-screen login). OAuth-only (Discord/GitHub/Twitch via Browser::auth()) — the mobile API has no email/password endpoint yet, so that form from the web reference was deliberately left out rather than built non-functional.
  • New OAuthCallback screen handles the he4rtapp://oauth/{action} deep link, trades the one-time code for a token pair (POST /api/mobile/auth/exchange), and bounces back to Login with a friendly message on failure.
  • AuthTokenStore (SecureStorage-backed) is the single source of truth for "is the user logged in". He4rtApi is a thin client for the four JWT endpoints (exchange/refresh/logout/me).
  • Splash now routes to /home or /login depending on whether a token is stored.
  • Perfil is connected for real: shows the actual username/avatar from GET /api/mobile/me, attempts one token refresh on a 401, and has a working Logout button (POST /api/mobile/auth/logout). Drops the old hardcoded POC Sanctum token entirely.
  • NATIVEPHP_DEEPLINK_SCHEME now defaults to he4rtapp, matching the API's HE4RT_APP_DEEPLINK_SCHEME default.

Known risk (not fixed here, by design)

The API side's plan doc flags a real, documented security debt: the custom URL scheme (he4rtapp://) isn't exclusive to this app — another app registering the same scheme could race to intercept the OAuth exchange code. The code is single-use and expires in 60s, which bounds the blast radius, but the correct fix is PKCE or a verified HTTPS App Link, and that needs coordinated changes on both repos. Tracked as follow-up, discussed and explicitly accepted for this PR rather than blocking login on it.

Test plan

  • php artisan test --compact — 31 passed (new: Login, OAuthCallback, token-refresh/logout flows on Perfil, Splash's two branches)
  • vendor/bin/pint --dirty — clean
  • php artisan native:validate — passes (pre-existing warnings only)
  • Manual check on iOS simulator against a running heartdevs.com instance (php artisan native:run ios)
  • Manual check on Android emulator (php artisan native:run android)
  • Confirm HE4RT_APP_DEEPLINK_SCHEME on heartdevs.com matches he4rtapp (or update .env/NATIVEPHP_DEEPLINK_SCHEME here to match whatever it's actually set to)

AuthTokenStore wraps SecureStorage as the single source of truth for
"is the user logged in"; He4rtApi is a thin client for the mobile JWT
endpoints (exchange/refresh/logout/me) documented in
docs/plans/2026-09-22-api-mobile-jwt.md on heartdevs.com.
Login is mobile-native (gradient brand hero, no literal web-form
clone) and OAuth-only — Discord/GitHub/Twitch via Browser::auth(),
since the mobile API doesn't expose an email/password endpoint yet.
OAuthCallback lands on the he4rtapp://oauth/{action} deep link, trades
the one-time code for a token pair, and bounces back to Login with a
friendly message on failure.

Known risk inherited from the API side (documented in
docs/plans/2026-09-22-api-mobile-jwt.md on heartdevs.com): the custom
URL scheme isn't exclusive to this app, so another app registering the
same scheme could race to consume the exchange code first. The code is
single-use and expires in 60s, which bounds the damage; the real fix
(PKCE or a verified HTTPS App Link) needs coordinated changes on both
repos and is tracked as follow-up, not fixed in this PR.

Added a fakeSecureStorage() test helper (tests/Pest.php) — FakeBridge
records SecureStorage calls but doesn't persist them, so this replays
the latest Set/Delete per key to make AuthTokenStore round-trip in
tests.
Routes to /home when a token is already in secure storage, /login
otherwise. Token validity itself isn't checked here — that's lazy,
left to whichever screen calls the API first (see Perfil).
Drops the hardcoded POC Sanctum token — Perfil now reads the real
stored JWT, calls GET /api/mobile/me, and shows the actual username
and avatar. A 401 triggers one refresh attempt before giving up and
bouncing to Login; a working Logout button calls POST
/api/mobile/auth/logout and clears the stored token either way.
@tecrodrigocastro
tecrodrigocastro requested a review from a team October 4, 2026 14:13
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