Skip to content

Device authorization flow #515

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Aug 12, 2025
Merged

Device authorization flow #515

merged 15 commits into from
Aug 12, 2025

Conversation

DaveOrDead
Copy link
Member

@DaveOrDead DaveOrDead commented Jul 31, 2025

Documentation for the new Device Authorization Flow feature

Screenshot 2025-08-01 at 6 33 52 am

Summary by CodeRabbit

  • New Features

    • Added comprehensive Device Authorization Flow docs: overview, quick-start, API-calls guidance, and troubleshooting with examples, security guidance, and testing tips.
  • Documentation

    • Clarified M2M quick-start navigation and streamlined API authorization/testing steps.
    • Added a “Device Authorization Flow” section in the docs sidebar for easier access.
  • Style

    • Reformatted sidebar entries for improved readability and consistency.

Copy link
Contributor

coderabbitai bot commented Jul 31, 2025

Walkthrough

Adds a new Device Authorization Flow docs section (overview, quick start, API calls, troubleshooting), updates the M2M quick start content, and adds a "Device authorization flow" item to the docs sidebar; mostly new MDX content and sidebar formatting changes.

Changes

Cohort / File(s) Change Summary
Device Authorization Flow docs
src/content/docs/authenticate/device-authorization-flow/overview.mdx, src/content/docs/authenticate/device-authorization-flow/quick-start.mdx, src/content/docs/authenticate/device-authorization-flow/api-calls.mdx, src/content/docs/authenticate/device-authorization-flow/troubleshooting.mdx
Added four new MDX pages: overview of RFC 8628 flow, step-by-step quick start with examples, API-calls page describing token validation (userinfo or JWKS), scope enforcement, middleware and error handling, security best practices, and a troubleshooting guide for polling/network issues.
M2M Quick Start update
src/content/docs/machine-to-machine-applications/about-m2m/m2m-quick-start-guide.mdx
Edited title/metadata and revised steps for clarity and conciseness; updated navigation paths and simplified instructions for authorizing APIs and testing M2M apps.
Sidebar data
src/data/sidebarData.ts
Reformatted sidebar object declarations for readability and added a "Device authorization flow" sidebar item (directory: "authenticate/device-authorization-flow", collapsed: false). No other functional changes.

Sequence Diagram(s)

sequenceDiagram
    participant Device
    participant User
    participant KindeAuth
    participant API

    Device->>KindeAuth: POST /device_authorization (client_id, scope[, audience])
    KindeAuth-->>Device: device_code, user_code, verification_uri(_complete), expires_in, interval
    Device->>User: Display user_code + verification URI/QR
    User->>KindeAuth: Authenticate and consent via verification URI
    Device->>KindeAuth: Poll /token with device_code
    alt authorization_pending / slow_down
        KindeAuth-->>Device: authorization_pending / slow_down
        Device->>KindeAuth: Continue / backoff polling
    else access_token issued
        KindeAuth-->>Device: access_token (scope, aud, exp)
        Device->>API: Request with Authorization: Bearer access_token
        API->>KindeAuth: Validate token (userinfo or JWKS)
        KindeAuth-->>API: Validation result / claims
        API-->>Device: 200 OK or 401/403
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Possibly related PRs

  • Feat/page content #317 — Modifies src/data/sidebarData.ts; likely related due to concurrent sidebar edits and possible merge conflicts.

Suggested reviewers

  • clairekinde11
  • marcosmartini
  • oliwolff1

Poem

🐰
A code on screen, a tiny beep,
I wait while users log in deep.
Tokens found, the API sings,
Docs hop out with helpful things.
Hooray — this rabbit brings confetti! 🎉

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/device-flow-docs

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

cloudflare-workers-and-pages bot commented Jul 31, 2025

Deploying kinde-docs-previews with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4877d6b
Status: ✅  Deploy successful!
Preview URL: https://e99a5c5a.kinde-docs-previews.pages.dev
Branch Preview URL: https://feat-device-flow-docs.kinde-docs-previews.pages.dev

View logs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/content/docs/authenticate/device-authorization-flow/overview.mdx (1)

118-121: Grammar / clarity nit

"any scopes which are belong to that audience""any scopes that belong to that audience".

-If an audience is specified in the request, any scopes which are belong to that audience
+If an audience is specified in the request, any scopes that belong to that audience
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5bd520 and 269f7a9.

📒 Files selected for processing (6)
  • src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (1 hunks)
  • src/content/docs/authenticate/device-authorization-flow/overview.mdx (1 hunks)
  • src/content/docs/authenticate/device-authorization-flow/quick-start.mdx (1 hunks)
  • src/content/docs/authenticate/device-authorization-flow/troubleshooting.mdx (1 hunks)
  • src/content/docs/machine-to-machine-applications/about-m2m/m2m-quick-start-guide.mdx (1 hunks)
  • src/data/sidebarData.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: the kinde widget content properties use camelcase naming convention (e.g., `pagetitle` instead of `p...
Learnt from: DaveOrDead
PR: kinde-oss/documentation#364
File: src/content/docs/design/design-with-code/understand-page-design.mdx:42-42
Timestamp: 2025-03-19T03:59:26.373Z
Learning: The Kinde widget content properties use camelCase naming convention (e.g., `pageTitle` instead of `page_title`) in JSX code examples and documentation.

Applied to files:

  • src/data/sidebarData.ts
🪛 Gitleaks (8.27.2)
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx

20-21: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


31-32: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


213-215: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

src/content/docs/authenticate/device-authorization-flow/quick-start.mdx

94-95: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🔇 Additional comments (28)
src/data/sidebarData.ts (4)

14-17: LGTM! Improved formatting for better readability.

The conversion from single-line to multi-line format for the "Learn about Kinde" item improves code readability and maintainability.


139-143: Device Authorization Flow section properly integrated.

The new sidebar entry is well-positioned under the "Auth and access" section with appropriate configuration. The directory path follows the expected pattern and collapsed: false ensures the content is accessible by default.


293-297: Consistent formatting improvements in M2M section.

The multi-line formatting for "M2M token customization" and "Troubleshooting" items maintains consistency with the overall formatting improvements in this file.

Also applies to: 303-307


349-351: Formatting consistency maintained in Features section.

The multi-line formatting for "About" and "Guides" items aligns with the overall formatting improvements throughout the file.

Also applies to: 354-356

src/content/docs/machine-to-machine-applications/about-m2m/m2m-quick-start-guide.mdx (1)

16-20: Updated navigation flow improves clarity.

The revised M2M application creation process with explicit application type selection (Machine-to-machine (M2M)) is clearer than the previous flow. The step-by-step approach reduces ambiguity about where to find the feature.

src/content/docs/authenticate/device-authorization-flow/quick-start.mdx (6)

1-26: Well-structured quick start guide with clear prerequisites.

The document provides a comprehensive introduction to Device Authorization Flow with appropriate prerequisites and step-by-step application setup. The choice of "Device and IoT" application type aligns with the OAuth 2.0 Device Authorization Grant use case.


27-33: Authentication method setup is correctly documented.

The email + code passwordless authentication is an appropriate choice for device authorization flows, as it provides a user-friendly experience on secondary devices.


38-57: Device authorization request follows RFC 8628 correctly.

The device authorization request format is compliant with OAuth 2.0 Device Authorization Grant (RFC 8628). The response includes all required fields: device_code, user_code, verification_uri, expires_in, and interval. The addition of verification_uri_complete and qr_code enhances user experience.


70-87: Token polling implementation follows best practices.

The polling mechanism correctly implements the device authorization flow with proper grant type (urn:ietf:params:oauth:grant-type:device_code) and respects the specified interval. The success response format matches OAuth 2.0 standards.


94-96: API usage example is appropriate but uses placeholder values.

The curl example correctly demonstrates Bearer token usage for API calls. The placeholder YOUR_ACCESS_TOKEN is appropriate for documentation purposes and doesn't represent a security concern.


100-105: Useful default application configuration tip.

The tip about setting a default application for device flows is valuable for improving user experience when no Client ID is specified in requests.

src/content/docs/authenticate/device-authorization-flow/troubleshooting.mdx (7)

16-30: Correct handling of authorization_pending error.

The explanation and code example properly demonstrate how to handle the authorization_pending error, which is the expected response during the normal flow while the user completes authorization.


32-46: Proper slow_down error handling with backoff.

The implementation correctly handles the slow_down error by increasing the polling interval by 5 seconds, which follows RFC 8628 recommendations for rate limiting.


48-78: Appropriate handling of terminal error conditions.

Both access_denied and expired_token errors are correctly identified as terminal conditions that require stopping the polling process. The suggested actions (inform user to retry, request new device code) are appropriate.


88-104: Excellent demonstration of proper vs improper polling.

The side-by-side comparison clearly shows the difference between respecting the polling interval and polling too frequently. This helps developers avoid common implementation mistakes.


134-161: Comprehensive error handling strategy.

The switch statement approach correctly categorizes errors into recoverable (authorization_pending, slow_down) and terminal (access_denied, expired_token) conditions, implementing appropriate responses for each.


171-192: Practical network error handling with retry logic.

The timeout handling and retry logic implementation provides robust network error recovery while avoiding infinite retry loops.


200-206: Useful network diagnostics commands.

The provided bash commands (nslookup, curl -I) are practical tools for diagnosing DNS and connectivity issues that developers might encounter.

src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (8)

17-22: Standard OAuth 2.0 Bearer token usage.

The Bearer token authentication example correctly demonstrates the standard HTTP Authorization header format for OAuth 2.0 access tokens.


28-60: Proper token validation using Kinde's userinfo endpoint.

The validation approach using Kinde's /oauth2/v2/user_profile endpoint is secure and follows OAuth 2.0 standards. The response examples clearly show both success and error scenarios.


90-102: Scope enforcement logic is correctly implemented.

The scope checking function properly decodes the JWT token and validates required scopes against the token's scope claim.


108-146: Well-structured middleware for API protection.

The Express.js middleware example demonstrates proper token extraction, validation, and error handling. Using Kinde's userinfo endpoint for validation is a secure approach.


173-175: Excellent security guidance on token storage.

The advice against localStorage and emphasis on server-side validation reflects current security best practices for token handling.


180-190: Appropriate rate limiting for token validation.

The rate limiting configuration (100 requests per 15 minutes) provides reasonable protection against abuse while allowing normal usage patterns.


196-206: Comprehensive authentication event logging.

The logging function captures essential security-relevant information (timestamp, action, success status, user ID, scopes) for monitoring and audit purposes.


212-225: Practical testing examples for API endpoints.

Both curl and JavaScript fetch examples correctly demonstrate how to test protected endpoints with Bearer tokens. The placeholder tokens are appropriate for documentation.

src/content/docs/authenticate/device-authorization-flow/overview.mdx (2)

98-105: Double-check non-standard server_error mapping

RFC 8628 does not list server_error for token polling; typical errors are authorization_pending, slow_down, access_denied, and expired_token.
If server_error is proprietary, add a note clarifying when it appears, otherwise align with the spec or remove to avoid confusion.


1-12: Front-matter looks correct

Front-matter keys (page_id, title, sidebar, relatedArticles) follow the repo’s pattern. No concerns here.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/content/docs/authenticate/device-authorization-flow/quick-start.mdx (1)

3-3: Capitalize “Quick Start” for consistency

Headings elsewhere in the docs capitalize both words (“Quick Start”). Aligning the front-matter title improves searchability and visual consistency.

-title: Quick start
+title: Quick Start
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d3c96af and 539f7ca.

📒 Files selected for processing (1)
  • src/content/docs/authenticate/device-authorization-flow/quick-start.mdx (1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.27.2)
src/content/docs/authenticate/device-authorization-flow/quick-start.mdx

94-95: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🔇 Additional comments (1)
src/content/docs/authenticate/device-authorization-flow/quick-start.mdx (1)

94-95: Static-analysis false positive – placeholder token is safe

Gitleaks flagged the Authorization: Bearer YOUR_ACCESS_TOKEN header as a secret.
Because the value is a placeholder, no action is required. Consider adding YOUR_ACCESS_TOKEN to the secret-scanner allow-list to prevent future noise.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/content/docs/authenticate/device-authorization-flow/quick-start.mdx (1)

38-43: Remove non-standard grant_type from device-code request

grant_type=urn:ietf:params:oauth:grant-type:device_code is not part of RFC 8628 for the device-code request and some servers will reject it. Only client_id (and optionally scope) should be posted here.

   -d "client_id=<YOUR_CLIENT_ID>" \
-  -d "grant_type=urn:ietf:params:oauth:grant-type:device_code"
+  # -d "scope=<YOUR_SCOPES>"   # optional
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (1)

66-83: JWT validation still uses shared secret

The sample still verifies the access token with YOUR_JWT_SECRET. Kinde signs tokens with asymmetric keys, so verification must use the public key from the JWKS endpoint (or introspection). Please update as previously suggested.

🧹 Nitpick comments (1)
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (1)

118-146: Avoid remote validation on every request – cache JWKS / token claims

Calling fetch(...) for each API hit adds latency and couples API uptime to Kinde’s availability. Prefer:

  1. Verify the JWT locally with cached JWKS keys (rotate periodically).
  2. Fall back to /userinfo only on signature failure.

This reduces network hops and increases resilience.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2582b52 and 68cffa4.

📒 Files selected for processing (4)
  • src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (1 hunks)
  • src/content/docs/authenticate/device-authorization-flow/quick-start.mdx (1 hunks)
  • src/content/docs/authenticate/device-authorization-flow/troubleshooting.mdx (1 hunks)
  • src/content/docs/machine-to-machine-applications/about-m2m/m2m-quick-start-guide.mdx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/content/docs/authenticate/device-authorization-flow/troubleshooting.mdx
  • src/content/docs/machine-to-machine-applications/about-m2m/m2m-quick-start-guide.mdx
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: in the project documentation, the `/kinde-apis/management` and `/kinde-apis/frontend` pages use a th...
Learnt from: marcosmartini
PR: kinde-oss/documentation#253
File: src/content/docs/properties/work-with-properties/property-groups.mdx:13-13
Timestamp: 2024-11-12T06:00:08.396Z
Learning: In the project documentation, the `/kinde-apis/management` and `/kinde-apis/frontend` pages use a third-party API reference and client that loads a Single Page Application (SPA). Therefore, links to sections within these pages are valid, even if they appear to point to non-existent sections in static analysis, and should not be flagged.

Applied to files:

  • src/content/docs/authenticate/device-authorization-flow/api-calls.mdx
📚 Learning: the kinde documentation's base url is not `kinde.com`, so links to the documentation should use the ...
Learnt from: marcosmartini
PR: kinde-oss/documentation#226
File: src/content/docs/developer-tools/sdks/backend/nextjs-sdk.mdx:0-0
Timestamp: 2024-10-22T06:12:51.559Z
Learning: The Kinde documentation's base URL is not `kinde.com`, so links to the documentation should use the correct base URL.

Applied to files:

  • src/content/docs/authenticate/device-authorization-flow/api-calls.mdx
📚 Learning: the kinde widget content properties use camelcase naming convention (e.g., `pagetitle` instead of `p...
Learnt from: DaveOrDead
PR: kinde-oss/documentation#364
File: src/content/docs/design/design-with-code/understand-page-design.mdx:42-42
Timestamp: 2025-03-19T03:59:26.373Z
Learning: The Kinde widget content properties use camelCase naming convention (e.g., `pageTitle` instead of `page_title`) in JSX code examples and documentation.

Applied to files:

  • src/content/docs/authenticate/device-authorization-flow/api-calls.mdx
🪛 Gitleaks (8.27.2)
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx

20-21: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


31-32: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


213-215: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

src/content/docs/authenticate/device-authorization-flow/quick-start.mdx

20-21: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


31-32: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🔇 Additional comments (1)
src/content/docs/authenticate/device-authorization-flow/quick-start.mdx (1)

70-75: LGTM – correct token polling parameters

grant_type=device_code belongs on the token endpoint, and the required parameters are present.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (1)

100-111: Do not use jwt.decode for scope checks; verify signature first

Decode bypasses signature verification and is unsafe for authorization checks. Verify the token before inspecting claims, or pass already-verified claims.

Option A: Verify inside the function (async):

-// Example: Check if user has required scope
-function hasRequiredScope(token, requiredScope) {
-  const decoded = jwt.decode(token);
-  const tokenScopes = decoded.scope.split(" ");
-  return tokenScopes.includes(requiredScope);
-}
+// Example: Check if user has required scope (after signature verification)
+async function hasRequiredScope(token, requiredScope, getKey) {
+  const verifyOptions = {
+    algorithms: ["RS256"],
+    issuer: "https://<your-subdomain>.kinde.com",
+    audience: "<your-api-audience>"
+  };
+  const decoded = await new Promise((resolve, reject) => {
+    jwt.verify(token, getKey, verifyOptions, (err, d) => err ? reject(err) : resolve(d));
+  });
+  const tokenScopes = (decoded.scope || "").split(" ");
+  return tokenScopes.includes(requiredScope);
+}
 
-// Usage
-if (!hasRequiredScope(accessToken, "read:users")) {
-  return res.status(403).json({error: "Insufficient scope"});
-}
+// Usage
+if (!(await hasRequiredScope(accessToken, "read:users", getKey))) {
+  return res.status(403).json({ error: "Insufficient scope" });
+}

Option B: Verify once in middleware and check scopes from verified claims:

function hasRequiredScopeFromClaims(claims, requiredScope) {
  const scopes = (claims.scope || "").split(" ");
  return scopes.includes(requiredScope);
}
src/content/docs/authenticate/device-authorization-flow/overview.mdx (1)

32-36: Align device authorization request parameters with RFC 8628 (client_id required; add scope; remove note about omitting client_id)

Per RFC 8628, device authorization request requires client_id and optionally scope. Remove the “can be omitted” note.

-**Parameters**:
-
-- `client_id` (optional): Your application's client ID - can be omitted if you have set an application as the default for device flows
-- `audience` (optional): The audience to use for the request
+**Parameters**:
+
+- `client_id` (required): Your application's client ID
+- `scope` (optional): Space-delimited list of scopes to request
+- `audience` (optional): The audience to use for the request
🧹 Nitpick comments (6)
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (3)

127-155: Prefer local JWT verification over per-request user_profile calls

Calling user_profile on every request adds latency, couples availability to the IdP, and complicates scope checks. Prefer validating the JWT locally with JWKS and issuer/audience checks, then attach claims to req.user. If you keep this pattern:

  • Add a timeout and handle network errors distinctly (e.g., 502 on upstream failure).
  • Consider caching positive validations briefly to reduce load.
  • Perform scope checks against the token’s scope claim, not the profile payload.

Would you like a concrete Express middleware example using the validateToken/getKey above that sets req.user = decoded?


206-214: Avoid logging sensitive data; fix claim names

Access tokens use sub for subject; user_id may not exist. Also avoid logging raw tokens or PII.

-function logAuthEvent(token, action, success) {
+function logAuthEvent(claims, action, success) {
   console.log({
     timestamp: new Date().toISOString(),
     action: action,
     success: success,
-    userId: token.user_id,
-    scopes: token.scope
+    userId: claims.sub,
+    scopes: claims.scope
   });
 }

Consider sending to a proper audit sink and redacting as needed.


19-22: Avoid leaking real tokens in examples

Examples use placeholders, which is fine. Add a short note below each example reminding not to paste real access tokens into shared logs or screenshots.

Also applies to: 221-234

src/content/docs/authenticate/device-authorization-flow/overview.mdx (3)

15-21: Consistency: use “authorization” (not “authentication”)

Title says device authorization; update section heading to match.

-## How the device authentication flow works
+## How the device authorization flow works

97-104: Clarify server_error description

server_error is a generic upstream failure; current description suggests misconfigured device code.

-| `server_error`          | Misconfigured device code            | Request a new device code      |
+| `server_error`          | Authorization server encountered an error | Back off and retry; contact support if persistent |

119-120: Grammar and clarity fix for scopes paragraph

-If an audience is specified in the request, any scopes which are belong to that audience that are granted to the user by their role will also be granted to the device. The list of scopes will be displayed on the consent screen. If the user consents, the scopes will be included in the `scope` claim of the access token.
+If an audience is specified in the request, any scopes that belong to that audience and are granted to the user via their role will also be granted to the device. These scopes are shown on the consent screen and, upon consent, included in the access token’s `scope` claim.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49bc9b2 and 37de349.

📒 Files selected for processing (3)
  • src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (1 hunks)
  • src/content/docs/authenticate/device-authorization-flow/overview.mdx (1 hunks)
  • src/content/docs/authenticate/device-authorization-flow/quick-start.mdx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/content/docs/authenticate/device-authorization-flow/quick-start.mdx
🧰 Additional context used
🪛 Gitleaks (8.27.2)
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx

20-21: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


31-32: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


222-224: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🔇 Additional comments (4)
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (1)

71-73: No changes required: JWKS endpoint is correct and OIDC discovery is supported

The JWKS URI for Kinde tenants is indeed
https://.kinde.com/.well-known/jwks
(without a .json suffix), and Kinde publishes a standard OpenID Connect Discovery document at
https://.kinde.com/.well-known/openid-configuration
which includes the jwks_uri field pointing to the same endpoint.

• File: src/content/docs/authenticate/device-authorization-flow/api-calls.mdx
• Lines: 71–73

src/content/docs/authenticate/device-authorization-flow/overview.mdx (3)

91-94: Verify polling “Maximum time” versus earlier expires_in example

You list expires_in: 600 (10 minutes) in the authorization response but “typically 30 minutes” here. Align with Kinde’s actual values.

Would you like me to search Kinde docs and update both places consistently?


105-110: Verify default access token lifetime

“Access tokens expire after 1 hour by default” may vary per tenant/policy.

Confirm Kinde’s default and whether it’s configurable, then reflect that nuance.


111-116: Verify multiple audiences support and app/API authorization note

Multiple audiences support is provider-specific. Confirm Kinde behavior and prerequisites (authorizing API for the device app).

If supported, consider linking to the specific Kinde docs page.

@clairekinde11 clairekinde11 removed the request for review from evgenyk August 12, 2025 00:47
@clairekinde11 clairekinde11 merged commit 4cd6439 into main Aug 12, 2025
5 of 6 checks passed
@clairekinde11 clairekinde11 deleted the feat/device-flow-docs branch August 12, 2025 00:50
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (2)

66-93: Strengthen JWT verification: correct JWKS URL, enable caching/rate limiting, validate issuer/audience, and handle JWKS errors

The sample verifies signatures but misses critical hardening:

  • Use the OIDC-standard JWKS path (jwks.json).
  • Enable jwks-rsa caching/rate limiting.
  • Validate issuer and audience.
  • Handle getSigningKey errors and prefer getPublicKey() when available.

These are security-sensitive defaults.

-// Node.js example using jsonwebtoken with JWKS
-const jwt = require("jsonwebtoken");
-const jwksClient = require("jwks-rsa");
-
-const client = jwksClient({
-  jwksUri: "https://<your-subdomain>.kinde.com/.well-known/jwks"
-});
-
-function getKey(header, callback) {
-  client.getSigningKey(header.kid, (err, key) => {
-    const signingKey = key.publicKey || key.rsaPublicKey;
-    callback(null, signingKey);
-  });
-}
-
-function validateToken(token) {
-  return new Promise((resolve, reject) => {
-    jwt.verify(token, getKey, { algorithms: ["RS256"] }, (err, decoded) => {
-      if (err) {
-        resolve({ valid: false, error: err.message });
-      } else {
-        resolve({ valid: true, user: decoded });
-      }
-    });
-  });
-}
+// Node.js example using jsonwebtoken with JWKS
+const jwt = require("jsonwebtoken");
+const jwksClient = require("jwks-rsa");
+
+const client = jwksClient({
+  jwksUri: "https://<your-subdomain>.kinde.com/.well-known/jwks.json",
+  cache: true,
+  cacheMaxEntries: 5,
+  cacheMaxAge: 10 * 60 * 1000, // 10 minutes
+  rateLimit: true,
+  jwksRequestsPerMinute: 10
+});
+
+function getKey(header, callback) {
+  client.getSigningKey(header.kid, (err, key) => {
+    if (err) return callback(err);
+    const signingKey = key.getPublicKey ? key.getPublicKey() : (key.publicKey || key.rsaPublicKey);
+    callback(null, signingKey);
+  });
+}
+
+function validateToken(token) {
+  const verifyOptions = {
+    algorithms: ["RS256"],
+    issuer: "https://<your-subdomain>.kinde.com",
+    audience: "<your-api-audience>"
+  };
+  return new Promise((resolve) => {
+    jwt.verify(token, getKey, verifyOptions, (err, decoded) => {
+      if (err) {
+        return resolve({ valid: false, error: err.message });
+      }
+      resolve({ valid: true, user: decoded });
+    });
+  });
+}

99-111: Do not use jwt.decode for scope checks — verify the token first

jwt.decode does not verify signatures; forged tokens could pass scope checks. Verify first, then inspect claims. Also, operate on decoded claims rather than raw token strings for clarity.

-// Example: Check if user has required scope
-function hasRequiredScope(token, requiredScope) {
-  const decoded = jwt.decode(token);
-  const tokenScopes = decoded.scope.split(" ");
-  return tokenScopes.includes(requiredScope);
-}
+// Example: Check if decoded claims include required scope
+function hasRequiredScopeFromClaims(claims, requiredScope) {
+  const raw = claims && claims.scope ? String(claims.scope) : "";
+  const tokenScopes = raw.split(" ").filter(Boolean);
+  return tokenScopes.includes(requiredScope);
+}
 
-// Usage
-if (!hasRequiredScope(accessToken, "read:users")) {
-  return res.status(403).json({error: "Insufficient scope"});
-}
+// Usage (verify first, then check scopes)
+try {
+  const decoded = jwt.verify(accessToken, getKey, {
+    algorithms: ["RS256"],
+    issuer: "https://<your-subdomain>.kinde.com",
+    audience: "<your-api-audience>"
+  });
+  if (!hasRequiredScopeFromClaims(decoded, "read:users")) {
+    return res.status(403).json({ error: "Insufficient scope" });
+  }
+} catch (e) {
+  return res.status(401).json({ error: "Invalid token" });
+}
🧹 Nitpick comments (3)
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (3)

3-3: Align title capitalization with docs naming

Use "Device Authorization Flow" for consistency with sidebar/nav and other pages.

-title: Call your API using device authorization flow
+title: Call your API using Device Authorization Flow

206-214: Avoid logging PII; use standard claims and guard missing scopes

Use sub instead of a non-standard user_id, and default scope to empty. Consider redacting or hashing identifiers in real apps.

 function logAuthEvent(token, action, success) {
   console.log({
     timestamp: new Date().toISOString(),
     action: action,
     success: success,
-    userId: token.user_id,
-    scopes: token.scope
+    userId: token.sub,       // standard subject claim
+    scopes: token.scope || "" // guard missing claim
   });
 }

19-22: Prevent accidental token leakage in docs snippets

These examples use placeholders, but scanners can flag them. Add a short note to discourage pasting real tokens into docs and CI logs.

 curl -X GET https://your-api.com/protected-resource \
   -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
+// Note: Replace YOUR_ACCESS_TOKEN when testing locally. Do not commit real tokens.
 curl -X GET https://<your-subdomain>.kinde.com/oauth2/v2/user_profile \
   -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
+// Note: Replace YOUR_ACCESS_TOKEN when testing locally. Do not commit real tokens.
 curl -X GET https://your-api.com/protected-resource \
   -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
+# Note: Replace YOUR_ACCESS_TOKEN when testing locally. Do not commit real tokens.

Also applies to: 31-33, 222-224

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37de349 and 4877d6b.

📒 Files selected for processing (1)
  • src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.27.2)
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx

20-21: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


31-32: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)


222-224: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🔇 Additional comments (1)
src/content/docs/authenticate/device-authorization-flow/api-calls.mdx (1)

192-199: Rate limiting example looks good

Clear, sane defaults for window and max requests. Route-scoped application is appropriate.

Comment on lines +127 to +154
function authenticateToken(req, res, next) {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];

if (!token) {
return res.status(401).json({error: "Access token required"});
}

// Validate token with Kinde
fetch("https://<your-subdomain>.kinde.com/oauth2/v2/user_profile", {
headers: {
Authorization: `Bearer ${token}`
}
})
.then((response) => {
if (!response.ok) {
throw new Error("Invalid token");
}
return response.json();
})
.then((user) => {
req.user = user;
next();
})
.catch((error) => {
return res.status(401).json({error: "Invalid token"});
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Prefer local JWT validation in middleware; avoid per-request network hop to user_profile

Round-tripping to Kinde for every request adds latency and a failure mode. Since a valid JWT already proves authenticity, validate locally and attach claims to req.user. If you keep the fetch approach, add timeout/error handling and note that user_profile does not carry scopes.

-function authenticateToken(req, res, next) {
-  const authHeader = req.headers["authorization"];
-  const token = authHeader && authHeader.split(" ")[1];
-
-  if (!token) {
-    return res.status(401).json({error: "Access token required"});
-  }
-
-  // Validate token with Kinde
-  fetch("https://<your-subdomain>.kinde.com/oauth2/v2/user_profile", {
-    headers: {
-      Authorization: `Bearer ${token}`
-    }
-  })
-    .then((response) => {
-      if (!response.ok) {
-        throw new Error("Invalid token");
-      }
-      return response.json();
-    })
-    .then((user) => {
-      req.user = user;
-      next();
-    })
-    .catch((error) => {
-      return res.status(401).json({error: "Invalid token"});
-    });
-}
+function authenticateToken(req, res, next) {
+  const authHeader = req.headers["authorization"];
+  const token = authHeader && authHeader.split(" ")[1];
+  if (!token) return res.status(401).json({ error: "Access token required" });
+
+  validateToken(token).then((result) => {
+    if (!result.valid) return res.status(401).json({ error: result.error || "Invalid token" });
+    req.user = result.user; // decoded claims
+    next();
+  }).catch(() => res.status(401).json({ error: "Invalid token" }));
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function authenticateToken(req, res, next) {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
if (!token) {
return res.status(401).json({error: "Access token required"});
}
// Validate token with Kinde
fetch("https://<your-subdomain>.kinde.com/oauth2/v2/user_profile", {
headers: {
Authorization: `Bearer ${token}`
}
})
.then((response) => {
if (!response.ok) {
throw new Error("Invalid token");
}
return response.json();
})
.then((user) => {
req.user = user;
next();
})
.catch((error) => {
return res.status(401).json({error: "Invalid token"});
});
}
function authenticateToken(req, res, next) {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
if (!token) return res.status(401).json({ error: "Access token required" });
validateToken(token)
.then((result) => {
if (!result.valid) return res.status(401).json({ error: result.error || "Invalid token" });
req.user = result.user; // decoded claims
next();
})
.catch(() => res.status(401).json({ error: "Invalid token" }));
}
🤖 Prompt for AI Agents
In src/content/docs/authenticate/device-authorization-flow/api-calls.mdx around
lines 127 to 154, the middleware currently fetches /user_profile on every
request; instead implement local JWT validation: extract the Bearer token,
download/refresh the provider JWKS (cache keys and respect cache-control),
verify token signature, issuer, audience, and expiry, then set req.user to the
decoded claims and call next(); if validation fails return 401. If you choose to
keep the remote fetch approach, add a request timeout, proper error handling for
non-200 responses and network errors, and a comment that user_profile does not
include scopes so you must rely on token claims for authorization.

Comment on lines +161 to +175
```javascript
function handleTokenError(res, error) {
switch (error.error) {
case "invalid_token":
// Token is invalid or expired
return res.status(401).json({error: "Please re-authenticate"});

case "insufficient_scope":
// Token doesn't have required permissions
return res.status(403).json({error: "Insufficient permissions"});

default:
return res.status(500).json({error: "Authentication error"});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

handleTokenError expects error.error, but jwt.verify throws typed errors

JsonWebTokenError, TokenExpiredError, etc., won’t have an error field. Map by error.name and still support structured errors from HTTP responses.

-function handleTokenError(res, error) {
-  switch (error.error) {
-    case "invalid_token":
-      // Token is invalid or expired
-      return res.status(401).json({error: "Please re-authenticate"});
-
-    case "insufficient_scope":
-      // Token doesn't have required permissions
-      return res.status(403).json({error: "Insufficient permissions"});
-
-    default:
-      return res.status(500).json({error: "Authentication error"});
-  }
-}
+function handleTokenError(res, error) {
+  const code = error?.error || error?.name;
+  switch (code) {
+    case "invalid_token":
+    case "JsonWebTokenError":
+    case "NotBeforeError":
+      return res.status(401).json({ error: "Please re-authenticate" });
+    case "TokenExpiredError":
+      return res.status(401).json({ error: "Session expired. Please re-authenticate" });
+    case "insufficient_scope":
+      return res.status(403).json({ error: "Insufficient permissions" });
+    default:
+      return res.status(500).json({ error: "Authentication error" });
+  }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```javascript
function handleTokenError(res, error) {
switch (error.error) {
case "invalid_token":
// Token is invalid or expired
return res.status(401).json({error: "Please re-authenticate"});
case "insufficient_scope":
// Token doesn't have required permissions
return res.status(403).json({error: "Insufficient permissions"});
default:
return res.status(500).json({error: "Authentication error"});
}
}
function handleTokenError(res, error) {
const code = error?.error || error?.name;
switch (code) {
case "invalid_token":
case "JsonWebTokenError":
case "NotBeforeError":
return res.status(401).json({ error: "Please re-authenticate" });
case "TokenExpiredError":
return res.status(401).json({ error: "Session expired. Please re-authenticate" });
case "insufficient_scope":
return res.status(403).json({ error: "Insufficient permissions" });
default:
return res.status(500).json({ error: "Authentication error" });
}
}
🤖 Prompt for AI Agents
In src/content/docs/authenticate/device-authorization-flow/api-calls.mdx around
lines 161 to 175, the handler currently inspects error.error but JWT runtime
errors use error.name (e.g., JsonWebTokenError, TokenExpiredError) and HTTP
responses may provide a structured error field; update the function to first
branch on error.name for known JWT errors (map TokenExpiredError -> 401 with
"Please re-authenticate", JsonWebTokenError -> 401 with generic auth error,
etc.), fall back to checking error.error for structured HTTP error payloads
(mapping "insufficient_scope" -> 403), and finally return a 500 for unknown
errors; ensure the response messages and status codes match the original intent
and preserve any original error details when appropriate.

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.

3 participants