Skip to content

fix(providers): restore self-service integrations - #2045

Merged
Asherlc merged 7 commits into
mainfrom
codex/issue-1996
Jul 26, 2026
Merged

Asherlc merged 7 commits into
mainfrom
codex/issue-1996

Conversation

@Asherlc

@Asherlc Asherlc commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a shared per-user personal-token connection flow to the server, web, and mobile apps
  • move BodySpec to its current public PKCE client and repair Cycling Analytics, Ultrahuman, and Wger with their supported self-service auth paths
  • stop loading Fitbit, Suunto, COROS, Komoot, MapMyFitness, and Decathlon in production while preserving their modules and historical provider IDs
  • update the provider inventory and auth guidance with current vendor sources
  • address every review finding with canonical auth classification, explicit missing-metadata failures, user-scoped Ultrahuman tokens, and post-persistence cache error reporting

Validation

Validated on Node 26.5.0 at exact head 5e5504c5a22c36765e885386221589a03af87400:

  • full unit/mobile suite — 839 files passed / 2 skipped; 13,804 tests passed / 21 skipped
  • review-focused Stryker run — 51/51 mutants killed (100%; 0 survived, 0 no coverage)
  • focused real-Compose Wger and Ultrahuman integration suite — 2 files / 18 tests passed
  • focused provider/server review suite — 208 unit tests passed
  • focused mobile review suite — 141 tests passed
  • pnpm typecheck
  • pnpm --filter dofek-server typecheck
  • pnpm --filter dofek-web typecheck
  • pnpm --filter dofek-mobile typecheck
  • pnpm lint
  • web Storybook production build
  • Biome on every review-bundle TypeScript/TSX file
  • git diff --check

Exact-head CI: https://github.com/Asherlc/dofek/actions/runs/30216187551

Fixes #1996

Summary by CodeRabbit

  • New Features

    • Added personal-token connection flows across web and mobile, including instructions, token entry, and connection status updates.
    • Added support for token-based connections for additional providers, including Wger, Ultrahuman, and Cycling Analytics.
    • Updated BodySpec connection support to use secure OAuth with PKCE.
  • Bug Fixes

    • Improved authentication failure handling with clearer reconnect guidance.
    • Prevented sensitive tokens and upstream error details from appearing in error reports.
  • Documentation

    • Updated provider configuration, supported data sources, authentication guidance, and production availability details.

Use current public or personal-token flows for BodySpec, Cycling
Analytics, Ultrahuman, and Wger. Stop loading providers that still
require unavailable vendor onboarding or obsolete API migration.

Refs #1996
Copilot AI review requested due to automatic review settings July 26, 2026 17:09
@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @Asherlc, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change introduces personal-token authentication across providers, server routes, web and mobile clients, and transactional token persistence. It also migrates selected providers away from deployment credentials, updates BodySpec to PKCE OAuth, and limits production registration to providers with supported onboarding.

Changes

Token authentication and provider contracts

Layer / File(s) Summary
Manual-token contracts and metadata
src/providers/types.ts, src/providers/provider-model.ts, src/providers/provider-auth-policy.ts
Adds the token auth type, manual-token setup, HTTPS instruction URL validation, and per-user compliance handling.
Provider authentication migrations
src/providers/{bodyspec,cycling-analytics,ultrahuman,wger}.ts
Adds personal-token flows, PKCE OAuth configuration, token refresh handling, and redacted authentication errors.
Server exchange and persistence
packages/server/src/routers/token-auth.ts, src/db/tokens.ts
Adds the protected token connection mutation and atomically persists provider connections and tokens.
Client connection flows
packages/web/src/components/*, packages/mobile/app/providers/*
Propagates token metadata, renders token modals, submits tokens, and refreshes provider state after success.
Production registration and documentation
src/jobs/*, packages/server/src/routers/sync-helpers.ts, README.md, docs/*
Removes externally gated providers from production registration and documents personal-token and provider configuration rules.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ProviderPanel
  participant tokenAuthRouter
  participant Provider
  participant Database
  User->>ProviderPanel: Enter personal token
  ProviderPanel->>tokenAuthRouter: connect(providerId, token)
  tokenAuthRouter->>Provider: exchangeToken(token)
  Provider-->>tokenAuthRouter: TokenSet
  tokenAuthRouter->>Database: Persist connection and tokens
  Database-->>ProviderPanel: success
  ProviderPanel->>ProviderPanel: Refresh provider status
Loading

Assessment against linked issues

Objective Addressed Explanation
Align production provider availability with deployable onboarding requirements [#1996]
Prevent providers from being silently hidden because required deployment credentials are absent [#1996]

Possibly related PRs

  • Asherlc/dofek#1284: Related per-user credential connection policy changes in the same provider-auth policy module.

Suggested labels: area/server, area/web, area/mobile, area/providers, area/db, type/bug, type/feature

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is relevant and imperative, but it uses fix(providers): instead of the required area prefix format. Rename it to an area-prefixed title such as [providers] restore self-service integrations.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Restore self-service provider integrations with per-user personal-token auth

🐞 Bug fix ✨ Enhancement 📝 Documentation 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Add a shared per-user personal-token connect flow across server, web, and mobile.
• Restore BodySpec, Cycling Analytics, Ultrahuman, and Wger using supported self-service auth.
• Stop registering gated providers in production; keep modules/IDs for historical data.
Diagram

graph TD
  A["Web/Mobile UI"] --> B["TokenAuthModal"] --> C(["tRPC: tokenAuth.connect"]) --> D(["Server: tokenAuthRouter"]) --> E["Provider: manualToken.exchangeToken"] --> F[("tokens store")]
  A --> G(["tRPC: sync.providers"]) --> H["ProviderModel (tokenAuth meta)"]

  subgraph Legend
    direction LR
    _ui["Client UI"] ~~~ _svc(["Service/Router"]) ~~~ _mod["Module/Provider"] ~~~ _db[("Database")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Fold token connect into syncRouter (e.g., sync.connectToken)
  • ➕ Fewer top-level routers to expose in appRouter
  • ➕ Keeps all provider connect operations under one logical API surface
  • ➖ syncRouter becomes a grab-bag of unrelated operations
  • ➖ Harder to apply distinct telemetry/meta/error handling for token-only flow
2. Represent token auth as a generic 'custom auth router' instead of new authType
  • ➕ Avoids expanding ProviderAuthType and downstream schema changes
  • ➕ Could reuse existing patterns used for Garmin/WHOOP
  • ➖ Overkill for a simple modal + mutation flow
  • ➖ Would likely duplicate per-provider UI plumbing instead of server-described metadata
3. Keep gated providers behind a runtime feature flag instead of removing registration
  • ➕ Easier to enable for internal testing or staged onboarding
  • ➕ Avoids diff churn when vendor access becomes available
  • ➖ Still risks production exposure if flag defaults/rollouts are misconfigured
  • ➖ Adds more configuration surface to maintain

Recommendation: The PR’s approach (explicit authType='token' + server-described tokenAuth metadata + dedicated tokenAuthRouter) is the cleanest way to support per-user personal tokens across clients while keeping provider logic in provider modules. It also enables a strict production registry by omitting externally gated providers without deleting modules/IDs. The main thing to watch is that all token-related error paths remain free of token leakage (tests already cover this).

Files changed (46) +1590 / -457

Enhancement (16) +503 / -26
[id].tsxCentralize provider auth modal rendering in ProviderDetailScreen +2/-15

Centralize provider auth modal rendering in ProviderDetailScreen

• Replaces inline modal rendering with ProviderDetailAuthModals. This enables adding token-auth modal support without further expanding ProviderDetailScreen JSX.

packages/mobile/app/providers/[id].tsx

auth-modals.tsxAdd TokenAuthModal and unify modal rendering for provider details +159/-1

Add TokenAuthModal and unify modal rendering for provider details

• Introduces TokenAuthModal (React Native) with an instructions link, secure token input, and tokenAuth.connect mutation. Adds ProviderDetailAuthModals wrapper to render credential/token/custom modals based on ProviderDetailModals state.

packages/mobile/app/providers/auth-modals.tsx

index.tsxSupport token auth providers in ProvidersScreen +42/-2

Support token auth providers in ProvidersScreen

• Adds state and handler logic for authType='token' to open TokenAuthModal using server-provided label/instructionsUrl. Propagates tokenAuth metadata into provider cards and invalidates sync.providers after successful token connect.

packages/mobile/app/providers/index.tsx

provider-card.tsxExtend Provider model to include tokenAuth metadata +1/-0

Extend Provider model to include tokenAuth metadata

• Adds optional tokenAuth (label + instructionsUrl) to the Provider interface so UI can render token auth flows described by the server.

packages/mobile/app/providers/provider-card.tsx

styles.tsAdd styling for token modal instruction link text +5/-0

Add styling for token modal instruction link text

• Introduces modalDescription style used by the token auth modal to render the vendor instructions link prompt.

packages/mobile/app/providers/styles.ts

use-provider-detail-actions.tsAdd token auth modal state and actions for provider detail screen +34/-1

Add token auth modal state and actions for provider detail screen

• Extends provider types and modal state to support authType='token'. Adds open/close/success handlers to invalidate sync.providers after token connection.

packages/mobile/app/providers/use-provider-detail-actions.ts

router.tsRegister tokenAuthRouter in the server app router +2/-0

Register tokenAuthRouter in the server app router

• Adds tokenAuthRouter import and registers it under appRouterProcedures to expose tokenAuth.connect via tRPC.

packages/server/src/router.ts

sync.tsExpose tokenAuth metadata in sync provider schemas and responses +9/-0

Expose tokenAuth metadata in sync provider schemas and responses

• Extends sync provider output schema with tokenAuth metadata and plumbs model.tokenAuth through both provider listing paths. Ensures providers without token auth return tokenAuth=null.

packages/server/src/routers/sync.ts

token-auth.tsAdd tokenAuth.connect mutation to save per-user personal tokens +66/-0

Add tokenAuth.connect mutation to save per-user personal tokens

• Implements tokenAuthRouter.connect to locate providers with authSetup.manualToken, exchange/validate the user token, persist the TokenSet, and invalidate sync.providers cache. Maps ProviderRateLimitError and authentication failures to actionable TRPC errors.

packages/server/src/routers/token-auth.ts

DataSourcesAuthModals.tsxAdd web TokenAuthModal for personal token providers +111/-0

Add web TokenAuthModal for personal token providers

• Introduces a new modal to collect a user token, call tokenAuth.connect, show errors, and record telemetry with provider context. Uses locallyReportedErrorMeta to integrate with the app’s query-client error reporting.

packages/web/src/components/DataSourcesAuthModals.tsx

DataSourcesPanel.tsxSupport token auth providers in DataSourcesPanel +36/-1

Support token auth providers in DataSourcesPanel

• Adds tokenAuthProvider state and switches on authType='token' to open TokenAuthModal. Invalidates sync.providers after successful token connect to refresh connection state.

packages/web/src/components/DataSourcesPanel.tsx

DataSourcesSyncTypes.tsAdd tokenAuth to SyncProviderSummary type +1/-0

Add tokenAuth to SyncProviderSummary type

• Extends the web-side provider summary type with tokenAuth metadata so UI can render token connect flows described by the server.

packages/web/src/components/DataSourcesSyncTypes.ts

auth-errors.tsAdd ProviderTokenRejectedError helper for manual token flows +6/-0

Add ProviderTokenRejectedError helper for manual token flows

• Introduces a specialized ProviderAuthError subclass for rejected personal tokens with actionable instructions appended to the error message.

src/providers/auth-errors.ts

provider-auth-policy.tsRecognize manualToken as a first-class ProviderAuthType +4/-5

Recognize manualToken as a first-class ProviderAuthType

• Keeps the legacy server-side credentials set empty and updates auth-type detection to return 'token' when manualToken is present.

src/providers/provider-auth-policy.ts

provider-model.tsExpose tokenAuth label/instructionsUrl via ProviderModel +16/-0

Expose tokenAuth label/instructionsUrl via ProviderModel

• Adds tokenAuth property and extends authSetup inspection logic to detect manualToken and surface its label/instructionsUrl for clients.

src/providers/provider-model.ts

types.tsAdd manualToken to ProviderAuthSetup and extend ProviderAuthType union +9/-1

Add manualToken to ProviderAuthSetup and extend ProviderAuthType union

• Introduces ProviderAuthSetup.manualToken definition and updates ProviderAuthType union and getProviderAuthType to support 'token' auth as a first-class mode.

src/providers/types.ts

Bug fix (5) +198 / -72
sync-helpers.tsStop registering externally gated providers in server registry +0/-9

Stop registering externally gated providers in server registry

• Removes Fitbit, MapMyFitness, Suunto, COROS, Komoot, and Decathlon from the server provider registration list while keeping other providers intact. This prevents production from exposing integrations that require unavailable vendor onboarding.

packages/server/src/routers/sync-helpers.ts

bodyspec.tsMigrate BodySpec to public PKCE OAuth client and Keycloak endpoints +15/-14

Migrate BodySpec to public PKCE OAuth client and Keycloak endpoints

• Replaces env-based client credentials with BodySpec’s published public client ID and OIDC endpoints. Makes authSetup always available and requires a PKCE code verifier during exchangeCode to complete the flow securely.

src/providers/bodyspec.ts

cycling-analytics.tsAdd manual token auth fallback and fix OAuth scopes for Cycling Analytics +41/-5

Add manual token auth fallback and fix OAuth scopes for Cycling Analytics

• Defines OAuth scopes/read_rides and adds a manualToken exchange that validates the bearer token against /me/rides. If OAuth env vars are missing, the provider still supports self-service connection via personal token.

src/providers/cycling-analytics.ts

ultrahuman.tsReplace Ultrahuman env auth with per-user personal token flow +56/-14

Replace Ultrahuman env auth with per-user personal token flow

• Adds authSetup.manualToken that validates tokens via partner daily_metrics and returns a long-lived TokenSet. Removes dependency on deployment env vars and updates sync to require stored tokens, using token-owner endpoints without email.

src/providers/ultrahuman.ts

wger.tsSwitch Wger to JWT refresh-token manual auth with rotation and revocation handling +86/-30

Switch Wger to JWT refresh-token manual auth with rotation and revocation handling

• Removes OAuth config and implements manualToken exchange via /token/refresh, parsing JWT expiration with zod validation. Updates token resolution to load/save/rotate tokens, delete revoked tokens, and surface actionable errors when tokens are missing or revoked.

src/providers/wger.ts

Tests (20) +808 / -325
[id].test.tsxAdd ProviderDetailScreen coverage for token providers +40/-0

Add ProviderDetailScreen coverage for token providers

• Mocks tokenAuth.connect and introduces a token-auth provider fixture. Adds a test asserting the token modal opens and submits the token connection mutation with providerId/token.

packages/mobile/app/providers/[id].test.tsx

auth-modals.test.tsxTest TokenAuthModal behavior and token-safe telemetry +70/-1

Test TokenAuthModal behavior and token-safe telemetry

• Adds TokenAuthModal tests verifying successful connect calls tokenAuth.connect and that failures capture provider context without leaking tokens. Updates mocks to include tokenAuth.connect.

packages/mobile/app/providers/auth-modals.test.tsx

index.test.tsxTest ProvidersScreen token auth flow wiring +59/-0

Test ProvidersScreen token auth flow wiring

• Adds token-provider fixtures and asserts the server-described personal token flow opens and submits the token connection mutation. Extends tRPC mocks with tokenAuth.connect.

packages/mobile/app/providers/index.test.tsx

router.test.tsInclude tokenAuth router in appRouter tests +2/-0

Include tokenAuth router in appRouter tests

• Mocks token-auth router and asserts appRouter exposes the tokenAuth procedure namespace.

packages/server/src/router.test.ts

sync-registration.test.tsAssert production registration includes self-service providers only +16/-0

Assert production registration includes self-service providers only

• Adds a test confirming self-service providers are registered while externally gated provider IDs are omitted.

packages/server/src/routers/sync-registration.test.ts

sync.test.tsCover tokenAuth metadata in sync.providers response +40/-0

Cover tokenAuth metadata in sync.providers response

• Adds a test ensuring providers with authSetup.manualToken are returned with authType='token' and tokenAuth label/instructionsUrl.

packages/server/src/routers/sync.test.ts

token-auth.test.tsAdd tests for tokenAuth.connect success and error mappings +196/-0

Add tests for tokenAuth.connect success and error mappings

• Introduces a comprehensive suite covering token exchange/save behavior, unknown provider handling, non-token providers, auth failures mapped to BAD_REQUEST, and provider rate limits mapped to TOO_MANY_REQUESTS.

packages/server/src/routers/token-auth.test.ts

DataSourcesAuthModals.stories.tsxAdd Storybook story for TokenAuthModal +19/-1

Add Storybook story for TokenAuthModal

• Imports TokenAuthModal and adds a PersonalToken story rendering it with Wger example metadata for UI review.

packages/web/src/components/DataSourcesAuthModals.stories.tsx

DataSourcesAuthModals.test.tsxTest web TokenAuthModal connect flow and token-safe telemetry +65/-0

Test web TokenAuthModal connect flow and token-safe telemetry

• Mocks tokenAuth.connect and adds tests verifying token connection success and error telemetry includes provider context only. Asserts the instructions link is rendered and that tokens never appear in telemetry payloads.

packages/web/src/components/DataSourcesAuthModals.test.tsx

DataSourcesPanel.test.tsxVerify DataSourcesPanel opens token auth with server metadata +46/-0

Verify DataSourcesPanel opens token auth with server metadata

• Extends auth modal mocks to include TokenAuthModal and adds a test asserting token providers open the token auth flow using server-provided label/instructionsUrl.

packages/web/src/components/DataSourcesPanel.test.tsx

provider-registration.test.tsUpdate worker/provider-registration tests for production registry +1/-25

Update worker/provider-registration tests for production registry

• Removes gated providers from the mocked provider list and expectations. Renames the test to reflect registering production providers only.

src/jobs/provider-registration.test.ts

automated-login.test.tsUpdate Ultrahuman tests to reflect per-user token auth +3/-26

Update Ultrahuman tests to reflect per-user token auth

• Removes env-var validation expectations and asserts Ultrahuman now provides a manualToken authSetup. Reinforces that deployment-wide user credentials are no longer required.

src/providers/automated-login.test.ts

bodyspec.test.tsUpdate BodySpec tests for public PKCE client and verifier handling +48/-57

Update BodySpec tests for public PKCE client and verifier handling

• Switches BodySpec tests to validate the new OIDC endpoints, PKCE configuration, and absence of a client secret. Adds coverage for exchanging callback code with a stored PKCE verifier and for failing when verifier is missing.

src/providers/bodyspec.test.ts

cycling-analytics.test.tsTest Cycling Analytics personal token flow and corrected OAuth scopes +21/-16

Test Cycling Analytics personal token flow and corrected OAuth scopes

• Updates OAuth config expectations to include read_rides scope with comma separator. Adds a test that when OAuth env vars are absent, authSetup exposes manualToken and validates it via a lightweight API call.

src/providers/cycling-analytics.test.ts

oauth-providers.test.tsRemove Wger OAuth tests now that Wger uses token auth +0/-131

Remove Wger OAuth tests now that Wger uses token auth

• Deletes Wger OAuth config/provider tests from the OAuth provider suite, reflecting the shift to JWT refresh token-based self-service auth.

src/providers/oauth-providers.test.ts

provider-auth-policy-unit.test.tsEnforce no legacy server-side user credential exemptions; accept manualToken +24/-9

Enforce no legacy server-side user credential exemptions; accept manualToken

• Updates policy tests so Ultrahuman is no longer exempt from per-user connect requirements and asserts the legacy exemption set is empty. Adds coverage that providers with manualToken are compliant.

src/providers/provider-auth-policy-unit.test.ts

provider-model.test.tsTest ProviderModel tokenAuth metadata exposure +24/-0

Test ProviderModel tokenAuth metadata exposure

• Adds a test asserting ProviderModel derives authType='token' and exposes tokenAuth label/instructionsUrl when authSetup.manualToken is defined.

src/providers/provider-model.test.ts

types.test.tsExtend getProviderAuthType tests for token auth and Ultrahuman +19/-2

Extend getProviderAuthType tests for token auth and Ultrahuman

• Adds coverage that manualToken maps to ProviderAuthType 'token' and updates Ultrahuman expectations to return 'token' rather than 'none'.

src/providers/types.test.ts

ultrahuman.test.tsUpdate Ultrahuman tests for token-only auth and request shape +31/-8

Update Ultrahuman tests for token-only auth and request shape

• Adds coverage that the client omits email query when syncing token-owner data. Validates manualToken exchangeToken behavior and removes env-var based expectations.

src/providers/ultrahuman.test.ts

wger.test.tsRewrite Wger tests for JWT refresh-token auth and rotation +84/-49

Rewrite Wger tests for JWT refresh-token auth and rotation

• Replaces Wger OAuth tests with manualToken exchange tests that parse JWT exp and handle refresh token rotation. Adds a sync test verifying expired tokens are refreshed and persisted before fetching data.

src/providers/wger.test.ts

Documentation (4) +81 / -25
README.mdDocument production provider inventory and gated integrations +35/-15

Document production provider inventory and gated integrations

• Updates provider configuration guidance to distinguish app OAuth credentials vs per-user tokens. Adds a section listing providers not registered in production with vendor-access rationale and links. Refreshes the provider table to reflect BodySpec PKCE public client and token-based providers.

README.md

adding-a-provider.mdClarify per-user auth policy and add manual token flow guidance +27/-1

Clarify per-user auth policy and add manual token flow guidance

• Removes prior legacy exemptions for external-account providers and documents 'manualToken' as a supported connect flow. Adds an example ProviderAuthSetup.manualToken implementation and explains token validation/storage behavior.

docs/adding-a-provider.md

bodyspec.mdUpdate BodySpec auth docs to PKCE public client and new endpoints +10/-6

Update BodySpec auth docs to PKCE public client and new endpoints

• Replaces client-credentials guidance with BodySpec’s current authorization-code + PKCE flow using the public client. Updates authorize/token URLs, scopes, and documents that no BodySpec env vars are required.

docs/bodyspec.md

README.mdRefresh provider auth capabilities and supported provider list +9/-3

Refresh provider auth capabilities and supported provider list

• Documents new authType 'token' and updates the supported provider list to exclude non-registered gated providers. Adds explicit note that certain provider modules remain for historical IDs but are not loaded in production.

src/providers/README.md

Other (1) +0 / -9
provider-registration.tsStop registering externally gated providers in worker registry +0/-9

Stop registering externally gated providers in worker registry

• Removes Fitbit, MapMyFitness, Suunto, COROS, Komoot, and Decathlon from the worker-side provider registration list to match production availability constraints.

src/jobs/provider-registration.ts

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Mobile Preview

Scan to open on device:

QR code for dofek://preview/pr-2045

Channel pr-2045
Deep Link dofek://preview/pr-2045
Commit 36c0f30

To test on device:

  1. Build and install the preview client: PREVIEW_CHANNEL=pr-2045 pnpm expo prebuild --clean -p ios
  2. Or tap deep link on an existing preview build: dofek://preview/pr-2045

Each PR gets its own channel. Build a preview client with PREVIEW_CHANNEL=pr-{N} to test.

Remove workers for unregistered providers and use the canonical
Cycling Analytics ID so token-connected sync jobs are consumed.

Refs #1996
@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@qodo-code-review

qodo-code-review Bot commented Jul 26, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 227 rules

Grey Divider


Remediation recommended

1. Token modal close race ✓ Resolved 🐞 Bug ☼ Reliability
Description
TokenAuthModal (mobile + web) allows closing/unmounting while connectMutation is in-flight; the
async handler still calls setError/setLoading in catch/finally after the modal is removed. This can
cause unnecessary post-unmount state updates and makes close/cancel semantics undefined during a
pending connect.
Code

packages/mobile/app/providers/auth-modals.tsx[R147-186]

+  const handleConnect = useCallback(async () => {
+    setError("");
+    setLoading(true);
+    try {
+      await connectMutation.mutateAsync({ providerId, token });
+      onSuccess();
+    } catch (caught: unknown) {
+      captureException(caught, {
+        source: "provider-token-auth-connect",
+        providerId,
+      });
+      setError(caught instanceof Error ? caught.message : "Token connection failed");
+    } finally {
+      setLoading(false);
+    }
+  }, [connectMutation, onSuccess, providerId, token]);
+
+  const openInstructions = useCallback(() => {
+    void Linking.openURL(instructionsUrl).catch((caught: unknown) => {
+      captureException(caught, {
+        source: "provider-token-auth-instructions",
+        providerId,
+      });
+      setError(caught instanceof Error ? caught.message : "Could not open token instructions");
+    });
+  }, [instructionsUrl, providerId]);
+
+  return (
+    <Modal visible transparent animationType="fade" onRequestClose={onClose}>
+      <View style={styles.modalOverlay}>
+        <View style={styles.modalContent}>
+          <View style={styles.modalHeader}>
+            <Text style={styles.modalTitle}>Connect {providerName}</Text>
+            <TouchableOpacity
+              onPress={onClose}
+              activeOpacity={0.7}
+              accessibilityRole="button"
+              accessibilityLabel={`Close ${providerName} connection`}
+            >
+              <Text style={styles.modalClose}>{"\u00D7"}</Text>
Relevance

⭐⭐⭐ High

Team previously accepted guarding modal async setState after unmount; same close-while-loading race
pattern.

PR-#729

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The modal can be unmounted via onClose while the async connect handler still updates component state
in catch/finally; the parents remove the modal by setting state to null.

packages/mobile/app/providers/auth-modals.tsx[122-235]
packages/mobile/app/providers/index.tsx[921-933]
packages/web/src/components/DataSourcesAuthModals.tsx[128-235]
packages/web/src/components/DataSourcesPanel.tsx[532-544]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`TokenAuthModal` can be dismissed while `mutateAsync()` is still running. Because the modal unmounts on close/success, `catch`/`finally` can still call `setError`/`setLoading` after unmount.

### Issue Context
This is the same lifecycle hazard previously handled in other modals (e.g., Garmin) by gating close when `loading` is true.

### Fix Focus Areas
- packages/mobile/app/providers/auth-modals.tsx[147-235]
- packages/mobile/app/providers/index.tsx[921-933]
- packages/web/src/components/DataSourcesAuthModals.tsx[128-235]
- packages/web/src/components/DataSourcesPanel.tsx[532-544]

### Implementation notes
- Add a `handleClose` that no-ops when `loading` is true, and use it for both the close button and `onRequestClose` (RN) / modal `onClose` (web).
- Alternatively (or additionally), add an `isMounted`/`cancelled` ref guard inside `handleConnect` before calling `setError`/`setLoading`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. tokenAuth.connect missing output schema ✓ Resolved 📘 Rule violation ≡ Correctness
Description
The new tokenAuth.connect mutation defines an input Zod schema but does not define an
.output(...) Zod schema, violating the requirement that all tRPC procedures validate outputs.
Code

packages/server/src/routers/token-auth.ts[R12-21]

+export const tokenAuthRouter = router({
+  connect: protectedProcedure
+    .input(
+      z.object({
+        providerId: z.string(),
+        token: z.string().trim().min(1, "Token is required"),
+      }),
+    )
+    .mutation(async ({ ctx, input }) => {
+      await ensureProvidersRegistered();
Relevance

⭐⭐⭐ High

Repo reviewers enforce tRPC output validation; multiple accepted requests added missing .output
schemas.

PR-#1123
PR-#2015

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The checklist requires every tRPC procedure to declare both .input(...) and .output(...) Zod
schemas. The new tokenAuth.connect mutation includes .input(...) but no .output(...) before
.mutation(...).

Rule 722090: Define Zod schemas for all tRPC procedure inputs and outputs
packages/server/src/routers/token-auth.ts[12-21]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`tRPC` procedure `tokenAuth.connect` is missing an `.output(...)` Zod schema.

## Issue Context
The procedure returns `{ success: true }` but does not validate that shape at runtime.

## Fix Focus Areas
- packages/server/src/routers/token-auth.ts[12-21]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. tokenAuth.connect rethrows raw error ✓ Resolved 📘 Rule violation ≡ Correctness
Description
tokenAuth.connect uses throw error; inside the procedure, which can surface non-TRPCError
exceptions to clients and break the required semantic error-code contract.
Code

packages/server/src/routers/token-auth.ts[R42-57]

+      } catch (error: unknown) {
+        if (error instanceof ProviderRateLimitError) {
+          throw new TRPCError({
+            code: "TOO_MANY_REQUESTS",
+            message: error.message,
+            cause: error,
+          });
+        }
+        if (authFailureReasonFromError(error)) {
+          throw new TRPCError({
+            code: "BAD_REQUEST",
+            message: error instanceof Error ? error.message : "Provider rejected this token.",
+            cause: error,
+          });
+        }
+        throw error;
Relevance

⭐⭐⭐ High

They’ve accepted replacing raw throws with TRPCError to preserve client error-code contract.

PR-#1960
PR-#1110

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The checklist requires all errors intended to reach clients from tRPC procedures to be thrown as
TRPCError. The new router’s catch block ends with throw error;, which can propagate a
non-TRPCError out of the procedure.

Rule 722038: Use TRPCError with semantic error codes for all tRPC procedure failures
packages/server/src/routers/token-auth.ts[42-57]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`packages/server/src/routers/token-auth.ts` rethrows unknown errors with `throw error;` from inside a tRPC procedure. Compliance requires mapping/wrapping procedure errors into `TRPCError` with an appropriate `code`.

## Issue Context
This occurs in the `connect` mutation when `exchangeToken()` throws an unexpected error type that is neither a `ProviderRateLimitError` nor recognized by `authFailureReasonFromError`.

## Fix Focus Areas
- packages/server/src/routers/token-auth.ts[42-57]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. Unsafe token instruction URLs ✓ Resolved 🐞 Bug ⛨ Security
Description
ProviderModel forwards manualToken.instructionsUrl as an unchecked string, but the server
enforces it as z.url() and the clients open it directly (<a href> / Linking.openURL). A
malformed URL can break sync.providers response validation, and non-HTTP(S) schemes can become a
security footgun if ever introduced in provider configs.
Code

src/providers/provider-model.ts[R44-57]

+          } else if (
+            "manualToken" in setup &&
+            typeof setup.manualToken === "object" &&
+            setup.manualToken !== null &&
+            "label" in setup.manualToken &&
+            typeof setup.manualToken.label === "string" &&
+            "instructionsUrl" in setup.manualToken &&
+            typeof setup.manualToken.instructionsUrl === "string"
+          ) {
+            authType = "token";
+            this.tokenAuth = {
+              label: setup.manualToken.label,
+              instructionsUrl: setup.manualToken.instructionsUrl,
+            };
Relevance

⭐⭐⭐ High

They commonly accept URL hardening/validation changes; aligns with server z.url() and safe client
opening.

PR-#1364
PR-#1422

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The model exposes instructionsUrl without validating it as a URL, while the server schema requires
z.url() and the clients open the URL directly.

src/providers/provider-model.ts[35-58]
packages/server/src/routers/sync.ts[44-60]
packages/mobile/app/providers/auth-modals.tsx[164-172]
packages/web/src/components/DataSourcesAuthModals.tsx[193-203]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`manualToken.instructionsUrl` is treated as an arbitrary string in `ProviderModel`, yet:
- server output schema requires it to be a URL (`z.url()`), so an invalid string can make `sync.providers` fail validation;
- clients open it directly.

### Issue Context
Today all providers appear to supply HTTPS URLs, but the type/system does not enforce it and `z.url()` does not enforce an allowlist of protocols.

### Fix Focus Areas
- src/providers/provider-model.ts[35-58]
- packages/server/src/routers/sync.ts[44-60]
- packages/mobile/app/providers/auth-modals.tsx[164-172]
- packages/web/src/components/DataSourcesAuthModals.tsx[193-203]

### Implementation notes
- In `ProviderModel`, only set `authType="token"`/`tokenAuth` if `instructionsUrl` parses via `new URL(...)` AND `protocol` is `https:` (optionally allow `http:` for local dev).
- In `sync.ts`, tighten the schema to an allowlist, e.g. `z.string().url().refine(u => new URL(u).protocol === 'https:' || (dev && 'http:'), '...')`.
- Consider a shared helper to validate/normalize instruction URLs for both server and clients.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. apiBaseUrl can be nulled ✓ Resolved 🐞 Bug ≡ Correctness
Description
tokenAuthRouter.connect passes the optional setup.apiBaseUrl into ensureProvider, which
upserts api_base_url with the provided value. If a manual-token provider omits apiBaseUrl, this
path can overwrite an existing non-null provider api_base_url with NULL.
Code

packages/server/src/routers/token-auth.ts[R60-62]

+      await ensureProvider(ctx.db, provider.id, provider.name, setup.apiBaseUrl, ctx.userId);
+      await saveTokens(ctx.db, provider.id, tokens, ctx.userId);
+      await queryCache.invalidateByPrefix(`${ctx.userId}:sync.providers`);
Relevance

⭐⭐ Medium

Potential data loss by nulling api_base_url, but no close historical precedent on ensureProvider
null-upserts.

PR-#1359
PR-#1578

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The router forwards an optional apiBaseUrl, and ensureProvider explicitly converts undefined to null
and upserts api_base_url with the excluded value.

packages/server/src/routers/token-auth.ts[20-64]
src/db/tokens.ts[40-56]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The token-auth connect flow persists provider metadata via `ensureProvider(..., setup.apiBaseUrl, ...)`. Because `apiBaseUrl` is optional, missing values can be written as NULL and overwrite existing metadata.

### Issue Context
`ensureProvider` uses `apiBaseUrl ?? null` and updates `api_base_url` on conflict, so `undefined` becomes NULL.

### Fix Focus Areas
- packages/server/src/routers/token-auth.ts[20-65]
- src/db/tokens.ts[40-68]

### Implementation notes
Choose one:
1) **Enforce required apiBaseUrl for manualToken**: in `tokenAuthRouter`, if `setup.apiBaseUrl` is missing, throw a `TRPCError` (e.g. `INTERNAL_SERVER_ERROR`) indicating provider misconfiguration.
2) **Preserve existing api_base_url on upsert**: in `ensureProvider` SQL, update `api_base_url` with `COALESCE(EXCLUDED.api_base_url, fitness.provider.api_base_url)` so NULL does not overwrite.

(Option 2 is more robust across all call sites.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread packages/server/src/routers/token-auth.ts Outdated
Comment thread packages/server/src/routers/token-auth.ts
Comment thread packages/mobile/app/providers/auth-modals.tsx
Comment thread src/providers/provider-model.ts Outdated
Comment thread packages/server/src/routers/token-auth.ts Outdated
@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Storybook previews for 36c0f303 are ready:

This comment updates automatically on each PR push.

Asherlc added 2 commits July 26, 2026 10:28
Make connection persistence atomic and classify provider outages and revoked personal tokens so clients reach retry or reconnect states.
Align Cycling Analytics queue and onboarding IDs with provider.id, and surface restored BodySpec in the onboarding guide.
@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

Assert token rejection branches, JWT causes, expiry boundaries, provider selection, and atomic persistence so authentication regressions cannot silently pass mutation CI. Share the HTTPS token metadata schema across provider and router boundaries.
@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/providers/provider-auth-policy.ts (1)

29-35: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Third copy of auth-type derivation, and it has already drifted.

getAuthTypeFromSetup() requires oauthConfig && exchangeCode for "oauth", while getProviderAuthType() in src/providers/types.ts:316-329 only checks oauthConfig, and ProviderModel (src/providers/provider-model.ts:48-66) implements a third variant using Reflect.get. A provider with oauthConfig but no exchangeCode plus a manualToken is classified "token" here and "oauth" there. Collapse these onto the shared getProviderAuthType()/setup-derivation helper so policy checks and client-facing metadata cannot disagree.

As per coding guidelines: "Use domain-driven modeling, focused modules, SOLID principles" and DRY — duplicate logic across modules is a code smell.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/providers/provider-auth-policy.ts` around lines 29 - 35, Replace the
local getAuthTypeFromSetup derivation with the shared
getProviderAuthType/setup-derivation helper used by ProviderModel, preserving
the canonical precedence and oauthConfig-only behavior. Update policy callers to
use that shared result so auth policy and provider metadata cannot classify the
same setup differently.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/adding-a-provider.md`:
- Line 18: Update the manualToken provider example around the Personal token
entry and its token-handling flow to avoid presenting refresh tokens as access
tokens. Narrow the example wording and supported input to personal API/access
tokens, or implement a provider-specific refresh-token exchange that preserves
the returned TokenSet fields instead of discarding refreshToken and assigning a
fixed expiry.

In `@packages/mobile/app/providers/use-provider-detail-actions.ts`:
- Around line 242-251: The token-provider action silently does nothing when
tokenAuth metadata is missing. In
packages/mobile/app/providers/use-provider-detail-actions.ts lines 242-251,
update the token case to add an else branch that sets syncMessage (or the
established equivalent error state) and calls captureException; in
packages/mobile/app/providers/index.tsx lines 601-610, add the corresponding
failure branch that surfaces an error consistently with the OAuth handling and
calls captureException.

In `@packages/mobile/components/ProviderGuide.test.tsx`:
- Around line 8-22: Remove the inline react-native and expo-router vi.mock
declarations from ProviderGuide.test.tsx, and rely on the shared mocks
configured by packages/mobile/test-setup.ts. Keep the test’s existing behavior
and imports unchanged.

In `@packages/server/src/routers/sync-registration.test.ts`:
- Around line 176-178: Update the expected provider ID in the registeredIds
assertion within the sync registration test to use CyclingAnalyticsProvider’s
canonical value, cycling_analytics, while preserving the other expected IDs.

In `@packages/server/src/routers/token-auth.ts`:
- Around line 81-101: Wrap the queryCache.invalidateByPrefix call after
connectProviderWithTokens in a try/catch; log the cache-invalidation error and
report it to Sentry via captureException(error), then continue returning {
success: true } because persistence already succeeded. Keep the existing
connection-save error handling unchanged.

In `@packages/web/src/components/DataSourcesAuthModals.stories.tsx`:
- Around line 85-97: Add Storybook coverage for TokenAuthModal's error and
loading states alongside PersonalToken. Configure the existing tRPC mock-link
pattern in DataSourcesAuthModals.stories.tsx so tokenAuth.connect returns an
error for PersonalTokenError and remains pending for a separate loading story,
while preserving the current provider props and callbacks.

In `@packages/web/src/components/DataSourcesAuthModals.tsx`:
- Around line 210-212: Update the conditional error message rendered by
DataSourcesAuthModals to include role="alert" on its existing div, preserving
the current styling and error text so screen readers announce token-connection
failures.

In `@packages/web/src/components/DataSourcesPanel.tsx`:
- Around line 341-350: Update the "token" branch in the provider handling switch
to fail fast with an explicit error when p.tokenAuth is missing, instead of
silently falling through to break. Preserve the existing setTokenAuthProvider
behavior when tokenAuth is present.

In `@src/providers/automated-login.test.ts`:
- Around line 341-351: Remove the duplicated Ultrahuman client token-rejection
and manualToken label assertions from automated-login.test.ts, and place them in
ultrahuman.test.ts alongside the existing 404 coverage. Keep the 401/403 status
cases and expected authentication_failed message intact, while leaving
automated-login.test.ts focused only on automated-login responsibilities.

In `@src/providers/cycling-analytics.ts`:
- Around line 146-192: The token resolution flow in `#resolveTokens` must support
manual Cycling Analytics tokens when OAuth configuration is unavailable. Follow
the loadTokens() and manual-token refresh approach used by wger.ts, and stop
routing these tokens through resolveOAuthTokens with
cyclingAnalyticsOAuthConfig(); preserve direct use of unexpired stored tokens
and ensure non-expiring manual tokens remain usable without client credentials.

In `@src/providers/types.ts`:
- Line 328: Update the auth classification logic in src/providers/types.ts at
line 328 to define and document explicit precedence for providers exposing both
oauthConfig and manualToken, ensuring the intended personal-token path remains
available. In src/providers/provider-model.ts at lines 57-64, always parse and
populate tokenAuth from manualToken independently of the winning authType so
clients still receive the token label and instructions URL.

In `@src/providers/ultrahuman.test.ts`:
- Around line 147-169: Add an explicit call-count assertion after each
getDailyMetrics await in the delegated-email and token-owner tests, verifying
mockFetch was called exactly once; apply the same correction to the analogous
test around the line 185-191 case. Keep the existing URL assertions unchanged.

In `@src/providers/ultrahuman.ts`:
- Around line 205-210: Update the token object returned by the non-expiring
personal-token flow to set expiresAt to null instead of a far-future sentinel
date. Apply the same change to the corresponding pattern in
cycling-analytics.ts, preserving the existing accessToken, refreshToken, and
scopes values.
- Around line 228-234: Update the Ultrahuman sync token-loading call in the
provider’s sync flow to pass options.userId as the third argument to loadTokens,
and fail fast when options.userId is missing before loading credentials;
preserve the existing missing-token error and client construction behavior.
- Around line 140-145: Update the response-status check in the Ultrahuman
request flow to throw ProviderTokenRejectedError only for 401 and 403; remove
404 from this token-rejection branch so a no-data daily_metrics response does
not mark the connection for reconnect.

In `@src/providers/wger-sync.integration.test.ts`:
- Around line 57-65: Extract the duplicated fakeJwt helper into
src/providers/test-helpers.ts and export it. In
src/providers/wger-sync.integration.test.ts lines 57-65 and
src/providers/wger.test.ts lines 84-90, remove the local fakeJwt definitions and
import the shared helper from ./test-helpers.ts.

---

Outside diff comments:
In `@src/providers/provider-auth-policy.ts`:
- Around line 29-35: Replace the local getAuthTypeFromSetup derivation with the
shared getProviderAuthType/setup-derivation helper used by ProviderModel,
preserving the canonical precedence and oauthConfig-only behavior. Update policy
callers to use that shared result so auth policy and provider metadata cannot
classify the same setup differently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c786b05f-c5fb-44d3-a49a-9cedb60a24c9

📥 Commits

Reviewing files that changed from the base of the PR and between d627cd8 and e218887.

📒 Files selected for processing (57)
  • README.md
  • docs/adding-a-provider.md
  • docs/bodyspec.md
  • packages/mobile/app/providers/[id].test.tsx
  • packages/mobile/app/providers/[id].tsx
  • packages/mobile/app/providers/auth-modals.test.tsx
  • packages/mobile/app/providers/auth-modals.tsx
  • packages/mobile/app/providers/index.test.tsx
  • packages/mobile/app/providers/index.tsx
  • packages/mobile/app/providers/provider-card.tsx
  • packages/mobile/app/providers/styles.ts
  • packages/mobile/app/providers/use-provider-detail-actions.ts
  • packages/mobile/components/ProviderGuide.test.tsx
  • packages/onboarding/src/provider-guide.test.ts
  • packages/onboarding/src/provider-guide.ts
  • packages/server/src/router.test.ts
  • packages/server/src/router.ts
  • packages/server/src/routers/sync-helpers.ts
  • packages/server/src/routers/sync-registration.test.ts
  • packages/server/src/routers/sync.test.ts
  • packages/server/src/routers/sync.ts
  • packages/server/src/routers/token-auth.test.ts
  • packages/server/src/routers/token-auth.ts
  • packages/web/src/components/DataSourcesAuthModals.stories.tsx
  • packages/web/src/components/DataSourcesAuthModals.test.tsx
  • packages/web/src/components/DataSourcesAuthModals.tsx
  • packages/web/src/components/DataSourcesPanel.test.tsx
  • packages/web/src/components/DataSourcesPanel.tsx
  • packages/web/src/components/DataSourcesSyncTypes.ts
  • packages/web/src/components/ProviderGuide.test.tsx
  • src/db/tokens.integration.test.ts
  • src/db/tokens.test.ts
  • src/db/tokens.ts
  • src/jobs/provider-queue-config.test.ts
  • src/jobs/provider-queue-config.ts
  • src/jobs/provider-registration.test.ts
  • src/jobs/provider-registration.ts
  • src/providers/README.md
  • src/providers/auth-errors.ts
  • src/providers/automated-login.test.ts
  • src/providers/bodyspec.test.ts
  • src/providers/bodyspec.ts
  • src/providers/cycling-analytics.test.ts
  • src/providers/cycling-analytics.ts
  • src/providers/oauth-providers.test.ts
  • src/providers/provider-auth-policy-unit.test.ts
  • src/providers/provider-auth-policy.ts
  • src/providers/provider-model.test.ts
  • src/providers/provider-model.ts
  • src/providers/types.test.ts
  • src/providers/types.ts
  • src/providers/ultrahuman-sync.integration.test.ts
  • src/providers/ultrahuman.test.ts
  • src/providers/ultrahuman.ts
  • src/providers/wger-sync.integration.test.ts
  • src/providers/wger.test.ts
  • src/providers/wger.ts
💤 Files with no reviewable changes (4)
  • src/jobs/provider-queue-config.ts
  • packages/server/src/routers/sync-helpers.ts
  • src/providers/oauth-providers.test.ts
  • src/jobs/provider-registration.ts

Comment thread docs/adding-a-provider.md Outdated
Comment thread packages/mobile/app/providers/use-provider-detail-actions.ts
Comment thread packages/mobile/components/ProviderGuide.test.tsx Outdated
Comment thread packages/server/src/routers/sync-registration.test.ts
Comment thread packages/server/src/routers/token-auth.ts
Comment thread src/providers/ultrahuman.test.ts
Comment thread src/providers/ultrahuman.ts Outdated
Comment thread src/providers/ultrahuman.ts
Comment thread src/providers/ultrahuman.ts Outdated
Comment thread src/providers/wger-sync.integration.test.ts Outdated
@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@Asherlc

Asherlc commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

Follow-up to the CodeRabbit review: #2045 (review)

All 16 inline findings now have explicit replies and resolved threads. The outside-diff finding is also fixed in 5e5504c: provider-auth-policy now uses the shared setup classifier, while ProviderModel and public auth metadata use the same canonical precedence and complete-OAuth contract.

The two suggestions intentionally not applied are documented in their threads with contract/runtime evidence:

  • Cycling Analytics already returns an unexpired stored personal token before OAuth configuration lookup; a no-env executable sync regression now proves it.
  • expiresAt remains a required Date in both TokenSet and the persisted schema, so changing it to null would require a separate broad migration.

Local validation at this exact head is green: full unit/mobile suite (13,804 passed), root/server/web/mobile typechecks, full lint, Storybook build, real-Compose Wger/Ultrahuman integration tests (18 passed), and targeted mutation testing (51/51 killed, 100%).

@Asherlc
Asherlc merged commit f5b951f into main Jul 26, 2026
144 checks passed
@Asherlc
Asherlc deleted the codex/issue-1996 branch July 26, 2026 19:25
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.

Ten registered providers are hidden in production because all required credentials are absent

2 participants