Skip to content

Add federated (Google/Facebook/Amazon) POTA sign-in via hosted-UI OAuth#161

Merged
patrickrb merged 3 commits into
devfrom
feat/pota-oauth-login
Jun 8, 2026
Merged

Add federated (Google/Facebook/Amazon) POTA sign-in via hosted-UI OAuth#161
patrickrb merged 3 commits into
devfrom
feat/pota-oauth-login

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

What

Adds federated sign-in (Google / Facebook / Login-with-Amazon) to the in-app POTA log upload from #154. Stacked on feat/pota-upload-spike — review the OAuth commit only.

Why

The spike's login is Cognito USER_SRP_AUTH — email + password. SRP can only authenticate accounts that have a Cognito password. POTA users who sign in "with Google" (or Facebook / Amazon) are federated identities with no pool password, so SRP fails for them and there's no in-app way forward. This closes that gap.

How POTA's auth is configured (reverse-engineered)

  • Hosted UI: https://parksontheair.auth.us-east-2.amazoncognito.com (from the pool's /.well-known/openid-configuration + pota.app's JS bundle).
  • Offers Google, Facebook, LoginWithAmazon, plus email/password, all on one managed-login page.
  • Authorization-code flow, public client (no secret) → PKCE. Scopes openid email phone profile.
  • The only registered redirect URI is https://pota.app/. Custom schemes, localhost, and any other path return redirect_mismatch.

Approach — and why WebView, not Custom Tabs

Because the sole redirect URI is POTA's own web domain, Chrome Custom Tabs can't intercept the code (claiming https://pota.app/ would need assetlinks.json on POTA's server). A WebView we control can: it watches navigation and lifts ?code= out the instant Cognito redirects, before pota.app actually loads. Then it exchanges code→tokens with PKCE at /oauth2/token.

The flow produces an ordinary Cognito refresh token, so everything downstream — idToken(), refreshIdToken(), PotaClient.uploadAdif() — is unchanged. Federated login only fills in that first token.

Google WebView caveat: the default Android WebView UA contains ; wv, which Google's consent screen rejects (disallowed_useragent). The WebView uses a plain Chrome UA so Google sign-in loads. Facebook / Amazon / email work either way.

Changes

  • PotaAuth.ktnewPkce(), authorizeUrl(), exchangeCode() (token-endpoint POST + refresh-token persistence), and email-claim parsing from the ID token for display. Same pota_auth SharedPreferences and ID-token cache as the SRP path.
  • PotaOAuthLogin.kt (new) — full-screen PotaOAuthDialog WebView that drives the hosted-UI flow, captures the redirect, and runs the exchange (spinner while exchanging). Clears cookies per attempt so the user can choose an account.
  • PotaScreen.kt — an "or … Sign in with Google / Facebook / Amazon" button in the existing login dialog; success resumes the pending upload. Native email/password dialog untouched.
  • strings_compose.xml — 3 new strings.

Status / scope

assembleDebug is green. As with the parent spike, the live federated round-trip is unverified — it needs a real Google/Facebook/Amazon-backed pota.app account on a device (the WebView-UA-vs-Google behavior especially only confirms on-device).

Follow-ups:

  • On-device federated login + upload verification (Google especially).
  • If Google's UA block can't be reliably bypassed, consider guiding Google-only users to set a pota.app password as a fallback.
  • Inherits the spike's plaintext-refresh-token storage; EncryptedSharedPreferences migration still pending there.

🤖 Generated with Claude Code

The SRP email+password path can only authenticate accounts that have a
Cognito password. POTA users who sign in with Google / Facebook /
Login-with-Amazon are federated identities with no pool password, so SRP
fails for them with no in-app recourse.

Add the Cognito hosted-UI authorization-code + PKCE flow, which covers
every login method POTA offers (the managed login page presents email,
Google, Facebook and Amazon together). It produces an ordinary refresh
token, so the existing idToken()/refreshIdToken() + /adif upload path is
unchanged downstream.

POTA's Cognito app client registers exactly one redirect URI
(https://pota.app/) and rejects custom schemes / localhost, so Chrome
Custom Tabs can't intercept the code. A WebView we control can: it watches
navigation and lifts ?code= out the instant Cognito redirects, before
pota.app loads. The default WebView UA ("; wv") trips Google's
disallowed_useragent block, so the WebView uses a plain Chrome UA.

- PotaAuth: newPkce()/authorizeUrl()/exchangeCode() + token-endpoint POST
  and id_token email-claim parsing for display.
- PotaOAuthLogin.kt (new): full-screen WebView dialog driving the flow.
- PotaScreen: "Sign in with Google / Facebook / Amazon" button in the
  existing login dialog hands off to the WebView; success resumes the
  pending upload.

assembleDebug green. Live federated round-trip still needs on-device
verification with a real federated pota.app account.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a federated OAuth (hosted-UI) sign-in path for POTA uploads so users who authenticate via Google/Facebook/Amazon (no Cognito password → SRP fails) can still obtain a refresh token and proceed with in-app uploads.

Changes:

  • Added Cognito hosted-UI OAuth2 authorization-code + PKCE support in PotaAuth (authorize URL, code exchange, refresh-token persistence).
  • Introduced a full-screen WebView dialog to run the hosted-UI flow and capture the redirect code.
  • Updated the existing POTA login dialog to offer a “Sign in with Google / Facebook / Amazon” path.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
ft8cn/app/src/main/res/values/strings_compose.xml Adds strings for the new “or” separator and hosted-UI sign-in UI.
ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/ui/pota/PotaScreen.kt Adds a federated sign-in button and hooks it to the new hosted-UI WebView flow.
ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/ui/pota/PotaOAuthLogin.kt New full-screen WebView dialog to drive hosted-UI login, capture code=, and exchange for tokens.
ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/pota/PotaAuth.kt Implements PKCE generation, hosted-UI authorize URL, token exchange, and email-claim extraction for display.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

url: String,
): Boolean = handleRedirect(url)
}
loadUrl(PotaAuth.authorizeUrl(pkce))
Comment on lines +107 to +110
// Plain Chrome UA (no "; wv") so Google's consent screen loads.
settings.userAgentString =
"Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36"
Base automatically changed from feat/pota-upload-spike to dev June 8, 2026 20:03
patrickrb and others added 2 commits June 8, 2026 15:13
# Conflicts:
#	ft8cn/app/src/main/kotlin/radio/ks3ckc/ft8us/pota/PotaAuth.kt
- Load the authorize URL from removeAllCookies' callback so the OAuth
  page only navigates once cookies are cleared (removeAllCookies is
  async; the eager loadUrl could reuse a stale session).
- Strip only the "; wv" token from the device default WebView UA instead
  of hard-coding an Android 14 / Pixel 8 / Chrome 125 string that ages
  out; "; wv" is what Google rejects (disallowed_useragent).
- Extract the UA logic to a testable stripWebViewToken() helper and cover
  it with StripWebViewTokenTest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0.66225% with 150 lines in your changes missing coverage. Please review.
✅ Project coverage is 6.49%. Comparing base (4d9f941) to head (0ce07d5).
⚠️ Report is 25 commits behind head on dev.

Files with missing lines Patch % Lines
...rc/main/kotlin/radio/ks3ckc/ft8us/pota/PotaAuth.kt 0.00% 67 Missing ⚠️
...otlin/radio/ks3ckc/ft8us/ui/pota/PotaOAuthLogin.kt 1.69% 58 Missing ⚠️
...in/kotlin/radio/ks3ckc/ft8us/ui/pota/PotaScreen.kt 0.00% 25 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##               dev    #161      +/-   ##
==========================================
+ Coverage     6.37%   6.49%   +0.12%     
- Complexity     681     685       +4     
==========================================
  Files          269     271       +2     
  Lines        30979   31416     +437     
  Branches      4856    4958     +102     
==========================================
+ Hits          1974    2040      +66     
- Misses       28864   29235     +371     
  Partials       141     141              
Files with missing lines Coverage Δ
...in/kotlin/radio/ks3ckc/ft8us/ui/pota/PotaScreen.kt 0.00% <0.00%> (ø)
...otlin/radio/ks3ckc/ft8us/ui/pota/PotaOAuthLogin.kt 1.69% <1.69%> (ø)
...rc/main/kotlin/radio/ks3ckc/ft8us/pota/PotaAuth.kt 0.00% <0.00%> (ø)

... and 14 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@patrickrb patrickrb merged commit 64fcc28 into dev Jun 8, 2026
4 checks passed
@patrickrb patrickrb deleted the feat/pota-oauth-login branch June 8, 2026 20:29
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.

2 participants