Skip to content

Update authentication and rate limit documentation - #250

Draft
ricardobcl wants to merge 3 commits into
masterfrom
support/update-authentication-documentation
Draft

Update authentication and rate limit documentation#250
ricardobcl wants to merge 3 commits into
masterfrom
support/update-authentication-documentation

Conversation

@ricardobcl

@ricardobcl ricardobcl commented Aug 23, 2026

Copy link
Copy Markdown

Description

Refreshes the authentication-related documentation to match the current behavior of the API, verified against the uphold/backend master branch. All corrections below were traced to the implementation.

_authentication.md

  • Token response sample: expires_in is omitted for non-expiring tokens (never null), and token_type: "bearer" is always present. Added notes on when expires_in and refresh_token appear, and documented that tokens issued through the web flow are always scoped (the sample now includes the scope field).
  • Documented the redirect_uri authorize parameter: optional, exact match against a registered redirect URL, falls back to the first registered one when omitted.
  • Documented that a replayed or concurrently redeemed authorization code fails with invalid_grant.
  • Added an Error Responses subsection with the RFC 6749 error body shape and the most common error codes, including the 401-with-Basic vs 400-with-body distinction for invalid client credentials.
  • PAT creation: the previous example used -u <email>:<password>, which returns a 401 — the endpoint requires an existing access token that is not limited to specific permissions (scoped Connect tokens are rejected). Also documented the 1–255 character description constraint, that the accessToken is only shown once, and that business accounts cannot create PATs.
  • PAT revocation: clarified that :token is the access token value (not the id), and documented the 204/404 responses.
  • PAT usage: added the -u <token>:x-oauth-basic Basic transport alongside Bearer.
  • Rewrote Basic Authentication: email/password is only accepted on the three 2FA-bootstrap endpoints (GET /me/authentication_methods, POST /me/authentication_methods/:id/request_challenge, GET /me/phones); everywhere else it returns 401.
  • New Two-Factor Authentication section: the OTP-Token: required response header (lowercase — the docs previously said Required), and the OTP-Token/OTP-Method-Id request headers (OTP-Method-Id is only taken into account when the user has no default authentication method).
  • Refined the "PATs bypass 2FA" claim: authentication itself skips the OTP challenge, but OTP-protected operations — such as creating another PAT or changing the password — still request one.

_applications.md

  • Redirect URL considerations: documented multiple redirect URIs (supported since 2022), exact-match semantics, and the actual scheme rules (a host is always required; https or a custom scheme that does not collide with a known URI scheme; http is rejected).
  • Added the grantable transactions:commit:otp scope.

_totp.md

  • Added the POST /me/authentication_methods/:id/request_challenge endpoint (verification code delivery, supported for sms methods only), which the new Two-Factor Authentication section links to.
  • Updated the listing sample to show an sms method, so the request-challenge example references a method type that supports challenges.
  • Documented the sms variant of the Add Authentication Method endpoint (POST /me/authentication_methods/:type).
  • List endpoint: switched the sample to Bearer authentication (matching the response shown) and documented the reduced response returned for email/password requests.
  • Remove endpoint: corrected the OTP requirement (only requested when the user has no default authentication method) and the deletion rule — the default method cannot be deleted, not "the last verified method". The default can be changed via POST /me/authentication_methods/:id/default, which remains undocumented.
  • Updated the intro to cover both authenticator apps and SMS.

_ratelimits.md

  • Global limit is 500 requests / 5-min window (was documented as 250 / 1-min).
  • Removed POST /password/reset — the route exists but has no dedicated limiter.
  • Added the authentication-method challenge limiter (1 / 45-sec per user) and the email-destination transaction limiter (5 / 60-min per user).
  • Fixed the reports limiter path (POST /me/reports/:type).
  • Clarified that per-user limits on unauthenticated endpoints are keyed on the username/email in the request, and documented the too_many_requests error code.

Notes for reviewers

  • The rate-limit table reflects the default app-side configuration (config/default.js). Note that the checked-in production config disables the app-side per-IP dimension for all per-IP limiters (presumably enforced at the CDN edge instead) — worth confirming with the backend team that the per-IP numbers still hold in production. The per-user limiters are unaffected.
  • The WWW-Authenticate header is intentionally not mentioned for the 401 invalid-client case: the pinned oauth2-server fork sets it on an internal response object that is discarded on the error path, so it never reaches the client.
  • contacts:read/contacts:write are grantable on master but no endpoint currently enforces them, so they were deliberately left undocumented. The same applies to phones:write, which was kept only because it was already documented.
  • transactions:write remains documented as deprecated (product stance), although it is still functional.
  • No PKCE content is included — that documentation should follow uphold/backend#19003 once it merges and deploys.

Related issues

uphold/backend#19003 (upcoming PKCE support — not covered here).

Impacted areas

Authentication, Applications, One-Time Password and Rate Limits pages of the API reference.

Steps to reproduce or test

Development

Verified every claim against uphold/backend master (controllers, security-service, oauth-manager, the pinned oauth2-server fork, and config/default.js).

QA

Render the four pages and confirm the flows work as described against the sandbox API.

Checklist

  • Add label Breaking Change if it applies.
  • Commits are atomic and logically separated.
  • Performance implications have been considered.
  • Security implications have been considered.
  • API documentation, if required, has been created or updated.
  • New dependencies have been added to package.json.
  • The README file, if required, has been updated.
  • Architectural diagram, if required, has been updated.

Deploy notes

N/A — no files removed, so no slate index changes are needed.

🤖 Generated with Claude Code

@ricardobcl ricardobcl self-assigned this Aug 23, 2026
Corrects the authentication section to match the current API behavior:
the token response shape (expires_in is omitted for non-expiring tokens,
token_type included), redirect_uri parameter and exact-match semantics,
multiple redirect URI support, OAuth error responses, PAT creation
authentication (requires an existing access token, not email/password),
PAT revocation semantics, the restricted availability of email/password
basic authentication, and the two-factor authentication headers
(OTP-Token, OTP-Method-Id). Adds the request challenge endpoint to the
One-Time Password section and the transactions:commit:otp scope.
Updates the global rate limit to the current 500 requests per 5-minute
window, removes the POST /password/reset row (no such limiter exists),
adds the authentication method challenge and email-destination
transaction limiters, and documents the too_many_requests error code.
@ricardobcl
ricardobcl force-pushed the support/update-authentication-documentation branch from 8d28189 to 7f42c93 Compare August 23, 2026 21:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant