stack/docs-mintlify/docs/concepts/api-keys.mdx
Madison 13fccd32b6
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Lint & build / lint_and_build (latest) (push) Has been cancelled
Dev Environment Test With Custom Base Port / restart-dev-and-test-with-custom-base-port (push) Has been cancelled
Dev Environment Test / restart-dev-and-test (push) Has been cancelled
Run setup tests with custom base port / setup-tests-with-custom-base-port (push) Has been cancelled
Run setup tests / setup-tests (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
Add docs-mintlify to root
2026-04-01 14:58:41 -05:00

2141 lines
62 KiB
Plaintext

---
title: "API Keys"
description: "Create and manage API keys for users and teams"
---
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. Stack Auth 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.
Stack Auth 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.
<CodeGroup dropdown>
```tsx Next.js
// app/components/create-api-key.tsx (Client Component)
"use client";
import { useUser } from "@stackframe/stack";
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>;
}
// app/components/create-api-key.tsx (Server Component)
import { stackServerApp } from "@/stack/server";
export async function AdminProvisionedApiKey() {
const user = await stackServerApp.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>;
}
```
```tsx React
// components/CreateApiKey.tsx
"use client";
import { useUser } from "@stackframe/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>;
}
```
```javascript Express
import express from "express";
import { StackServerApp } from "@stackframe/js";
const app = express();
app.use(express.json());
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
// Example: provision a user API key from your backend (admin/service flow).
app.post("/admin/users/:userId/api-keys", async (req, res) => {
const user = await stackServerApp.getUser(req.params.userId);
if (!user) return res.status(404).json({ error: "User not found" });
const apiKey = await user.createApiKey({
description: req.body.description ?? "Admin-provisioned API key",
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});
res.json({ id: apiKey.id, value: apiKey.value });
});
```
```javascript Node.js
import { StackServerApp } from "@stackframe/js";
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
export async function provisionUserApiKey({ userId, description }) {
const user = await stackServerApp.getUser(userId);
if (!user) throw new Error("User not found");
return await user.createApiKey({
description: description ?? "Admin-provisioned API key",
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});
}
```
```javascript Vanilla JavaScript
import { StackClientApp } from "@stackframe/js";
// Browser/client usage: requires the user to already be signed in.
const stackClientApp = new StackClientApp({
tokenStore: "cookie",
});
export async function createUserApiKey() {
const user = await stackClientApp.getUser({ or: "throw" });
const apiKey = await user.createApiKey({
description: "My client application",
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
});
console.log("API Key created:", apiKey.value);
return apiKey;
}
```
```python Django
# views.py
import requests
import time
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.stack-auth.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())
```
```python FastAPI
# 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.stack-auth.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()
```
```python Flask
# 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.stack-auth.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())
```
</CodeGroup>
### Team API keys
Team API keys are associated with teams and can be used to provide access to team resources over your API.
<CodeGroup dropdown>
```tsx Next.js
// app/components/create-team-api-key.tsx (Client Component)
"use client";
import { useUser } from "@stackframe/stack";
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>;
}
// app/components/create-team-api-key.tsx (Server Component)
import { stackServerApp } from "@/stack/server";
export async function AdminProvisionedTeamApiKey({ teamId }: { teamId: string }) {
const team = await stackServerApp.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>;
}
```
```tsx React
// components/CreateTeamApiKey.tsx
"use client";
import { useUser } from "@stackframe/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>;
}
```
```javascript Express
import express from "express";
import { StackServerApp } from "@stackframe/js";
const app = express();
app.use(express.json());
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
// Example: provision a team API key from your backend (admin/service flow).
app.post("/admin/teams/:teamId/api-keys", async (req, res) => {
const team = await stackServerApp.getTeam(req.params.teamId);
if (!team) return res.status(404).json({ error: "Team not found" });
const teamApiKey = await team.createApiKey({
description: req.body.description ?? "Admin-provisioned team API key",
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});
res.json({ id: teamApiKey.id, value: teamApiKey.value });
});
```
```javascript Node.js
import { StackServerApp } from "@stackframe/js";
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
export async function provisionTeamApiKey({ teamId, description }) {
const team = await stackServerApp.getTeam(teamId);
if (!team) throw new Error("Team not found");
return await team.createApiKey({
description: description ?? "Admin-provisioned team API key",
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});
}
```
```javascript Vanilla JavaScript
import { StackClientApp } from "@stackframe/js";
// Browser/client usage: requires the user to already be signed in and a team membership.
const stackClientApp = new StackClientApp({
tokenStore: "cookie",
});
export async function createTeamApiKey(teamId) {
const user = await stackClientApp.getUser({ or: "throw" });
const team = await user.getTeam(teamId);
if (!team) throw new Error("Team not found (or you are not a member)");
const teamApiKey = await team.createApiKey({
description: "Team integration service",
expiresAt: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000),
});
console.log("Team API Key created:", teamApiKey.value);
return teamApiKey;
}
```
```python Django
# 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.stack-auth.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())
```
```python FastAPI
# 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.stack-auth.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()
```
```python Flask
# 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.stack-auth.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())
```
</CodeGroup>
## Enabling the API Keys App
To use API keys in your application, you need to enable the API Keys app in your Stack Auth dashboard:
1. Navigate to your Stack Auth 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
Stack Auth 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.
<CodeGroup dropdown>
```tsx Next.js
import { AccountSettings } from "@stackframe/stack";
export default function MyAccountPage() {
return <AccountSettings fullPage={true} />;
}
```
```tsx React
import { AccountSettings } from "@stackframe/react";
export default function MyAccountPage() {
return <AccountSettings fullPage={true} />;
}
```
```javascript Express
// Not applicable: <AccountSettings /> is a React UI component.
// Use a React/Next.js frontend, and call your Express API routes from it.
```
```javascript Node.js
// Not applicable: <AccountSettings /> is a React UI component.
// Use a React/Next.js frontend, and run your Node.js backend separately.
```
```javascript Vanilla JavaScript
// Not applicable: <AccountSettings /> is a React UI component.
// If you're not using React, build a custom UI and use the API key methods instead.
```
```python Django
# Not applicable: <AccountSettings /> is a React UI component.
# Use a React/Next.js frontend, and expose Django endpoints for your app as needed.
```
```python FastAPI
# Not applicable: <AccountSettings /> is a React UI component.
# Use a React/Next.js frontend, and expose FastAPI endpoints for your app as needed.
```
```python Flask
# Not applicable: <AccountSettings /> is a React UI component.
# Use a React/Next.js frontend, and expose Flask endpoints for your app as needed.
```
</CodeGroup>
### 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
<CodeGroup dropdown>
```tsx Next.js
// app/components/create-api-key.tsx (Client Component)
"use client";
import { useUser } from "@stackframe/stack";
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>;
}
// app/components/create-api-key.tsx (Server Component)
import { stackServerApp } from "@/stack/server";
export async function AdminProvisionedApiKey() {
const user = await stackServerApp.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>;
}
```
```tsx React
// components/CreateApiKey.tsx
"use client";
import { useUser } from "@stackframe/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>;
}
```
```javascript Express
import express from "express";
import { StackServerApp } from "@stackframe/js";
const app = express();
app.use(express.json());
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
app.post("/admin/users/:userId/api-keys", async (req, res) => {
const user = await stackServerApp.getUser(req.params.userId);
if (!user) return res.status(404).json({ error: "User not found" });
const apiKey = await user.createApiKey({
description: req.body.description ?? "Admin-provisioned API key",
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});
res.json({ id: apiKey.id, value: apiKey.value });
});
```
```javascript Node.js
import { StackServerApp } from "@stackframe/js";
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
export async function provisionUserApiKey({ userId, description }) {
const user = await stackServerApp.getUser(userId);
if (!user) throw new Error("User not found");
return await user.createApiKey({
description: description ?? "Admin-provisioned API key",
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});
}
```
```javascript Vanilla JavaScript
import { StackClientApp } from "@stackframe/js";
const stackClientApp = new StackClientApp({
tokenStore: "cookie",
});
export async function createUserApiKey() {
const user = await stackClientApp.getUser({ or: "throw" });
const apiKey = await user.createApiKey({
description: "My client application",
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
});
console.log("API Key created:", apiKey.value);
return apiKey;
}
```
```python Django
# views.py
import requests
import time
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.stack-auth.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())
```
```python FastAPI
# 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.stack-auth.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()
```
```python Flask
# 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.stack-auth.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())
```
</CodeGroup>
### Creating Team API Keys
<CodeGroup dropdown>
```tsx Next.js
// app/components/create-team-api-key.tsx (Client Component)
"use client";
import { useUser } from "@stackframe/stack";
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>;
}
// app/components/create-team-api-key.tsx (Server Component)
import { stackServerApp } from "@/stack/server";
export async function AdminProvisionedTeamApiKey({ teamId }: { teamId: string }) {
const team = await stackServerApp.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>;
}
```
```tsx React
// components/CreateTeamApiKey.tsx
"use client";
import { useUser } from "@stackframe/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>;
}
```
```javascript Express
import express from "express";
import { StackServerApp } from "@stackframe/js";
const app = express();
app.use(express.json());
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
app.post("/admin/teams/:teamId/api-keys", async (req, res) => {
const team = await stackServerApp.getTeam(req.params.teamId);
if (!team) return res.status(404).json({ error: "Team not found" });
const teamApiKey = await team.createApiKey({
description: req.body.description ?? "Admin-provisioned team API key",
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});
res.json({ id: teamApiKey.id, value: teamApiKey.value });
});
```
```javascript Node.js
import { StackServerApp } from "@stackframe/js";
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
export async function provisionTeamApiKey({ teamId, description }) {
const team = await stackServerApp.getTeam(teamId);
if (!team) throw new Error("Team not found");
return await team.createApiKey({
description: description ?? "Admin-provisioned team API key",
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});
}
```
```javascript Vanilla JavaScript
import { StackClientApp } from "@stackframe/js";
const stackClientApp = new StackClientApp({
tokenStore: "cookie",
});
export async function createTeamApiKey(teamId) {
const user = await stackClientApp.getUser({ or: "throw" });
const team = await user.getTeam(teamId);
if (!team) throw new Error("Team not found (or you are not a member)");
const teamApiKey = await team.createApiKey({
description: "Team integration service",
expiresAt: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000),
});
console.log("Team API Key created:", teamApiKey.value);
return teamApiKey;
}
```
```python Django
# 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.stack-auth.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())
```
```python FastAPI
# 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.stack-auth.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()
```
```python Flask
# 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.stack-auth.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())
```
</CodeGroup>
### Listing API Keys
<CodeGroup dropdown>
```tsx Next.js
// app/components/api-keys-list.tsx (Client Component)
"use client";
import { useUser } from "@stackframe/stack";
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>
);
}
// app/components/api-keys-list.tsx (Server Component)
import { stackServerApp } from "@/stack/server";
export async function ApiKeysListServer() {
const user = await stackServerApp.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>
);
}
```
```tsx React
// components/ApiKeysList.tsx
"use client";
import { useUser } from "@stackframe/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>
);
}
```
```javascript Express
import express from "express";
import { StackServerApp } from "@stackframe/js";
const app = express();
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
app.get("/admin/users/:userId/api-keys", async (req, res) => {
const user = await stackServerApp.getUser(req.params.userId);
if (!user) return res.status(404).json({ error: "User not found" });
const apiKeys = await user.listApiKeys();
res.json(
apiKeys.map((key) => ({
id: key.id,
description: key.description,
lastFour: key.value.lastFour,
createdAt: key.createdAt,
expiresAt: key.expiresAt,
revokedAt: key.manuallyRevokedAt,
})),
);
});
```
```javascript Node.js
import { StackServerApp } from "@stackframe/js";
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
export async function listUserApiKeys({ userId }) {
const user = await stackServerApp.getUser(userId);
if (!user) throw new Error("User not found");
return await user.listApiKeys();
}
```
```javascript Vanilla JavaScript
import { StackClientApp } from "@stackframe/js";
const stackClientApp = new StackClientApp({
tokenStore: "cookie",
});
export async function listMyApiKeys() {
const user = await stackClientApp.getUser({ or: "throw" });
const apiKeys = await user.listApiKeys();
for (const key of apiKeys) {
console.log(key.id, key.description, key.value.lastFour);
}
return apiKeys;
}
```
```python Django
# 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.stack-auth.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)
```
```python FastAPI
# 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.stack-auth.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()
```
```python Flask
# 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.stack-auth.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())
```
</CodeGroup>
### Revoking API Keys
API keys can be revoked when they are no longer needed or if they have been compromised.
<CodeGroup dropdown>
```tsx Next.js
// app/components/revoke-api-key.tsx (Client Component)
"use client";
import { useUser } from "@stackframe/stack";
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>;
}
// lib/api-keys.ts (Server-side helper)
import { stackServerApp } from "@/stack/server";
export async function revokeApiKey(userId: string, apiKeyId: string) {
const user = await stackServerApp.getUser(userId);
if (!user) return;
const apiKeys = await user.listApiKeys();
const apiKeyToRevoke = apiKeys.find((key) => key.id === apiKeyId);
if (apiKeyToRevoke) {
await apiKeyToRevoke.revoke();
}
}
```
```tsx React
// components/RevokeApiKey.tsx
"use client";
import { useUser } from "@stackframe/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>;
}
```
```javascript Express
import express from "express";
import { StackServerApp } from "@stackframe/js";
const app = express();
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
app.delete("/admin/users/:userId/api-keys/:apiKeyId", async (req, res) => {
const user = await stackServerApp.getUser(req.params.userId);
if (!user) return res.status(404).json({ error: "User not found" });
const apiKeys = await user.listApiKeys();
const apiKeyToRevoke = apiKeys.find((key) => key.id === req.params.apiKeyId);
if (!apiKeyToRevoke) return res.status(404).json({ error: "API key not found" });
await apiKeyToRevoke.revoke();
res.json({ message: "API key revoked successfully" });
});
```
```javascript Node.js
import { StackServerApp } from "@stackframe/js";
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
export async function revokeUserApiKey({ userId, apiKeyId }) {
const user = await stackServerApp.getUser(userId);
if (!user) throw new Error("User not found");
const apiKeys = await user.listApiKeys();
const apiKeyToRevoke = apiKeys.find((key) => key.id === apiKeyId);
if (!apiKeyToRevoke) throw new Error("API key not found");
await apiKeyToRevoke.revoke();
}
```
```javascript Vanilla JavaScript
import { StackClientApp } from "@stackframe/js";
const stackClientApp = new StackClientApp({
tokenStore: "cookie",
});
export async function revokeMyApiKey(apiKeyId) {
const user = await stackClientApp.getUser({ or: "throw" });
const apiKeys = await user.listApiKeys();
const apiKeyToRevoke = apiKeys.find((key) => key.id === apiKeyId);
if (!apiKeyToRevoke) throw new Error("API key not found");
await apiKeyToRevoke.revoke();
}
```
```python Django
# 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.stack-auth.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"})
```
```python FastAPI
# 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.stack-auth.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"}
```
```python Flask
# 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.stack-auth.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"})
```
</CodeGroup>
### Checking API Key Validity
You can check if an API key is still valid:
<CodeGroup dropdown>
```tsx Next.js
// app/components/check-api-key.tsx (Client Component)
"use client";
import { useUser } from "@stackframe/stack";
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>;
}
// app/components/check-api-key.tsx (Server Component)
import { stackServerApp } from "@/stack/server";
export async function CheckApiKeyValidityServer({
userId,
apiKeyId,
}: {
userId: string;
apiKeyId: string;
}) {
const user = await stackServerApp.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>;
}
```
```tsx React
// components/CheckApiKey.tsx
"use client";
import { useUser } from "@stackframe/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>;
}
```
```javascript Express
import express from "express";
import { StackServerApp } from "@stackframe/js";
const app = express();
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
app.get("/admin/users/:userId/api-keys/:apiKeyId/validity", async (req, res) => {
const user = await stackServerApp.getUser(req.params.userId);
if (!user) return res.status(404).json({ error: "User not found" });
const apiKeys = await user.listApiKeys();
const apiKey = apiKeys.find((key) => key.id === req.params.apiKeyId);
if (!apiKey) return res.status(404).json({ error: "API key not found" });
res.json({
valid: apiKey.isValid(),
reason: apiKey.isValid() ? null : apiKey.whyInvalid(),
});
});
```
```javascript Node.js
import { StackServerApp } from "@stackframe/js";
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
export async function checkApiKeyValidity({ userId, apiKeyId }) {
const user = await stackServerApp.getUser(userId);
if (!user) throw new Error("User not found");
const apiKeys = await user.listApiKeys();
const apiKey = apiKeys.find((key) => key.id === apiKeyId);
if (!apiKey) throw new Error("API key not found");
return {
valid: apiKey.isValid(),
reason: apiKey.isValid() ? null : apiKey.whyInvalid(),
};
}
```
```javascript Vanilla JavaScript
import { StackClientApp } from "@stackframe/js";
const stackClientApp = new StackClientApp({
tokenStore: "cookie",
});
export async function checkMyApiKeyValidity(apiKeyId) {
const user = await stackClientApp.getUser({ or: "throw" });
const apiKeys = await user.listApiKeys();
const apiKey = apiKeys.find((key) => key.id === apiKeyId);
if (!apiKey) throw new Error("API key not found");
return {
valid: apiKey.isValid(),
reason: apiKey.isValid() ? null : apiKey.whyInvalid(),
};
}
```
```python Django
# 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.stack-auth.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})
```
```python FastAPI
# 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.stack-auth.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}
```
```python Flask
# 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.stack-auth.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})
```
</CodeGroup>
## Authenticating Requests with API Keys
To validate incoming API requests with API keys on your server, use the `getUser` or `getTeam` methods with the `apiKey` option:
### Validating User API Keys
<CodeGroup dropdown>
```typescript Next.js
import { stackServerApp } from "@/stack/server";
export async function GET(request: Request) {
// Extract the API key from the request headers
const apiKey = request.headers.get("X-Stack-Api-Key");
if (!apiKey) {
return Response.json({ error: "API key required" }, { status: 401 });
}
// Validate the API key and get the associated user
const user = await stackServerApp.getUser({ apiKey });
if (!user) {
return Response.json({ error: "Invalid API key" }, { status: 401 });
}
// Process the request with the authenticated user
const data = {
userId: user.id,
email: user.primaryEmail,
// Your API logic here
};
return Response.json(data);
}
```
```tsx React
// API key validation must happen on a server.
// Use the Express/Node.js/Next.js examples in your backend, then call it from React.
```
```javascript Express
import express from "express";
import { StackServerApp } from "@stackframe/js";
const app = express();
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
app.get("/api/protected", async (req, res) => {
const apiKey = req.headers["x-stack-api-key"];
if (!apiKey) {
return res.status(401).json({ error: "API key required" });
}
const user = await stackServerApp.getUser({ apiKey });
if (!user) {
return res.status(401).json({ error: "Invalid API key" });
}
res.json({
userId: user.id,
email: user.primaryEmail,
});
});
```
```javascript Node.js
import { StackServerApp } from "@stackframe/js";
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
async function validateApiKey(apiKey) {
const user = await stackServerApp.getUser({ apiKey });
if (!user) {
throw new Error("Invalid API key");
}
return user;
}
```
```javascript Vanilla JavaScript
// API key validation must happen on a server.
// If you're not using a framework, validate in a Node.js HTTP server (see the Node.js tab).
```
```python Django
import requests
from django.http import JsonResponse
def protected_view(request):
api_key = request.headers.get("X-Stack-Api-Key")
if not api_key:
return JsonResponse({"error": "API key required"}, status=401)
# Validate API key with Stack Auth server API
response = requests.post(
"https://api.stack-auth.com/api/v1/user-api-keys/check",
headers={
"x-stack-access-type": "server",
"x-stack-project-id": stack_project_id,
"x-stack-secret-server-key": stack_secret_server_key,
},
json={
"api_key": api_key,
},
)
if response.status_code != 200:
return JsonResponse({"error": "Invalid API key"}, status=401)
api_key_data = response.json()
return JsonResponse({"userId": api_key_data["user_id"]})
```
```python FastAPI
import requests
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
@app.get("/api/protected")
async def protected_route(x_stack_api_key: str = Header(None)):
if not x_stack_api_key:
raise HTTPException(status_code=401, detail="API key required")
# Validate API key with Stack Auth server API
response = requests.post(
"https://api.stack-auth.com/api/v1/user-api-keys/check",
headers={
"x-stack-access-type": "server",
"x-stack-project-id": stack_project_id,
"x-stack-secret-server-key": stack_secret_server_key,
},
json={
"api_key": x_stack_api_key,
},
)
if response.status_code != 200:
raise HTTPException(status_code=401, detail="Invalid API key")
api_key_data = response.json()
return {"userId": api_key_data["user_id"]}
```
```python Flask
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/api/protected")
def protected_route():
api_key = request.headers.get("X-Stack-Api-Key")
if not api_key:
return jsonify({"error": "API key required"}), 401
# Validate API key with Stack Auth server API
response = requests.post(
"https://api.stack-auth.com/api/v1/user-api-keys/check",
headers={
"x-stack-access-type": "server",
"x-stack-project-id": stack_project_id,
"x-stack-secret-server-key": stack_secret_server_key,
},
json={
"api_key": api_key,
},
)
if response.status_code != 200:
return jsonify({"error": "Invalid API key"}), 401
api_key_data = response.json()
return jsonify({"userId": api_key_data["user_id"]})
```
</CodeGroup>
### Validating Team API Keys
For team API keys, use `getTeam` with the `apiKey` option:
<CodeGroup dropdown>
```typescript Next.js
import { stackServerApp } from "@/stack/server";
export async function POST(request: Request) {
const apiKey = request.headers.get("X-Stack-Api-Key");
if (!apiKey) {
return Response.json({ error: "API key required" }, { status: 401 });
}
// Validate the team API key and get the associated team
const team = await stackServerApp.getTeam({ apiKey });
if (!team) {
return Response.json({ error: "Invalid team API key" }, { status: 401 });
}
// Process team-level request
const teamData = {
teamId: team.id,
teamName: team.displayName,
// Your team API logic here
};
return Response.json(teamData);
}
```
```tsx React
// API key validation must happen on a server.
// Use the Express/Node.js/Next.js examples in your backend, then call it from React.
```
```javascript Express
import express from "express";
import { StackServerApp } from "@stackframe/js";
const app = express();
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
app.post("/api/team-protected", async (req, res) => {
const apiKey = req.headers["x-stack-api-key"];
if (!apiKey) {
return res.status(401).json({ error: "API key required" });
}
const team = await stackServerApp.getTeam({ apiKey });
if (!team) {
return res.status(401).json({ error: "Invalid team API key" });
}
res.json({
teamId: team.id,
teamName: team.displayName,
});
});
```
```javascript Node.js
import { StackServerApp } from "@stackframe/js";
const stackServerApp = new StackServerApp({
projectId: process.env.STACK_PROJECT_ID,
publishableClientKey: process.env.STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
tokenStore: "memory",
});
async function validateTeamApiKey(apiKey) {
const team = await stackServerApp.getTeam({ apiKey });
if (!team) {
throw new Error("Invalid team API key");
}
return team;
}
```
```javascript Vanilla JavaScript
// API key validation must happen on a server.
// If you're not using a framework, validate in a Node.js HTTP server (see the Node.js tab).
```
```python Django
import requests
from django.http import JsonResponse
def team_protected_view(request):
api_key = request.headers.get("X-Stack-Api-Key")
if not api_key:
return JsonResponse({"error": "API key required"}, status=401)
# Validate team API key with Stack Auth server API
response = requests.post(
"https://api.stack-auth.com/api/v1/team-api-keys/check",
headers={
"x-stack-access-type": "server",
"x-stack-project-id": stack_project_id,
"x-stack-secret-server-key": stack_secret_server_key,
},
json={
"api_key": api_key,
},
)
if response.status_code != 200:
return JsonResponse({"error": "Invalid team API key"}, status=401)
api_key_data = response.json()
return JsonResponse({"teamId": api_key_data["team_id"]})
```
```python FastAPI
import requests
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
@app.post("/api/team-protected")
async def team_protected_route(x_stack_api_key: str = Header(None)):
if not x_stack_api_key:
raise HTTPException(status_code=401, detail="API key required")
# Validate team API key with Stack Auth server API
response = requests.post(
"https://api.stack-auth.com/api/v1/team-api-keys/check",
headers={
"x-stack-access-type": "server",
"x-stack-project-id": stack_project_id,
"x-stack-secret-server-key": stack_secret_server_key,
},
json={
"api_key": x_stack_api_key,
},
)
if response.status_code != 200:
raise HTTPException(status_code=401, detail="Invalid team API key")
api_key_data = response.json()
return {"teamId": api_key_data["team_id"]}
```
```python Flask
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/api/team-protected", methods=["POST"])
def team_protected_route():
api_key = request.headers.get("X-Stack-Api-Key")
if not api_key:
return jsonify({"error": "API key required"}), 401
# Validate team API key with Stack Auth server API
response = requests.post(
"https://api.stack-auth.com/api/v1/team-api-keys/check",
headers={
"x-stack-access-type": "server",
"x-stack-project-id": stack_project_id,
"x-stack-secret-server-key": stack_secret_server_key,
},
json={
"api_key": api_key,
},
)
if response.status_code != 200:
return jsonify({"error": "Invalid team API key"}), 401
api_key_data = response.json()
return jsonify({"teamId": api_key_data["team_id"]})
```
</CodeGroup>
### Best Practices for API Key Authentication
1. **Use HTTPS**: Always use HTTPS in production to protect API keys in transit
2. **Validate on every request**: Never trust client-side validation alone
3. **Use appropriate headers**: Common header names include `X-Stack-Api-Key`, `Authorization: Bearer <key>`, or `X-Api-Key`
4. **Rate limiting**: Implement rate limiting to prevent abuse
5. **Monitor usage**: Track API key usage to detect anomalies