Skip to content

[RFC]: Enhance auth for patient links with opaque secret #334

@s1monj

Description

@s1monj

Problem

  • Invitation links currently contain:
    • host
    • client_id
    • authorization_code
    • code_verifier
  • This makes the URL itself effectively a bearer credential
  • Links are:
    • long and hard to manage
    • exposed in logs, email previews, browser history
  • Authorization codes are pre-generated and long-lived around 2 weeks
  • Static PKCE reduces security value
  • We want to:
    • shorten and simplify links
    • make links opaque
    • reduce exposure of sensitive values
    • reduce lifetime of credentials
    • do all this without rolling our own auth or modifying Django OAuth Toolkit

Goal

  • Short, opaque invitation links
  • No OAuth artifacts exposed in URLs
  • Minimal lifetime for sensitive credentials
  • Maintain current UX email or SMS to mobile app
  • Keep OAuth token issuance fully handled by Django OAuth Toolkit
  • Avoid custom token logic or changes to DOT internals

Current Approach

  • Server pre-generates:
    • client_id
    • authorization_code
    • code_verifier
  • Invitation link embeds all values:
    https://example.com?invitation=host~client_id~code~verifier
  • Client:
    • parses link
    • calls DOT token endpoint directly
    • Issues:
      • URL contains sensitive credentials
      • auth codes exist long before use
      • harder to revoke cleanly

Proposal

  • Replace embedded credentials with opaque lookup secret

Link format

https://launch.example.com?link=jhe.example.com~abcd1234efgh5678

Flow

  • Invitation record stores:
    • client_id
    • opaque secret
    • expiry
    • redeemed flag
  • Client flow:
    1. Parse link and extract host and secret
    2. POST to JHE:
      POST https://jhe.example.com/api/invitation
      { secret: abcd1234efgh5678}
      
    3. Server:
      • validate secret unused and unexpired
      • generate authorization_code and code_verifier just in time
      • mark invitation as redeemed
      • return:
        • client_id
        • authorization_code
        • code_verifier
    4. Client:
      • performs standard OAuth token exchange with DOT

Key design principles

  • Invitation secret is:
    • high entropy
    • single-use
    • short-lived
  • OAuth grant is:
    • created only at redemption time
    • short-lived
  • Django OAuth Toolkit remains responsible for:
    • token issuance
    • refresh tokens
    • ID tokens

Tradeoffs

Pros

  • Shorter, cleaner, opaque links
  • No sensitive OAuth artifacts in URLs
  • Reduced exposure in logs, email, analytics
  • Authorization codes exist only when needed
  • Cleaner revocation before redemption
  • Preserves use of Django OAuth Toolkit without modification

Cons

  • Slight increase in server side logic for invitation handling

Risks

  • Brute force attempts against invitation secrets

Open Questions

?

Metadata

Metadata

Assignees

No one assigned

    Labels

    rfcRequest For Comment doc

    Type

    No type

    Projects

    Status

    Ready

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions