stack/docs-mintlify/guides/apps/api-keys/overview.mdx
Bilal Godil c91a23ee88 Hexclave rename PR5: rename stack*App local-variable convention
Step 5: rename lowercase local vars stackApp/stackServerApp/stackClientApp/
stackAdminApp -> hexclave* across SDK source, apps, examples, and docs-mintlify
(docs/ excluded). Public StackServerApp/StackClientApp classes and the
useStackApp hook are unchanged. typecheck + lint green.
2026-06-03 12:17:14 -07:00

1286 lines
41 KiB
Plaintext

---
title: "API Keys"
description: "Create and manage API keys for users and teams"
icon: "/images/app-icons/api-keys.svg"
---
The API Keys app enables your users to generate and manage API keys for programmatic access to your backend services. API keys provide a secure way to authenticate requests, allowing developers to associate API calls with specific users or teams. Hexclave provides prebuilt UI components for users and teams to manage their own API keys.
## Overview
API keys allow your users to access your backend services programmatically without interactive authentication.
The flow works as follows: a user or client sends an API request with an API key to your application server. Your server validates the API key with Hexclave, which returns an authenticated User object. Your server then processes the request and returns the response.
Hexclave provides two types of API keys:
### User API keys
User API keys are associated with individual users and allow them to authenticate with your API.
<Tabs>
<Tab title="Next.js Client">
```typescript title="app/components/create-api-key.tsx"
"use client";
import { useUser } from "@hexclave/next";
export default function CreateApiKey() {
const user = useUser({ or: 'redirect' });
const handleCreateKey = async () => {
const apiKey = await user.createApiKey({
description: "My client application",
expiresAt: new Date(Date.now() + (90 * 24 * 60 * 60 * 1000)), // 90 days
});
console.log("API Key created:", apiKey.value);
};
return <button onClick={handleCreateKey}>Create API Key</button>;
}
```
</Tab>
<Tab title="Next.js Server">
```typescript title="app/components/create-api-key.tsx"
import { hexclaveServerApp } from "@/stack/server";
export default async function CreateApiKey() {
const user = await hexclaveServerApp.getUser({ or: 'redirect' });
const apiKey = await user.createApiKey({
description: "Admin-provisioned API key",
expiresAt: new Date(Date.now() + (30 * 24 * 60 * 60 * 1000)), // 30 days
});
return <div>API Key: {apiKey.value}</div>;
}
```
</Tab>
<Tab title="React">
```typescript title="components/CreateApiKey.tsx"
"use client";
import { useUser } from "@hexclave/react";
export default function CreateApiKey() {
const user = useUser({ or: 'redirect' });
const handleCreateKey = async () => {
const apiKey = await user.createApiKey({
description: "My client application",
expiresAt: new Date(Date.now() + (90 * 24 * 60 * 60 * 1000)), // 90 days
});
console.log("API Key created:", apiKey.value);
};
return <button onClick={handleCreateKey}>Create API Key</button>;
}
```
</Tab>
<Tab title="Django">
```python title="views.py"
import requests
from django.http import JsonResponse
def create_user_api_key(request):
# Get the current user's access token from session/cookie
access_token = request.COOKIES.get('stack-access-token')
# Create API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/user-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
},
json={
'user_id': 'me',
'description': 'My client application',
'expires_at_millis': int((time.time() + 90 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
raise Exception(f"Failed to create API key: {response.text}")
return JsonResponse(response.json())
```
</Tab>
<Tab title="FastAPI">
```python title="main.py"
import requests
import time
from fastapi import Cookie, HTTPException
@app.post("/api/create-user-api-key")
async def create_user_api_key(stack_access_token: str = Cookie(None, alias="stack-access-token")):
if not stack_access_token:
raise HTTPException(status_code=401, detail="Not authenticated")
# Create API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/user-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': stack_access_token,
},
json={
'user_id': 'me',
'description': 'My client application',
'expires_at_millis': int((time.time() + 90 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
raise HTTPException(status_code=response.status_code, detail=response.text)
return response.json()
```
</Tab>
<Tab title="Flask">
```python title="app.py"
import requests
import time
from flask import request, jsonify
@app.route('/api/create-user-api-key', methods=['POST'])
def create_user_api_key():
access_token = request.cookies.get('stack-access-token')
if not access_token:
return jsonify({'error': 'Not authenticated'}), 401
# Create API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/user-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
},
json={
'user_id': 'me',
'description': 'My client application',
'expires_at_millis': int((time.time() + 90 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
return jsonify({'error': response.text}), response.status_code
return jsonify(response.json())
```
</Tab>
</Tabs>
### Team API keys
Team API keys are associated with teams and can be used to provide access to team resources over your API.
<Tabs>
<Tab title="Next.js Client">
```typescript title="app/components/create-team-api-key.tsx"
"use client";
import { useUser } from "@hexclave/next";
export default function CreateTeamApiKey({ teamId }: { teamId: string }) {
const user = useUser({ or: 'redirect' });
const team = user.useTeam(teamId);
const handleCreateKey = async () => {
if (!team) return;
const teamApiKey = await team.createApiKey({
description: "Team integration service",
expiresAt: new Date(Date.now() + (60 * 24 * 60 * 60 * 1000)), // 60 days
});
console.log("Team API Key created:", teamApiKey.value);
};
return <button onClick={handleCreateKey}>Create Team API Key</button>;
}
```
</Tab>
<Tab title="Next.js Server">
```typescript title="app/components/create-team-api-key.tsx"
import { hexclaveServerApp } from "@/stack/server";
export default async function CreateTeamApiKey({ teamId }: { teamId: string }) {
const team = await hexclaveServerApp.getTeam(teamId);
if (!team) {
return <div>Team not found</div>;
}
const teamApiKey = await team.createApiKey({
description: "Admin-provisioned team API key",
expiresAt: new Date(Date.now() + (30 * 24 * 60 * 60 * 1000)), // 30 days
});
return <div>Team API Key: {teamApiKey.value}</div>;
}
```
</Tab>
<Tab title="React">
```typescript title="components/CreateTeamApiKey.tsx"
"use client";
import { useUser } from "@hexclave/react";
export default function CreateTeamApiKey({ teamId }: { teamId: string }) {
const user = useUser({ or: 'redirect' });
const team = user.useTeam(teamId);
const handleCreateKey = async () => {
if (!team) return;
const teamApiKey = await team.createApiKey({
description: "Team integration service",
expiresAt: new Date(Date.now() + (60 * 24 * 60 * 60 * 1000)), // 60 days
});
console.log("Team API Key created:", teamApiKey.value);
};
return <button onClick={handleCreateKey}>Create Team API Key</button>;
}
```
</Tab>
<Tab title="Django">
```python title="views.py"
import requests
import time
from django.http import JsonResponse
def create_team_api_key(request, team_id):
# Get the current user's access token from session/cookie
access_token = request.COOKIES.get('stack-access-token')
# Create team API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/team-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
},
json={
'team_id': team_id,
'description': 'Team integration service',
'expires_at_millis': int((time.time() + 60 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
raise Exception(f"Failed to create team API key: {response.text}")
return JsonResponse(response.json())
```
</Tab>
<Tab title="FastAPI">
```python title="main.py"
import requests
import time
from fastapi import Cookie, HTTPException
@app.post("/api/teams/{team_id}/api-keys")
async def create_team_api_key(team_id: str, stack_access_token: str = Cookie(None, alias="stack-access-token")):
if not stack_access_token:
raise HTTPException(status_code=401, detail="Not authenticated")
# Create team API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/team-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': stack_access_token,
},
json={
'team_id': team_id,
'description': 'Team integration service',
'expires_at_millis': int((time.time() + 60 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
raise HTTPException(status_code=response.status_code, detail=response.text)
return response.json()
```
</Tab>
<Tab title="Flask">
```python title="app.py"
import requests
import time
from flask import request, jsonify
@app.route('/api/teams/<team_id>/api-keys', methods=['POST'])
def create_team_api_key(team_id):
access_token = request.cookies.get('stack-access-token')
if not access_token:
return jsonify({'error': 'Not authenticated'}), 401
# Create team API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/team-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
},
json={
'team_id': team_id,
'description': 'Team integration service',
'expires_at_millis': int((time.time() + 60 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
return jsonify({'error': response.text}), response.status_code
return jsonify(response.json())
```
</Tab>
</Tabs>
## Enabling the API Keys App
To use API keys in your application, you need to enable the API Keys app in your Hexclave dashboard:
1. Navigate to your Hexclave dashboard
2. Go to the **Apps** section
3. Find and click on **API Keys** in the app store
4. Click the **Enable** button
Once enabled, you can configure User API Keys and Team API Keys in the app settings. The app will provide your users with a prebuilt UI to manage their own API keys.
## Prebuilt UI Components
Hexclave provides prebuilt UI components that allow your users to manage their own API keys without any additional code:
### User API Keys UI
For frameworks that support React components, the `<AccountSettings>` component includes an API Keys tab where users can:
- View all their active API keys
- Create new API keys with custom descriptions and expiration dates
- Revoke existing API keys
- See when each key was created and when it expires.
<Tabs>
<Tab title="Next.js">
```typescript title="app/src/account-page.tsx"
import { AccountSettings } from '@hexclave/next';
export default function MyAccountPage() {
return (
<AccountSettings
fullPage={true}
/>
);
}
```
</Tab>
<Tab title="React">
```typescript title="app/src/account-page.tsx"
import { AccountSettings } from '@hexclave/react';
export default function MyAccountPage() {
return (
<AccountSettings
fullPage={true}
/>
);
}
```
</Tab>
</Tabs>
### Team API Keys UI
For team API keys, the team settings page automatically includes an API Keys section when:
- The API Keys app is enabled
- `allowTeamApiKeys` is configured in your project settings
- The user has the `$manage_api_keys` permission for the team
Users with appropriate permissions can manage team API keys directly from the team settings interface.
## Working with API Keys
### Creating User API Keys
<Tabs>
<Tab title="Next.js Client">
```typescript title="app/components/create-api-key.tsx"
"use client";
import { useUser } from "@hexclave/next";
export default function CreateApiKey() {
const user = useUser({ or: 'redirect' });
const handleCreateKey = async () => {
const apiKey = await user.createApiKey({
description: "My client application",
expiresAt: new Date(Date.now() + (90 * 24 * 60 * 60 * 1000)), // 90 days
});
console.log("API Key created:", apiKey.value);
};
return <button onClick={handleCreateKey}>Create API Key</button>;
}
```
</Tab>
<Tab title="Next.js Server">
```typescript title="app/components/create-api-key.tsx"
import { hexclaveServerApp } from "@/stack/server";
export default async function CreateApiKey() {
const user = await hexclaveServerApp.getUser({ or: 'redirect' });
const apiKey = await user.createApiKey({
description: "Admin-provisioned API key",
expiresAt: new Date(Date.now() + (30 * 24 * 60 * 60 * 1000)), // 30 days
});
return <div>API Key: {apiKey.value}</div>;
}
```
</Tab>
<Tab title="React">
```typescript title="components/CreateApiKey.tsx"
"use client";
import { useUser } from "@hexclave/react";
export default function CreateApiKey() {
const user = useUser({ or: 'redirect' });
const handleCreateKey = async () => {
const apiKey = await user.createApiKey({
description: "My client application",
expiresAt: new Date(Date.now() + (90 * 24 * 60 * 60 * 1000)), // 90 days
});
console.log("API Key created:", apiKey.value);
};
return <button onClick={handleCreateKey}>Create API Key</button>;
}
```
</Tab>
<Tab title="Django">
```python title="views.py"
import requests
from django.http import JsonResponse
def create_user_api_key(request):
# Get the current user's access token from session/cookie
access_token = request.COOKIES.get('stack-access-token')
# Create API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/user-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
},
json={
'user_id': 'me',
'description': 'My client application',
'expires_at_millis': int((time.time() + 90 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
raise Exception(f"Failed to create API key: {response.text}")
return JsonResponse(response.json())
```
</Tab>
<Tab title="FastAPI">
```python title="main.py"
import requests
import time
from fastapi import Cookie, HTTPException
@app.post("/api/create-user-api-key")
async def create_user_api_key(stack_access_token: str = Cookie(None, alias="stack-access-token")):
if not stack_access_token:
raise HTTPException(status_code=401, detail="Not authenticated")
# Create API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/user-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': stack_access_token,
},
json={
'user_id': 'me',
'description': 'My client application',
'expires_at_millis': int((time.time() + 90 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
raise HTTPException(status_code=response.status_code, detail=response.text)
return response.json()
```
</Tab>
<Tab title="Flask">
```python title="app.py"
import requests
import time
from flask import request, jsonify
@app.route('/api/create-user-api-key', methods=['POST'])
def create_user_api_key():
access_token = request.cookies.get('stack-access-token')
if not access_token:
return jsonify({'error': 'Not authenticated'}), 401
# Create API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/user-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
},
json={
'user_id': 'me',
'description': 'My client application',
'expires_at_millis': int((time.time() + 90 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
return jsonify({'error': response.text}), response.status_code
return jsonify(response.json())
```
</Tab>
</Tabs>
### Creating Team API Keys
<Tabs>
<Tab title="Next.js Client">
```typescript title="app/components/create-team-api-key.tsx"
"use client";
import { useUser } from "@hexclave/next";
export default function CreateTeamApiKey({ teamId }: { teamId: string }) {
const user = useUser({ or: 'redirect' });
const team = user.useTeam(teamId);
const handleCreateKey = async () => {
if (!team) return;
const teamApiKey = await team.createApiKey({
description: "Team integration service",
expiresAt: new Date(Date.now() + (60 * 24 * 60 * 60 * 1000)), // 60 days
});
console.log("Team API Key created:", teamApiKey.value);
};
return <button onClick={handleCreateKey}>Create Team API Key</button>;
}
```
</Tab>
<Tab title="Next.js Server">
```typescript title="app/components/create-team-api-key.tsx"
import { hexclaveServerApp } from "@/stack/server";
export default async function CreateTeamApiKey({ teamId }: { teamId: string }) {
const team = await hexclaveServerApp.getTeam(teamId);
if (!team) {
return <div>Team not found</div>;
}
const teamApiKey = await team.createApiKey({
description: "Admin-provisioned team API key",
expiresAt: new Date(Date.now() + (30 * 24 * 60 * 60 * 1000)), // 30 days
});
return <div>Team API Key: {teamApiKey.value}</div>;
}
```
</Tab>
<Tab title="React">
```typescript title="components/CreateTeamApiKey.tsx"
"use client";
import { useUser } from "@hexclave/react";
export default function CreateTeamApiKey({ teamId }: { teamId: string }) {
const user = useUser({ or: 'redirect' });
const team = user.useTeam(teamId);
const handleCreateKey = async () => {
if (!team) return;
const teamApiKey = await team.createApiKey({
description: "Team integration service",
expiresAt: new Date(Date.now() + (60 * 24 * 60 * 60 * 1000)), // 60 days
});
console.log("Team API Key created:", teamApiKey.value);
};
return <button onClick={handleCreateKey}>Create Team API Key</button>;
}
```
</Tab>
<Tab title="Django">
```python title="views.py"
import requests
import time
from django.http import JsonResponse
def create_team_api_key(request, team_id):
# Get the current user's access token from session/cookie
access_token = request.COOKIES.get('stack-access-token')
# Create team API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/team-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
},
json={
'team_id': team_id,
'description': 'Team integration service',
'expires_at_millis': int((time.time() + 60 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
raise Exception(f"Failed to create team API key: {response.text}")
return JsonResponse(response.json())
```
</Tab>
<Tab title="FastAPI">
```python title="main.py"
import requests
import time
from fastapi import Cookie, HTTPException
@app.post("/api/teams/{team_id}/api-keys")
async def create_team_api_key(team_id: str, stack_access_token: str = Cookie(None, alias="stack-access-token")):
if not stack_access_token:
raise HTTPException(status_code=401, detail="Not authenticated")
# Create team API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/team-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': stack_access_token,
},
json={
'team_id': team_id,
'description': 'Team integration service',
'expires_at_millis': int((time.time() + 60 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
raise HTTPException(status_code=response.status_code, detail=response.text)
return response.json()
```
</Tab>
<Tab title="Flask">
```python title="app.py"
import requests
import time
from flask import request, jsonify
@app.route('/api/teams/<team_id>/api-keys', methods=['POST'])
def create_team_api_key(team_id):
access_token = request.cookies.get('stack-access-token')
if not access_token:
return jsonify({'error': 'Not authenticated'}), 401
# Create team API key via client API
response = requests.post(
'https://api.hexclave.com/api/v1/team-api-keys',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
},
json={
'team_id': team_id,
'description': 'Team integration service',
'expires_at_millis': int((time.time() + 60 * 24 * 60 * 60) * 1000),
}
)
if response.status_code != 200:
return jsonify({'error': response.text}), response.status_code
return jsonify(response.json())
```
</Tab>
</Tabs>
### Listing API Keys
<Tabs>
<Tab title="Next.js Client">
```typescript title="app/components/api-keys-list.tsx"
"use client";
import { useUser } from "@hexclave/next";
export default function ApiKeysList() {
const user = useUser({ or: 'redirect' });
const apiKeys = user.useApiKeys();
return (
<div>
<h2>Your API Keys</h2>
{apiKeys.map(key => (
<div key={key.id}>
<p>{key.description}</p>
<p>Last 4 digits: {key.value.lastFour}</p>
<p>Created: {key.createdAt.toLocaleDateString()}</p>
</div>
))}
</div>
);
}
```
</Tab>
<Tab title="Next.js Server">
```typescript title="app/components/api-keys-list.tsx"
import { hexclaveServerApp } from "@/stack/server";
export default async function ApiKeysList() {
const user = await hexclaveServerApp.getUser({ or: 'redirect' });
const apiKeys = await user.listApiKeys();
return (
<div>
<h2>Your API Keys</h2>
{apiKeys.map(key => (
<div key={key.id}>
<p>{key.description}</p>
<p>Last 4 digits: {key.value.lastFour}</p>
<p>Created: {key.createdAt.toLocaleDateString()}</p>
</div>
))}
</div>
);
}
```
</Tab>
<Tab title="React">
```typescript title="components/ApiKeysList.tsx"
"use client";
import { useUser } from "@hexclave/react";
export default function ApiKeysList() {
const user = useUser({ or: 'redirect' });
const apiKeys = user.useApiKeys();
return (
<div>
<h2>Your API Keys</h2>
{apiKeys.map(key => (
<div key={key.id}>
<p>{key.description}</p>
<p>Last 4 digits: {key.value.lastFour}</p>
<p>Created: {key.createdAt.toLocaleDateString()}</p>
</div>
))}
</div>
);
}
```
</Tab>
<Tab title="Django">
```python title="views.py"
import requests
from django.http import JsonResponse
def list_user_api_keys(request):
# Get the current user's access token from session/cookie
access_token = request.COOKIES.get('stack-access-token')
# List user's API keys via client API
response = requests.get(
'https://api.hexclave.com/api/v1/user-api-keys?user_id=me',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
}
)
if response.status_code != 200:
raise Exception(f"Failed to list API keys: {response.text}")
return JsonResponse(response.json(), safe=False)
```
</Tab>
<Tab title="FastAPI">
```python title="main.py"
import requests
from fastapi import Cookie, HTTPException
@app.get("/api/user-api-keys")
async def list_user_api_keys(stack_access_token: str = Cookie(None, alias="stack-access-token")):
if not stack_access_token:
raise HTTPException(status_code=401, detail="Not authenticated")
# List user's API keys via client API
response = requests.get(
'https://api.hexclave.com/api/v1/user-api-keys?user_id=me',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': stack_access_token,
}
)
if response.status_code != 200:
raise HTTPException(status_code=response.status_code, detail=response.text)
return response.json()
```
</Tab>
<Tab title="Flask">
```python title="app.py"
import requests
from flask import request, jsonify
@app.route('/api/user-api-keys', methods=['GET'])
def list_user_api_keys():
access_token = request.cookies.get('stack-access-token')
if not access_token:
return jsonify({'error': 'Not authenticated'}), 401
# List user's API keys via client API
response = requests.get(
'https://api.hexclave.com/api/v1/user-api-keys?user_id=me',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
}
)
if response.status_code != 200:
return jsonify({'error': response.text}), response.status_code
return jsonify(response.json())
```
</Tab>
</Tabs>
### Revoking API Keys
API keys can be revoked when they are no longer needed or if they have been compromised.
<Tabs>
<Tab title="Next.js Client">
```typescript title="app/components/revoke-api-key.tsx"
"use client";
import { useUser } from "@hexclave/next";
export default function RevokeApiKey({ apiKeyId }: { apiKeyId: string }) {
const user = useUser({ or: 'redirect' });
const apiKeys = user.useApiKeys();
const handleRevoke = async () => {
const apiKeyToRevoke = apiKeys.find(key => key.id === apiKeyId);
if (apiKeyToRevoke) {
await apiKeyToRevoke.revoke();
console.log("API Key revoked");
}
};
return <button onClick={handleRevoke}>Revoke API Key</button>;
}
```
</Tab>
<Tab title="Next.js Server">
```typescript title="lib/api-keys.ts"
import { hexclaveServerApp } from "@/stack/server";
export async function revokeApiKey(userId: string, apiKeyId: string) {
const user = await hexclaveServerApp.getUser(userId);
if (!user) return;
const apiKeys = await user.listApiKeys();
const apiKeyToRevoke = apiKeys.find(key => key.id === apiKeyId);
if (apiKeyToRevoke) {
await apiKeyToRevoke.revoke();
}
}
```
</Tab>
<Tab title="React">
```typescript title="components/RevokeApiKey.tsx"
"use client";
import { useUser } from "@hexclave/react";
export default function RevokeApiKey({ apiKeyId }: { apiKeyId: string }) {
const user = useUser({ or: 'redirect' });
const apiKeys = user.useApiKeys();
const handleRevoke = async () => {
const apiKeyToRevoke = apiKeys.find(key => key.id === apiKeyId);
if (apiKeyToRevoke) {
await apiKeyToRevoke.revoke();
console.log("API Key revoked");
}
};
return <button onClick={handleRevoke}>Revoke API Key</button>;
}
```
</Tab>
<Tab title="Django">
```python title="views.py"
import requests
from django.http import JsonResponse
def revoke_api_key(request, api_key_id):
# Get the current user's access token from session/cookie
access_token = request.COOKIES.get('stack-access-token')
# Revoke API key via client API (update with revoked: true)
response = requests.patch(
f'https://api.hexclave.com/api/v1/user-api-keys/{api_key_id}',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
},
json={
'revoked': True,
}
)
if response.status_code != 200:
raise Exception(f"Failed to revoke API key: {response.text}")
return JsonResponse({'message': 'API key revoked successfully'})
```
</Tab>
<Tab title="FastAPI">
```python title="main.py"
import requests
from fastapi import Cookie, HTTPException
@app.delete("/api/user-api-keys/{api_key_id}")
async def revoke_api_key(api_key_id: str, stack_access_token: str = Cookie(None, alias="stack-access-token")):
if not stack_access_token:
raise HTTPException(status_code=401, detail="Not authenticated")
# Revoke API key via client API (update with revoked: true)
response = requests.patch(
f'https://api.hexclave.com/api/v1/user-api-keys/{api_key_id}',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': stack_access_token,
},
json={
'revoked': True,
}
)
if response.status_code != 200:
raise HTTPException(status_code=response.status_code, detail=response.text)
return {"message": "API key revoked successfully"}
```
</Tab>
<Tab title="Flask">
```python title="app.py"
import requests
from flask import request, jsonify
@app.route('/api/user-api-keys/<api_key_id>', methods=['DELETE'])
def revoke_api_key(api_key_id):
access_token = request.cookies.get('stack-access-token')
if not access_token:
return jsonify({'error': 'Not authenticated'}), 401
# Revoke API key via client API (update with revoked: true)
response = requests.patch(
f'https://api.hexclave.com/api/v1/user-api-keys/{api_key_id}',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
},
json={
'revoked': True,
}
)
if response.status_code != 200:
return jsonify({'error': response.text}), response.status_code
return jsonify({'message': 'API key revoked successfully'})
```
</Tab>
</Tabs>
### Checking API Key Validity
You can check if an API key is still valid:
<Tabs>
<Tab title="Next.js Client">
```typescript title="app/components/check-api-key.tsx"
"use client";
import { useUser } from "@hexclave/next";
export default function CheckApiKeyValidity({ apiKeyId }: { apiKeyId: string }) {
const user = useUser({ or: 'redirect' });
const apiKeys = user.useApiKeys();
const apiKey = apiKeys.find(key => key.id === apiKeyId);
if (!apiKey) {
return <div>API key not found</div>;
}
if (apiKey.isValid()) {
return <div>API key is valid</div>;
}
const reason = apiKey.whyInvalid();
return <div>API key is invalid: {reason}</div>;
}
```
</Tab>
<Tab title="Next.js Server">
```typescript title="app/components/check-api-key.tsx"
import { hexclaveServerApp } from "@/stack/server";
export default async function CheckApiKeyValidity({
userId,
apiKeyId
}: {
userId: string,
apiKeyId: string
}) {
const user = await hexclaveServerApp.getUser(userId);
if (!user) return <div>User not found</div>;
const apiKeys = await user.listApiKeys();
const apiKey = apiKeys.find(key => key.id === apiKeyId);
if (!apiKey) {
return <div>API key not found</div>;
}
if (apiKey.isValid()) {
return <div>API key is valid</div>;
}
const reason = apiKey.whyInvalid();
return <div>API key is invalid: {reason}</div>;
}
```
</Tab>
<Tab title="React">
```typescript title="components/CheckApiKey.tsx"
"use client";
import { useUser } from "@hexclave/react";
export default function CheckApiKeyValidity({ apiKeyId }: { apiKeyId: string }) {
const user = useUser({ or: 'redirect' });
const apiKeys = user.useApiKeys();
const apiKey = apiKeys.find(key => key.id === apiKeyId);
if (!apiKey) {
return <div>API key not found</div>;
}
if (apiKey.isValid()) {
return <div>API key is valid</div>;
}
const reason = apiKey.whyInvalid();
return <div>API key is invalid: {reason}</div>;
}
```
</Tab>
<Tab title="Django">
```python title="views.py"
import requests
import time
from django.http import JsonResponse
def check_api_key_validity(request, api_key_id):
# Get the current user's access token from session/cookie
access_token = request.COOKIES.get('stack-access-token')
# Get API key details via client API
response = requests.get(
f'https://api.hexclave.com/api/v1/user-api-keys/{api_key_id}',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
}
)
if response.status_code != 200:
return JsonResponse({'error': 'API key not found'}, status=404)
api_key = response.json()
# Check if manually revoked
if api_key.get('manually_revoked_at_millis'):
return JsonResponse({
'valid': False,
'reason': 'manually-revoked'
})
# Check if expired
if api_key.get('expires_at_millis'):
if api_key['expires_at_millis'] < time.time() * 1000:
return JsonResponse({
'valid': False,
'reason': 'expired'
})
return JsonResponse({'valid': True})
```
</Tab>
<Tab title="FastAPI">
```python title="main.py"
import requests
import time
from fastapi import Cookie, HTTPException
@app.get("/api/check-api-key/{api_key_id}")
async def check_api_key_validity(api_key_id: str, stack_access_token: str = Cookie(None, alias="stack-access-token")):
if not stack_access_token:
raise HTTPException(status_code=401, detail="Not authenticated")
# Get API key details via client API
response = requests.get(
f'https://api.hexclave.com/api/v1/user-api-keys/{api_key_id}',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': stack_access_token,
}
)
if response.status_code != 200:
raise HTTPException(status_code=404, detail="API key not found")
api_key = response.json()
# Check if manually revoked
if api_key.get('manually_revoked_at_millis'):
return {
'valid': False,
'reason': 'manually-revoked'
}
# Check if expired
if api_key.get('expires_at_millis'):
if api_key['expires_at_millis'] < time.time() * 1000:
return {
'valid': False,
'reason': 'expired'
}
return {'valid': True}
```
</Tab>
<Tab title="Flask">
```python title="app.py"
import requests
import time
from flask import request, jsonify
@app.route('/api/check-api-key/<api_key_id>', methods=['GET'])
def check_api_key_validity(api_key_id):
access_token = request.cookies.get('stack-access-token')
if not access_token:
return jsonify({'error': 'Not authenticated'}), 401
# Get API key details via client API
response = requests.get(
f'https://api.hexclave.com/api/v1/user-api-keys/{api_key_id}',
headers={
'x-stack-access-type': 'client',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-access-token': access_token,
}
)
if response.status_code != 200:
return jsonify({'error': 'API key not found'}), 404
api_key = response.json()
# Check if manually revoked
if api_key.get('manually_revoked_at_millis'):
return jsonify({
'valid': False,
'reason': 'manually-revoked'
})
# Check if expired
if api_key.get('expires_at_millis'):
if api_key['expires_at_millis'] < time.time() * 1000:
return jsonify({
'valid': False,
'reason': 'expired'
})
return jsonify({'valid': True})
```
</Tab>
</Tabs>