> **Building with AI coding agents?** If you're using an AI coding agent, install the official Scalekit plugin. It gives your agent full awareness of the Scalekit API — reducing hallucinations and enabling faster, more accurate code generation.
>
> - **Claude Code**: `/plugin marketplace add scalekit-inc/claude-code-authstack` then `/plugin install <auth-type>@scalekit-auth-stack`
> - **GitHub Copilot CLI**: `copilot plugin marketplace add scalekit-inc/github-copilot-authstack` then `copilot plugin install <auth-type>@scalekit-auth-stack`
> - **Codex**: run the bash installer, restart, then open Plugin Directory and enable `<auth-type>`
> - **Skills CLI** (Windsurf, Cline, 40+ agents): `npx skills add scalekit-inc/skills --list` then `--skill <skill-name>`
>
> `<auth-type>` / `<skill-name>`: `agent-auth`, `full-stack-auth`, `mcp-auth`, `modular-sso`, `modular-scim` — [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Authenticate customer apps

This guide explains how you enable API authentication for your customers' applications using Scalekit's OAuth 2.0 client credentials flow. When your customers build applications that need to access your API, they use client credentials registered through your Scalekit environment to obtain access tokens. Your API validates these tokens to authorize their requests using JWKS.

## How your customers' applications authenticate with your API

Your Scalekit environment functions as an OAuth 2.0 Authorization Server. Your customers' applications authenticate using the client credentials flow, exchanging their registered client ID and secret for access tokens that authorize API requests to your platform.

### Storing client credentials

Your customers' applications securely store the credentials you issued to them in environment variables. This example shows how their applications would store these credentials:

```sh title="Environment variables in customer's application"
YOURAPP_ENVIRONMENT_URL="<YOURAPP_SCALEKIT_ENVIRONMENT_URL>"
YOURAPP_CLIENT_ID="<YOURAPP_CLIENT_ID>"
YOURAPP_CLIENT_SECRET="<YOURAPP_CLIENT_SECRET>"
```

These credentials are obtained when you register an API client for your customer (see the [quickstart guide](/authenticate/m2m/api-auth-quickstart/) for client registration).

### Obtaining access tokens

Your customers' applications obtain access tokens from your Scalekit authorization server before making API requests. They send their credentials to your token endpoint:

```sh title="Token endpoint"
https://<YOURAPP_SCALEKIT_ENVIRONMENT_URL>/oauth/token
```

Here's how your customers' applications request access tokens:

```sh
    curl -X POST \
      "https://<YOURAPP_SCALEKIT_ENVIRONMENT_URL>/oauth/token" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=client_credentials" \
      -d "client_id=<YOURAPP_CLIENT_ID>" \
      -d "client_secret=<YOURAPP_CLIENT_SECRET>" \
      -d "scope=openid profile email"
    ```
  ```python
    import os
    import json
    import requests

    # Customer's application configuration
    env_url = os.environ['YOURAPP_SCALEKIT_ENVIRONMENT_URL']

    def get_m2m_access_token():
        """
        Customer's application requests an access token using client credentials.
        This token will be used to authenticate API requests to your platform.
        """
        headers = {"Content-Type": "application/x-www-form-urlencoded"}
        params = {
            "grant_type": "client_credentials",
            "client_id": os.environ['YOURAPP_SCALEKIT_CLIENT_ID'],
            "client_secret": os.environ['YOURAPP_SCALEKIT_CLIENT_SECRET'],
            "scope": "openid profile email"
        }

        response = requests.post(
            url=f"{env_url}/oauth/token",
            headers=headers,
            data=params,
            verify=True
        )

        access_token = response.json().get('access_token')
        return access_token
    ```
  Your authorization server returns a JSON response containing the access token:

```json title="Token response"
{
  "access_token": "<YOURAPP_ACCESS_TOKEN>",
  "token_type": "Bearer",
  "expires_in": 86399,
  "scope": "openid"
}
```

| Field | Description |
|-------|-------------|
| `access_token` | Token for authenticating API requests |
| `token_type` | Always "Bearer" for this flow |
| `expires_in` | Token validity period in seconds (typically 24 hours) |
| `scope` | Authorized scopes for this token |

### Using access tokens

After obtaining an access token, your customers' applications include it in the Authorization header when making requests to your API:

```sh title="Customer's application making an API request"
curl --request GET "https://<your-api-endpoint>" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <YOURAPP_ACCESS_TOKEN>"
```

## Validating access tokens

Your API server must validate access tokens before processing requests. Scalekit uses JSON Web Tokens (JWTs) signed with RSA keys, which you validate using the JSON Web Key Set (JWKS) endpoint.

### Retrieving JWKS

Your application should fetch the public keys from the JWKS endpoint:

```sh title="JWKS endpoint"
https://<YOURAPP_ENVIRONMENT_URL>/keys
```

```json title="JWKS response"
{
  "keys": [
    {
      "use": "sig",
      "kty": "RSA",
      "kid": "snk_58327480989122566",
      "alg": "RS256",
      "n": "wUaqIj3pIE_zfGN9u4GySZs862F-0Kl-..",
      "e": "AQAB"
    }
  ]
}
```

### Token validation process

When your API receives a request with a JWT, follow these steps:

1. Extract the token from the Authorization header
2. Fetch the JWKS from the endpoint
3. Use the public key from JWKS to verify the token's signature
4. Validate the token's claims (issuer, audience, expiration)

This example shows how to fetch JWKS data:

```sh title="Fetch JWKS with cURL"
curl -s "https://<YOURAPP_ENVIRONMENT_URL>/keys" | jq
```

```javascript title="Express.js" wrap
    const express = require('express');
    const jwt = require('jsonwebtoken');
    const jwksClient = require('jwks-rsa');
    const app = express();

    // Initialize JWKS client to validate tokens from customer applications
    // This fetches public keys from your Scalekit environment
    const client = jwksClient({
      jwksUri: `https://<YOURAPP_ENVIRONMENT_URL>/keys`
    });

    // Function to get signing key for token verification
    function getKey(header, callback) {
      client.getSigningKey(header.kid, function(err, key) {
        if (err) return callback(err);

        const signingKey = key.publicKey || key.rsaPublicKey;
        callback(null, signingKey);
      });
    }

    // Middleware to validate JWT from customer's API client application
    function validateJwt(req, res, next) {
      // Extract token sent by customer's application
      const authHeader = req.headers.authorization;
      if (!authHeader || !authHeader.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'Missing authorization token' });
      }

      const token = authHeader.split(' ')[1];

      // Verify the token signature using JWKS
      jwt.verify(token, getKey, {
        algorithms: ['RS256']
      }, (err, decoded) => {
        if (err) {
          return res.status(401).json({ error: 'Invalid token', details: err.message });
        }

        // Token is valid - add decoded claims to request
        req.user = decoded;
        next();
      });
    }

    // Apply validation middleware to your API routes
    app.use('/api', validateJwt);

    // Example protected API endpoint
    app.get('/api/data', (req, res) => {
      res.json({
        message: 'Customer application authenticated successfully',
        userId: req.user.sub
      });
    });

    app.listen(3000, () => {
      console.log('API server running on port 3000');
    });
    ```
  ```python title="Flask" wrap collapse={1-9}
    from scalekit import ScalekitClient
    import os

    # Initialize Scalekit SDK to validate tokens from customer applications
    scalekit_client = ScalekitClient(
        env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
        client_id=os.getenv("SCALEKIT_CLIENT_ID"),
        client_secret=os.getenv("SCALEKIT_CLIENT_SECRET")
    )

    def validate_api_request(request):
        """
        Validate access token from customer's API client application.
        Your API uses this to authorize requests from customer applications.
        """
        # Extract token sent by customer's application
        auth_header = request.headers.get('Authorization')
        if not auth_header or not auth_header.startswith('Bearer '):
            return None, "Missing authorization token"

        token = auth_header.split(' ')[1]

        try:
            # Validate token and extract claims using Scalekit SDK
            claims = scalekit_client.validate_access_token_and_get_claims(
                token=token
            )

            # Token is valid - return claims for authorization logic
            return claims, None
        except Exception as e:
            return None, f"Invalid token: {str(e)}"

    # Example: Use in your Flask API endpoint
    @app.route('/api/data', methods=['GET'])
    def get_data():
        claims, error = validate_api_request(request)

        if error:
            return {"error": error}, 401

        # Customer application is authenticated
        return {
            "message": "Customer application authenticated successfully",
            "userId": claims.get("sub")
        }
    ```
**Token validation best practices:** When implementing token validation in your API:

1. Always verify the token signature using the public key from JWKS
2. Validate token expiration and required claims (issuer, audience, expiration)
3. Cache JWKS responses to improve performance and reduce latency
4. Implement token revocation checks for sensitive operations
5. Use HTTPS for all API endpoints to prevent token interception
6. Check scopes in the token claims to enforce fine-grained permissions

### SDK support status

All Scalekit SDKs include helpers for validating access tokens:

- **Node.js**: Provides `validateAccessToken` and `validateToken` methods with `TokenValidationOptions` for validating issuer, audience, and required scopes.
- **Python**: Provides `validate_access_token`, `validate_token`, and `validate_access_token_and_get_claims` methods with `TokenValidationOptions` for validating issuer, audience, and required scopes.
- **Go**: Provides `ValidateAccessToken`, generic `ValidateToken[T]`, and `GetAccessTokenClaims` helpers that validate tokens using JWKS and return typed claims with errors. These methods accept `context.Context` as the first argument for cancellation and timeout.
- **Java**: Provides `validateAccessToken` (boolean) and `validateAccessTokenAndGetClaims` (returns claims and throws `APIException`) for token validation in JVM applications.

You can still use standard JWT libraries with the JWKS endpoint, as shown in the examples above, when you need custom validation logic or cannot use an SDK in your API service.

---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
