Skip to content

Add in-app POTA log upload (Cognito auth spike)#154

Open
patrickrb wants to merge 1 commit into
devfrom
feat/pota-upload-spike
Open

Add in-app POTA log upload (Cognito auth spike)#154
patrickrb wants to merge 1 commit into
devfrom
feat/pota-upload-spike

Conversation

@patrickrb
Copy link
Copy Markdown
Owner

What

Adds in-app POTA activation-log upload, so activators can submit logs directly from the app instead of exporting an ADIF and uploading it on the pota.app website.

How POTA's upload works (reverse-engineered)

The pota.app uploader is a static SPA over an AWS backend. Auth is the work; the upload is trivial. POTA's Cognito pool/client IDs are public (shipped in pota.app's JS bundle, also reproduced in the open-source pota-adif-upload Rust client):

  • Login: AWS Cognito USER_SRP_AUTHPASSWORD_VERIFIER → JWT ID token
    • region us-east-2, pool us-east-2_nA5jZ0klh, client 7hluqct0n2nckib7i7sd5753oa (no client secret)
  • Upload: POST https://api.pota.app/adif, header Authorization: <ID token> (raw JWT, no Bearer), multipart/form-data with a single adif part
  • Confirm: GET https://api.pota.app/user/jobs

The user signs in with their normal pota.app account email + password.

Changes

  • PotaAuth.kt (new) — Cognito SRP login via aws-android-sdk-cognitoidentityprovider; refresh token persisted in a private pota_auth SharedPreferences; fresh ID tokens minted via REFRESH_TOKEN_AUTH (a plain JSON POST — no SRP, no SDK).
  • PotaClient.uploadAdif() / getJobs() — multipart POST /adif with the raw ID token; job-status read.
  • PotaAdifExporter.buildActivationAdif() — extracted from the share path so share-sheet and upload emit byte-identical ADIF.
  • PotaScreen History tab — primary Upload to POTA button + sign-in dialog. First upload prompts login; afterwards the stored refresh token mints tokens silently. Existing Share ADIF / Open pota.app fallbacks unchanged.
  • build.gradle — adds com.amazonaws:aws-android-sdk-cognitoidentityprovider:2.76.0.

Status / scope

This is an auth spike: assembleDebug is green (AWS SDK resolves, Kotlin/dex/package clean), but the live round-trip (real SRP login + a real POST /adif accepted by POTA) still needs to be exercised on-device with a real pota.app account — no device was attached at build time.

Follow-ups before this is "done":

  • On-device live login + upload verification (confirm raw-token Authorization and multipart shape are accepted; check /user/jobs)
  • Move the refresh token to EncryptedSharedPreferences (currently plaintext, matching the existing QRZ password storage — could cover both)
  • Unit tests for buildActivationAdif parity + PotaAuth token caching

Caveats

Rides POTA's undocumented API (same path potashell and the Rust CLI use). POTA could rotate the client ID or change the endpoint without notice, so the manual Share/Open fallbacks intentionally stay.

🤖 Generated with Claude Code

POTA's activation-log upload is gated by AWS Cognito (SRP) then a plain
multipart POST to api.pota.app/adif. The pool/client IDs are public (they
ship in pota.app's JS bundle and the open-source pota-adif-upload crate), so
the app can log in with the user's pota.app account and upload directly
instead of bouncing through the website.

The pota module already produced correct per-park ADIF and hit the public
spot endpoints; the only missing piece was the Cognito login.

- PotaAuth: USER_SRP_AUTH login via aws-android-sdk-cognitoidentityprovider,
  refresh token persisted in a private SharedPreferences, fresh ID tokens
  minted via REFRESH_TOKEN_AUTH (raw JSON POST, no SDK).
- PotaClient.uploadAdif()/getJobs(): multipart POST /adif with the raw ID
  token in Authorization (no Bearer prefix), plus job-status read.
- PotaAdifExporter.buildActivationAdif(): extracted so the share-sheet and
  upload paths emit identical bytes.
- PotaScreen History tab: primary "Upload to POTA" button + sign-in dialog;
  first upload prompts login, then it is silent. Share-ADIF / Open-pota.app
  fallbacks are unchanged.

Spike scope: refresh token is stored in plaintext (matches the existing QRZ
password storage) -- EncryptedSharedPreferences is a planned follow-up. Rides
POTA's undocumented API, so the manual fallbacks stay.

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

codecov Bot commented Jun 8, 2026

Codecov Report

❌ Patch coverage is 0% with 310 lines in your changes missing coverage. Please review.
✅ Project coverage is 6.19%. Comparing base (a24a2b7) to head (8f01398).
⚠️ Report is 1 commits behind head on dev.

Files with missing lines Patch % Lines
...in/kotlin/radio/ks3ckc/ft8us/ui/pota/PotaScreen.kt 0.00% 102 Missing ⚠️
...rc/main/kotlin/radio/ks3ckc/ft8us/pota/PotaAuth.kt 0.00% 86 Missing ⚠️
...lin/radio/ks3ckc/ft8us/ui/pota/PotaAdifExporter.kt 0.00% 67 Missing ⚠️
.../main/kotlin/radio/ks3ckc/ft8us/pota/PotaClient.kt 0.00% 55 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##               dev    #154      +/-   ##
==========================================
- Coverage     6.24%   6.19%   -0.05%     
  Complexity     669     669              
==========================================
  Files          267     268       +1     
  Lines        30584   30829     +245     
  Branches      4763    4831      +68     
==========================================
  Hits          1910    1910              
- Misses       28533   28778     +245     
  Partials       141     141              
Files with missing lines Coverage Δ
.../main/kotlin/radio/ks3ckc/ft8us/pota/PotaClient.kt 0.00% <0.00%> (ø)
...lin/radio/ks3ckc/ft8us/ui/pota/PotaAdifExporter.kt 4.00% <0.00%> (-0.09%) ⬇️
...rc/main/kotlin/radio/ks3ckc/ft8us/pota/PotaAuth.kt 0.00% <0.00%> (ø)
...in/kotlin/radio/ks3ckc/ft8us/ui/pota/PotaScreen.kt 0.00% <0.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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