Skip to content

minimed: replace broken SSO auth with OAuth2 PKCE + MongoDB token storage - #54

Open
LeFrenchGuy wants to merge 9 commits into
nightscout:devfrom
LeFrenchGuy:main
Open

minimed: replace broken SSO auth with OAuth2 PKCE + MongoDB token storage#54
LeFrenchGuy wants to merge 9 commits into
nightscout:devfrom
LeFrenchGuy:main

Conversation

@LeFrenchGuy

Copy link
Copy Markdown

Background

The previous Minimed CareLink authentication relied on posting credentials directly to /patient/sso/login, Medtronic's old proprietary SSO endpoint. That endpoint no longer works: Medtronic has migrated CareLink to an Auth0-based OAuth2 infrastructure and direct credential POSTs are consistently blocked by CAPTCHA in server-side environments.

This PR replaces the broken auth mechanism entirely with a proper OAuth2 PKCE flow, ported and adapted from carelink-bridge, which already implements this successfully in production.


What's changing

1. OAuth2 PKCE authentication (lib/sources/minimedcarelink/auth.js)

Implements the full CareLink OAuth2 flow:

  • Discovery: dynamically fetches Auth0 SSO config from the CareLink discovery endpoint (/connect/carepartner/v13/discover/android/3.6), supports both US and EU regions
  • PKCE: generates a cryptographically random code_verifier + code_challenge pair (SHA-256, base64url) per RFC 7636
  • Token exchange: POSTs the authorization code + PKCE verifier to the token endpoint
  • Code extraction: handles both standard https:// redirect URLs and CareLink's custom scheme (com.medtronic.carepartner:/sso?code=...)

2. Token lifecycle management (lib/sources/minimedcarelink/oauth.js)

Handles everything after initial login:

  • Load, validate and save logindata from file path or inline JSON
  • JWT expiry check: returns true if less than 1 minute of validity remains
  • Token refresh: POSTs grant_type=refresh_token, handles token rotation
  • Region detection from token_url (.minimed.com vs .minimed.eu)

3. MongoDB-backed token persistence (lib/sources/minimedcarelink/token-store.js)

Stores OAuth tokens in the Nightscout MongoDB database (connect_tokens collection):

  • Tokens survive container restarts and redeployments without any manual intervention
  • Tokens are saved via upsert and loaded automatically when the poller starts
  • If MONGO_CONNECTION is not set, fails with a clear actionable error

New environment variable:

-`MONGO_CONNECTION`: reuses the same connection string as Nightscout itself, no duplication needed. Also accepts `CONNECT_MONGO_CONNECTION` for users who prefer the `CONNECT_` prefix convention.

4. nightscout-connect login CLI command (commands/login.js)

A new login subcommand handles the one-time authentication bootstrap. Unlike carelink-bridge which uses Puppeteer to automate the browser entirely, this implementation avoids that ~300MB dependency by guiding the user through a short manual step instead.

nightscout-connect login             # EU by default
nightscout-connect login --region us

The flow:

  1. If valid tokens already exist in MongoDB, the command exits early, nothing to do
  2. The CareLink authorize URL is opened automatically in your system browser
  3. Log in with your CareLink credentials. If CAPTCHA appears, complete it as normal
  4. After login, the page will fail to load: this is expected. CareLink redirects to a custom app scheme (com.medtronic.carepartner://) that the browser can't handle
  5. Open DevTools (F12) → Network tab → find the blocked request to /authorize/resume → click it → Response Headers → copy the Location value (starts with com.medtronic.carepartner:/sso?code=...)
  6. Paste that URL back into the terminal when prompted
  7. Tokens are exchanged and saved to MongoDB. The command prints the token expiry and detected region on success

This is a one-time operation. After that the poller handles everything automatically, token refresh included, until the refresh token itself expires (~30 days with CareLink), at which point you run login again.

5. Leveled logger (lib/logger.js)

Controlled by LOG_LEVEL (debug | info | warn | error | silent, default: info). Also used to silence the noisy xstate actions.log() output in the poller, fetch, cycle, and session machines that was firing on every polling cycle.

6. Removal of username/password credential auth

CONNECT_CARELINK_USERNAME and CONNECT_CARELINK_PASSWORD are no longer used. The supported authentication path is now:

  1. Run nightscout-connect login once → tokens saved to MongoDB
  2. Poller loads tokens from MongoDB on startup
  3. Poller refreshes tokens automatically via refresh_token (no user interaction, no CAPTCHA)
  4. If the refresh token ever expires (~30 days), re-run nightscout-connect login

Companion: Nightscout admin UI integration (upcoming)

A companion PR to cgm-remote-monitor is in the works that will expose the full login flow directly from the Nightscout /admin page, no CLI access needed. The plan:

  • A CareLink Authentication panel with a "Start Login" button that opens the Auth0 authorize URL in a new tab
  • A paste field for the callback URL after login
  • A live status indicator showing token validity and expiry countdown
  • On submit, tokens are exchanged and saved directly to MongoDB via the Nightscout API

This will make re-authentication accessible to users who manage their Nightscout instance through the web UI rather than the command line. For now, the nightscout-connect login CLI command is the only supported bootstrap path.


Configuration

Variable Required Description
MONGO_CONNECTION Yes MongoDB URI, reuse from Nightscout config
CONNECT_MONGO_CONNECTION Alternative Same, for CONNECT_-prefix convention users
LOG_LEVEL No debug | info (default) | warn | error | silent
CONNECT_CARELINK_REGION No eu (default) | us

CONNECT_CARELINK_USERNAME and CONNECT_CARELINK_PASSWORD are no longer needed and can be removed from your config.

Add new Auth0/OAuth2 PKCE-based authentication for CareLink API,
replacing the legacy session-based approach. Includes interactive
login command, automated credential login with CAPTCHA fallback,
and token refresh support.
Replaces file-based logindata.json storage with a MongoDB-backed
token store (connect_tokens collection). Tokens now survive container
restarts without requiring write access to /opt/app/ or any volume
mounts. Zero user configuration needed — MONGO_CONNECTION is already
required by Nightscout.
If CONNECT_CARELINK_USERNAME and CONNECT_CARELINK_PASSWORD are set and
no tokens are found in MongoDB, automatically run the PKCE login flow
so the container works without any manual setup step.
- Add lib/logger.js with debug/info/warn/error levels (default: info)
- Per-cycle data dumps, BLE/M2M success, session setup → debug
- Token refresh, fetch tally, errors → info/warn
- Controlled via LOG_LEVEL env var
Replace actions.log() calls with logger.debug() so they only appear
when LOG_LEVEL=debug. Replace null action implementations for
recordFrame/resetCapture/startCapture with no-op functions to suppress
xstate "No implementation found" warnings.
Replace all active actions.log() calls in poller.js and session.js
with logger equivalents (debug/warn) so they're gated by LOG_LEVEL.
CareLink's Auth0 login requires CAPTCHA for server-side credential
POSTs, making headless username/password login unreliable in practice.
The CONNECT_CARELINK_USERNAME/PASSWORD env vars are no longer needed.

Run 'nightscout-connect login' once to authenticate via the OAuth2
PKCE browser flow and store tokens in MongoDB.
Replace the Minimed CareLink setup docs with the new OAuth2 flow:
- Remove CONNECT_CARELINK_USERNAME/PASSWORD from setup instructions
- Document the nightscout-connect login command and DevTools paste flow
- Add MONGO_CONNECTION requirement
- Mention upcoming Nightscout admin UI integration
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