diff --git a/src/content/docs/build/applications/rotate-client-secret.mdx b/src/content/docs/build/applications/rotate-client-secret.mdx
index 61c7c595e..3eaff03a6 100644
--- a/src/content/docs/build/applications/rotate-client-secret.mdx
+++ b/src/content/docs/build/applications/rotate-client-secret.mdx
@@ -1,47 +1,65 @@
---
page_id: 8f6af95a-14ef-436d-862f-bfa82e836558
-title: Rotate client secret
+title: Rotate client secret for your application
description: Security guide for rotating client secrets in backend and machine-to-machine applications including step-by-step rotation process and dependency management.
sidebar:
order: 4
+ label: Rotate client secret
+tableOfContents:
+ maxHeadingLevel: 3
relatedArticles:
- 6c70b7ae-1b1b-43bb-bea1-9b3ec88dd082
- 38d2394f-f064-47a1-89d0-078597b78412
topics:
- applications
- security
+ - authentication
sdk: []
languages: []
audience: developers
complexity: intermediate
keywords:
- client secret rotation
- - security
- - backend apps
- - M2M apps
+ - rotate client secret
+ - backend application security
+ - M2M application security
+ - machine to machine apps
- secret management
-updated: 2024-01-15
+ - Kinde client secret
+ - credential rotation
+ - application security
+updated: 2026-05-13
featured: false
deprecated: false
-ai_summary: Security guide for rotating client secrets in backend and machine-to-machine applications including step-by-step rotation process and dependency management.
+ai_summary: "This page explains how to rotate the client secret for backend and machine-to-machine (M2M) applications in Kinde. Rotating a client secret involves completely deactivating the old secret, so all dependent apps, connections, and services must be updated with the new secret immediately. The guide covers the step-by-step process: navigating to the application details in the Kinde dashboard, accessing the Admin actions section, and selecting Rotate. Users on paid plans can retain the previous client secret temporarily using the Maintain previous secret toggle, giving time to update dependencies before deleting the old secret. Once the previous secret is deleted, a new rotation can be performed. Client secret rotation is only available for back-end and machine-to-machine applications."
---
-To ensure your applications remain secure, you can periodically rotate the Client secret stored in the Kinde-side application.
+To ensure your applications remain secure, you can periodically rotate the client secret generated by Kinde.
-You can only do this for back-end and machine-to-machine applications.
+You can only rotate client secrets for:
+- Back-end applications
+- Machine to Machine (M2M) applications
-Note that you can only rotate a client secret by completely deactivating the old one. So you must update any dependent apps, connections, and services with the new secret ASAP.
+
## Rotate client secret in Kinde
-1. In Kinde, go to **Settings > Applications**.
-2. Select **View details** on the relevant application.
-3. Scroll to the **Admin actions** section.
-4. If you have previously retained a Client secret you’ll need to delete the previous secret first:
- 1. Take a copy of the previous secret if you want to.
- 2. Select **Delete previous client secret.**
-5. Select **Rotate**. A confirmation window opens.
-6. If you want, opt in to rotate the client secret and retain the old secret. You may need to [upgrade plans](https://kinde.com/pricing/) to do this.
-7. If you don’t want to retain the previous secret, or you don’t want to upgrade, leave the switch off.
-8. Select **Rotate client secret**.
-9. Update any dependent apps, connections, and services with the new secret.
+1. Go to your Kinde dashboard and select **View details** on the application you want to rotate the client secret for.
+2. Go to **Details** > scroll to the **Admin actions** section.
+3. Select **Rotate**. A confirmation window opens.
+
+ If you want to retain the previous client secret, enable the **Maintain previous secret** toggle.
+
+4. Select **Rotate client secret**.
+
+## Maintaining the previous client secret
+
+If you kept the previous client secret, you will have options to **copy** or **delete** the previous client secret.
+
+1. Select **Delete previous client secret** before you can rotate to a new client secret.
+
+
\ No newline at end of file
diff --git a/src/content/docs/get-started/connect/getting-app-keys.mdx b/src/content/docs/get-started/connect/getting-app-keys.mdx
index 090e02d41..fdb8af6c9 100644
--- a/src/content/docs/get-started/connect/getting-app-keys.mdx
+++ b/src/content/docs/get-started/connect/getting-app-keys.mdx
@@ -1,8 +1,11 @@
---
page_id: 38d2394f-f064-47a1-89d0-078597b78412
-title: Get application keys
+title: Get application keys to connect your codebase
sidebar:
order: 1
+ label: Get app keys
+tableOfContents:
+ maxHeadingLevel: 3
description: Guide to obtaining and managing Kinde application keys including client ID, client secret, and security best practices for credential management.
relatedArticles:
- 684fc526-a338-4a67-9af6-742a39b66aff
@@ -23,50 +26,58 @@ audience:
complexity: beginner
keywords:
- app keys
+ - application keys
- client id
- client secret
+ - rotate client secret
+ - M2M application
+ - front-end applications
+ - PKCE
- credentials
+ - secrets manager
- environment variables
- security
-updated: 2024-01-15
+updated: 2026-05-13
featured: false
deprecated: false
-ai_summary: Guide to obtaining and managing Kinde application keys including client ID, client secret, and security best practices for credential management.
+ai_summary: "This guide explains how to find, copy, and use Kinde application keys (App keys) — the credentials required to connect a codebase to Kinde. Each application has a unique set of keys in its settings page, including a Kinde-issued domain, an optional custom domain, a client ID, and a client secret (back-end and M2M applications only). The guide walks through viewing and copying keys, then linking them to an application via the relevant SDK. It also covers three common questions: client secrets for back-end and M2M applications can be rotated directly, while front-end applications such as single-page apps and mobile apps have no client secret because their source code is publicly accessible and authorization is handled via the Authorization Code Flow with PKCE; and client secrets should be stored in a secure environment such as a secrets manager, with .env or configuration files excluded from version control via .gitignore."
---
-Application keys - or app keys - are credentials that you need to connect your project to Kinde. There are unique app keys for each application you have. This includes each machine to machine, front-end, back-end, or single-page application, etc.
+Application keys or **App keys** are credentials that you need to connect your codebase to Kinde. Each application and type has a unique set of keys you will find in the settings page for your application. Learn more about [applications in Kinde](/build/applications/about-applications/).
-For more information about Kinde apps, see [Applications in Kinde](/build/applications/about-applications/).
+## View and copy app keys
-## About app keys
+1. Go to your Kinde dashboard and select **View details** on the application you want to connect.
+2. Go to **Details** > scroll to the **App keys** section.
-In the App keys section of your application in Kinde, you’ll find these details:
+ You will find the following keys:
+ - **Custom domain** - if you have [configured a custom domain](/build/domains/pointing-your-domain/)
+ - **Domain** - this is the domain issued by Kinde
+ - **Client ID** - unique for this app
+ - **Client secret** - unique for this app (only back-end and M2M apps have client secrets)
-- **Custom domain** - if you have opted to use one
-- **Domain** - this is the domain issued by Kinde
-- **Client ID** - unique for this app
-- **Client secret** - unique for this app (only back-end and M2M apps have client secrets)
+3. Select the **Copy** icon next to the key you want to copy.
+4. Add the app keys to your application by following the instructions in the relevant [SDK](/developer-tools/about/our-sdks/).
-
+You can rotate client secrets for your back-end and M2M applications.
-## View and copy app keys
+1. Go to the **Details** page of your application.
+2. Scroll to the **Admin actions** section.
+3. Select **Rotate client secret**.
+4. Follow the prompts to rotate your client secret.
+
+Learn more about how to [rotate your client secret](/build/applications/rotate-client-secret/).
-1. Go to **Settings > Environment > Applications**.
-2. Find the application you want to connect.
-3. On the application tile, select **View details**.
-4. Scroll to the **App keys** section.
-5. Select **Copy** to copy the keys.
-6. Make sure you securely store and manage your client secret as it provides access to your Kinde account for your product.
+### Why don't front-end applications have a client secret?
-
+### How can I securely store my client secret?
-7. Add the app key details to the .env file in your code base or starter kit. Follow the instructions in the relevant [SDK](/developer-tools/about/our-sdks/) or the README file in the starter kit.
-8. Do this for each application you want to connect.
+Store your client secret in a secure environment, such as a secrets manager. Add your `.env` or configuration file to `.gitignore` to prevent it from being committed to your codebase.
\ No newline at end of file
diff --git a/src/content/docs/manage-your-apis/troubleshoot-api-keys/common-api-key-errors.mdx b/src/content/docs/manage-your-apis/troubleshoot-api-keys/common-api-key-errors.mdx
index 943641aad..78d7818c8 100644
--- a/src/content/docs/manage-your-apis/troubleshoot-api-keys/common-api-key-errors.mdx
+++ b/src/content/docs/manage-your-apis/troubleshoot-api-keys/common-api-key-errors.mdx
@@ -4,13 +4,20 @@ title: Troubleshoot API key errors
description: Common API key errors and their solutions, including authentication issues, scope problems, and organization access errors
sidebar:
order: 1
+tableOfContents:
+ maxHeadingLevel: 3
relatedArticles:
- 8f7a2b1c-3d4e-4f6a-8b8c-9d0e1f2a3b4c
- 7d6c5b4a-3f2e-4a0b-8c8d-7e6f5a4b3c2d
- 3b2a1c0e-9d8f-4a7b-6c5d-3c2d1a0b9c8e
topics:
- api-keys
- - troubleshooting-api-keys
+ - troubleshooting
+ - authentication
+ - authorization
+ - scopes
+ - organizations
+ - rate-limiting
sdk: []
languages: []
audience:
@@ -19,54 +26,53 @@ audience:
complexity: intermediate
keywords:
- api key errors
- - troubleshooting
- - authentication errors
- - scope errors
- - organization access
- - api key issues
-updated: 2025-08-14
+ - invalid api key
+ - token expired
+ - malformed request
+ - insufficient scope
+ - organization access denied
+ - rate limit exceeded
+ - HTTP 401 unauthorized
+ - HTTP 403 forbidden
+ - HTTP 429 too many requests
+ - bearer token
+ - token refresh
+ - api key troubleshooting
+ - authentication failed
+updated: 2026-05-13
featured: false
deprecated: false
-ai_summary: Common API key errors and their solutions, including authentication issues, scope problems, and organization access errors.
+ai_summary: "This guide covers how to identify and fix common API key errors in Kinde, organized into three categories. Authentication errors include invalid API key (HTTP 401), expired tokens, and malformed requests — caused by incorrect key copy, revoked keys, clock skew, or malformed request bodies. Token requests to /oauth2/token use the client_credentials grant with client_id, client_secret, and grant_type as URL-encoded POST body fields with Content-Type application/x-www-form-urlencoded; scope is optional in Kinde. Authorization errors cover insufficient scope (HTTP 403), organization access denied, and rate limit exceeded (HTTP 429) — caused by missing permissions, org mismatches, or high request volume. Configuration errors focus on invalid or unavailable scopes encountered during API key creation. For each error, the guide provides symptoms with HTTP status codes, common causes, code examples in JavaScript and bash, and step-by-step debugging instructions. The guide concludes with best practices for long-term API key management: rotating keys regularly, revoking unused keys, auditing scope permissions, and monitoring usage patterns."
---
-Although we hope nothing goes awry with API keys, it can happen. This guide will help you identify, diagnose, and resolve problems that could arise when using API keys.
-
-## Perform regular maintenance to prevent errors with API keys
-
-It's always better to prevent errors than have to deal with them in a moment of crisis. Here's some tips for long-term API key management.
-
-- **Rotate API keys**: Periodically rotate keys for security. See [Rotate API keys](/manage-your-apis/add-manage-api-keys/rotate-api-keys/)
-- **Revoke unused keys**: Clean up keys that are no longer needed. See [Revoke API keys](/manage-your-apis/add-manage-api-keys/revoke-api-keys/)
-- **Review scopes**: Regularly audit and update API key permissions. See [Scopes for API keys](/manage-your-apis/add-manage-api-keys/scopes-for-api-keys/)
-- **Monitor usage**: Track API key usage patterns and anomalies. See [Verify API keys](/manage-your-apis/add-manage-api-keys/verify-api-keys-in-your-api/)
+This guide covers common API key errors in Kinde, their causes, and how to resolve them.
## Common error types
-Here's a summary of some of the most common errors and the type of errors that can occur.
+Here's a summary of the most common error types.
-### Authentication errors
+**Authentication errors**
- Invalid API key
- Expired tokens
- Malformed requests
- Missing authentication headers
-### Authorization errors
+**Authorization errors**
- Insufficient scopes
- Organization access denied
- Rate limit exceeded
- Geographic restrictions
-### Configuration errors
+**Configuration errors**
- Missing properties
- Invalid scopes
- Organization mismatch
- Workflow failures
-See below for full explanations and de-bugging assistance.
+See below for full explanations and debugging assistance.
## Authentication errors
@@ -86,17 +92,14 @@ See below for full explanations and de-bugging assistance.
**Solutions:**
-```bash
-# Verify API key format
-# Should look like: k_live_1234567890abcdef...
-
-# Check header format
-curl -X GET https://your-domain.kinde.com/api/users \
- -H "Authorization: Bearer YOUR_API_KEY" # Note: "Bearer " prefix
-
-# Verify key is active
-# Go to Settings > API Keys and check if key is enabled
-```
+1. Verify API key format. Should look like: `k_live_1234567890abcdef...`.
+2. Check header format:
+ ```bash
+ curl -X GET https://your-domain.kinde.com/api/users \
+ -H "Authorization: Bearer YOUR_API_KEY"
+ ```
+
+3. Verify key is active: Go to **Settings > API Keys** and check if key is enabled.
**Debugging steps:**
@@ -119,36 +122,37 @@ curl -X GET https://your-domain.kinde.com/api/users \
**Solutions:**
-```javascript
-// Implement token refresh logic
-async function getValidToken(apiKey) {
- try {
- const response = await fetch("https://your-domain.kinde.com/oauth2/token", {
- method: "POST",
- headers: {
- "Content-Type": "application/x-www-form-urlencoded",
- Authorization: `Bearer ${apiKey}`
- },
- body: "grant_type=client_credentials&scope=openid profile"
- });
-
- const data = await response.json();
- return data.access_token;
- } catch (error) {
- throw new Error("Failed to obtain access token");
- }
-}
+1. Implement token refresh logic
+ ```javascript
+ async function getValidToken(clientId, clientSecret) {
+ try {
+ const response = await fetch("https://your-domain.kinde.com/oauth2/token", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded"
+ },
+ body: `client_id=${encodeURIComponent(clientId)}&client_secret=${encodeURIComponent(clientSecret)}&grant_type=client_credentials`
+ });
+
+ const data = await response.json();
+ return data.access_token;
+ } catch (error) {
+ throw new Error("Failed to obtain access token");
+ }
+ }
+ ```
-// Use in your application
-let accessToken = await getValidToken(apiKey);
-let tokenExpiry = Date.now() + 3600000; // 1 hour
+2. Use in your application
+ ```javascript
+ let accessToken = await getValidToken(clientId, clientSecret);
+ let tokenExpiry = Date.now() + 3600000; // 1 hour
-// Check if token needs refresh
-if (Date.now() >= tokenExpiry) {
- accessToken = await getValidToken(apiKey);
- tokenExpiry = Date.now() + 3600000;
-}
-```
+ // Check if token needs refresh
+ if (Date.now() >= tokenExpiry) {
+ accessToken = await getValidToken(clientId, clientSecret);
+ tokenExpiry = Date.now() + 3600000;
+ }
+ ```
**Debugging steps:**
@@ -172,27 +176,29 @@ if (Date.now() >= tokenExpiry) {
**Solutions:**
-```javascript
-// Ensure proper header format
-const headers = {
- Authorization: `Bearer ${apiKey}`,
- "Content-Type": "application/json"
-};
-
-// For token exchange
-const formData = new URLSearchParams();
-formData.append("grant_type", "client_credentials");
-formData.append("scope", "openid profile");
-
-const response = await fetch("https://your-domain.kinde.com/oauth2/token", {
- method: "POST",
- headers: {
- "Content-Type": "application/x-www-form-urlencoded",
- Authorization: `Bearer ${apiKey}`
- },
- body: formData
-});
-```
+1. Ensure proper header format
+ ```javascript
+ const headers = {
+ Authorization: `Bearer ${apiKey}`,
+ "Content-Type": "application/json"
+ };
+ ```
+
+2. For token exchange
+ ```javascript
+ const formData = new URLSearchParams();
+ formData.append("client_id", clientId);
+ formData.append("client_secret", clientSecret);
+ formData.append("grant_type", "client_credentials");
+
+ const response = await fetch("https://your-domain.kinde.com/oauth2/token", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded"
+ },
+ body: formData
+ });
+ ```
## Authorization errors
@@ -211,12 +217,9 @@ const response = await fetch("https://your-domain.kinde.com/oauth2/token", {
**Solutions:**
-```bash
-# Check current scopes
-# Go to the relevant user or organization > API Keys > [Your Key] > View
-# Verify required scopes are assigned
-
-```
+1. Check current scopes
+ - Go to the relevant user or organization > **API Keys > [Your Key] > View**
+ - Verify required scopes are assigned
**Debugging steps:**
@@ -240,35 +243,37 @@ const response = await fetch("https://your-domain.kinde.com/oauth2/token", {
**Solutions:**
-```javascript
-// Verify organization in token
-function validateOrganizationAccess(token, requiredOrgCode) {
- if (!token.org_code) {
- throw new Error("Organization-scoped token required");
- }
+1. Verify organization in token:
+ ```javascript
+ function validateOrganizationAccess(token, requiredOrgCode) {
+ if (!token.org_code) {
+ throw new Error("Organization-scoped token required");
+ }
- if (token.org_code !== requiredOrgCode) {
- throw new Error("Access denied: organization mismatch");
- }
+ if (token.org_code !== requiredOrgCode) {
+ throw new Error("Access denied: organization mismatch");
+ }
- return true;
-}
-
-// Use in your API endpoint
-app.get("/api/users/:userId", async (req, res) => {
- try {
- const token = extractTokenFromRequest(req);
- const user = await getUserById(req.params.userId);
-
- // Validate organization access
- validateOrganizationAccess(token, user.organization_code);
-
- res.json(user);
- } catch (error) {
- res.status(403).json({error: error.message});
- }
-});
-```
+ return true;
+ }
+ ```
+
+2. Use in your API endpoint
+ ```javascript
+ app.get("/api/users/:userId", async (req, res) => {
+ try {
+ const token = extractTokenFromRequest(req);
+ const user = await getUserById(req.params.userId);
+
+ // Validate organization access
+ validateOrganizationAccess(token, user.organization_code);
+
+ res.json(user);
+ } catch (error) {
+ res.status(403).json({error: error.message});
+ }
+ });
+ ```
**Debugging steps:**
@@ -292,46 +297,48 @@ app.get("/api/users/:userId", async (req, res) => {
**Solutions:**
-```javascript
-// Implement rate limiting in your application
-class RateLimiter {
- constructor(limit, window) {
- this.limit = limit;
- this.window = window;
- this.requests = new Map();
- }
-
- async checkLimit(key) {
- const now = Date.now();
- const windowStart = now - this.window;
-
- // Clean old entries
- if (this.requests.has(key)) {
- this.requests.set(
- key,
- this.requests.get(key).filter((timestamp) => timestamp > windowStart)
- );
+1. Implement rate limiting in your application
+ ```javascript
+ class RateLimiter {
+ constructor(limit, window) {
+ this.limit = limit;
+ this.window = window;
+ this.requests = new Map();
+ }
+
+ async checkLimit(key) {
+ const now = Date.now();
+ const windowStart = now - this.window;
+
+ // Clean old entries
+ if (this.requests.has(key)) {
+ this.requests.set(
+ key,
+ this.requests.get(key).filter((timestamp) => timestamp > windowStart)
+ );
+ }
+
+ const currentRequests = this.requests.get(key) || [];
+
+ if (currentRequests.length >= this.limit) {
+ return false; // Rate limited
+ }
+
+ currentRequests.push(now);
+ this.requests.set(key, currentRequests);
+ return true;
+ }
}
+ ```
- const currentRequests = this.requests.get(key) || [];
+2. Usage
+ ```javascript
+ const rateLimiter = new RateLimiter(100, 60000); // 100 requests per minute
- if (currentRequests.length >= this.limit) {
- return false; // Rate limited
+ if (!(await rateLimiter.checkLimit(apiKey))) {
+ throw new Error("Rate limit exceeded. Please try again later.");
}
-
- currentRequests.push(now);
- this.requests.set(key, currentRequests);
- return true;
- }
-}
-
-// Usage
-const rateLimiter = new RateLimiter(100, 60000); // 100 requests per minute
-
-if (!(await rateLimiter.checkLimit(apiKey))) {
- throw new Error("Rate limit exceeded. Please try again later.");
-}
-```
+ ```
**Debugging steps:**
@@ -357,17 +364,16 @@ if (!(await rateLimiter.checkLimit(apiKey))) {
**Solutions:**
-```bash
-# Check available scopes
-# Go to Settings > APIs > Scopes
-# Verify scope names and availability
+**Check available scopes:**
-# Common standard scopes:
-# Common standard scopes:
-# - openid
-# - profile
-# - email
-# - offline
+1. Go to Settings > APIs > Scopes
+2. Verify scope names and availability
+
+Common standard scopes:
+- openid
+- profile
+- email
+- offline
**Debugging steps:**
@@ -375,3 +381,12 @@ if (!(await rateLimiter.checkLimit(apiKey))) {
2. Check scope spelling and format.
3. Verify scope availability for your plan.
4. Create custom scopes if needed.
+
+## Prevent future API key errors
+
+It's always better to prevent errors than deal with them under pressure. Here are some tips for long-term API key management.
+
+- **Rotate API keys**: Periodically rotate keys for security. See [Rotate API keys](/manage-your-apis/add-manage-api-keys/rotate-api-keys/)
+- **Revoke unused keys**: Clean up keys that are no longer needed. See [Revoke API keys](/manage-your-apis/add-manage-api-keys/revoke-api-keys/)
+- **Review scopes**: Regularly audit and update API key permissions. See [Scopes for API keys](/manage-your-apis/add-manage-api-keys/scopes-for-api-keys/)
+- **Monitor usage**: Track API key usage patterns and anomalies. See [Verify API keys](/manage-your-apis/add-manage-api-keys/verify-api-keys-in-your-api/)
\ No newline at end of file