Update authentication and rate limit documentation - #250

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

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

Conversation

@ricardobcl

@ricardobclricardobcl 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

@ricardobclricardobcl 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
ricardobclforce-pushed the support/update-authentication-documentation branch from 8d28189 to 7f42c93CompareAugust 23, 2026 21:16
CopilotAI reviewed Sep 1, 2026

CopilotAI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for freeto 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.

2 participants

@ricardobcl
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Update authentication and rate limit documentation - #250

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

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

Conversation

@ricardobcl

@ricardobclricardobcl 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

@ricardobclricardobcl 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
ricardobclforce-pushed the support/update-authentication-documentation branch from 8d28189 to 7f42c93CompareAugust 23, 2026 21:16
CopilotAI reviewed Sep 1, 2026

CopilotAI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for freeto 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.

2 participants

@ricardobcl
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Update authentication and rate limit documentation - #250

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

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

Conversation

@ricardobcl

@ricardobclricardobcl 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

@ricardobclricardobcl 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
ricardobclforce-pushed the support/update-authentication-documentation branch from 8d28189 to 7f42c93CompareAugust 23, 2026 21:16
CopilotAI reviewed Sep 1, 2026

CopilotAI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for freeto 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.

2 participants

@ricardobcl
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Update authentication and rate limit documentation - #250

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

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

Conversation

@ricardobcl

@ricardobclricardobcl 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

@ricardobclricardobcl 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
ricardobclforce-pushed the support/update-authentication-documentation branch from 8d28189 to 7f42c93CompareAugust 23, 2026 21:16
CopilotAI reviewed Sep 1, 2026

CopilotAI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for freeto 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.

2 participants

@ricardobcl
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Update authentication and rate limit documentation - #250

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

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

Conversation

@ricardobcl

@ricardobclricardobcl 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

@ricardobclricardobcl 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
ricardobclforce-pushed the support/update-authentication-documentation branch from 8d28189 to 7f42c93CompareAugust 23, 2026 21:16
CopilotAI reviewed Sep 1, 2026

CopilotAI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for freeto 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.

2 participants

@ricardobcl
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Update authentication and rate limit documentation - #250

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

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

Conversation

@ricardobcl

@ricardobclricardobcl 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

@ricardobclricardobcl 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
ricardobclforce-pushed the support/update-authentication-documentation branch from 8d28189 to 7f42c93CompareAugust 23, 2026 21:16
CopilotAI reviewed Sep 1, 2026

CopilotAI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for freeto 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.

2 participants

@ricardobcl
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Update authentication and rate limit documentation - #250

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

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

Conversation

@ricardobcl

@ricardobclricardobcl 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

@ricardobclricardobcl 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
ricardobclforce-pushed the support/update-authentication-documentation branch from 8d28189 to 7f42c93CompareAugust 23, 2026 21:16
CopilotAI reviewed Sep 1, 2026

CopilotAI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for freeto 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.

2 participants

@ricardobcl
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Update authentication and rate limit documentation - #250

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

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

Conversation

@ricardobcl

@ricardobclricardobcl 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

@ricardobclricardobcl 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
ricardobclforce-pushed the support/update-authentication-documentation branch from 8d28189 to 7f42c93CompareAugust 23, 2026 21:16
CopilotAI reviewed Sep 1, 2026

CopilotAI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for freeto 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.

2 participants

@ricardobcl