Public API Access Guide
This guide explains how to access the Enterprise AI Public API using OAuth 2.0 Client Credentials Flow. The Vertical Template provides a server-side proxy pattern that keeps credentials secure while enabling API access.
Overview
The Public API at https://test-api.myenterprise.ai/ provides enterprise AI capabilities. Access is controlled through Microsoft Entra ID (Azure AD) using the Client Credentials Flow.
What is Client Credentials Flow?
Client Credentials Flow is an OAuth 2.0 grant type for server-to-server authentication:
- No user context required - Uses application identity, not user identity
- Server-side only - Credentials never exposed to browser
- Automatic token management - Tokens cached and refreshed automatically
Architecture
PUBLIC API PROXY ARCHITECTURE
+-----------+ +----------------------+ +-------------------+
| Browser | | Next.js API Route | | Entra ID |
| (Client) | | (Server-Side Proxy) | | Token Endpoint |
+-----+-----+ +----------+-----------+ +---------+---------+
| | |
| POST /api/eai/ | |
| public/v3/endpoint | |
| ------------------> | |
| | |
| | POST /oauth2/v2.0/token |
| | grant_type=client_creds |
| | ------------------------>|
| | |
| | { access_token: "..." } |
| | <------------------------|
| | |
| v |
| +---------------+ |
| | Public API | |
| | test-api. | |
| | myenterprise | |
| +---------------+ |
| | |
| { data: ... } | |
| <------------------- | |
Key Points:
- Tokens are acquired server-side using client credentials
- Client (browser) never sees credentials or raw tokens
- Anonymous users can access the API (no user login required)
- Tokens are cached server-side until near expiry
Prerequisites
Before starting, you need:
- Azure/Entra ID Access - Ability to register applications
- Public API Permissions - Admin must grant your app access to the Public API
- Environment Variable Access - Ability to configure
.env.local
Setup Guide
Step 1: Use Your Existing Entra ID App Registration
The Public API uses the same Entra ID app registration as user authentication. You should already have:
ENTRA_TENANT_ID- Your Entra tenant GUIDENTRA_CLIENT_ID- Your app registration client IDENTRA_CLIENT_SECRET- Your app registration secret
If you haven't set these up yet, see the Quickstart Guide.
Step 2: Add API Permissions
- Go to Azure Portal > Microsoft Entra ID > App registrations
- Select your existing app registration
- Go to API permissions
- Click Add a permission
- Select APIs my organization uses
- Find Enterprise AI Public API
- Select Application permissions (not Delegated)
- Select the required permissions
- Click Add permissions
- Click Grant admin consent (requires admin rights)
Step 3: Get the API Scope
The scope is the App ID URI of the Public API:
- If Public API App ID URI is
api://eai-public-api - Add this to your
ENTRA_SCOPES
Contact your Enterprise AI administrator for the correct scope value.
Step 4: Configure Environment
Update your .env.local to include the API scope in ENTRA_SCOPES:
# =============================================================================
# Microsoft Entra ID Configuration
# =============================================================================
# Your Entra tenant and app registration
ENTRA_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ENTRA_TENANT_NAME=yourcompany.onmicrosoft.com
ENTRA_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ENTRA_CLIENT_SECRET=your-secret-value
# OAuth scopes - include your API scope for client credentials flow
# The token service extracts api:// scopes automatically
ENTRA_SCOPES=email offline_access openid profile api://eai-public-api
# Public API base URL
BASE_URL_PUBLIC_API=https://test-api.myenterprise.ai
Note: The token service automatically extracts the
api://scope fromENTRA_SCOPESfor client credentials flow.
Using the API
Client-Side: Calling Through the Proxy
All Public API calls go through the proxy at /api/eai/public/v3/...:
// Call any Public API endpoint through the proxy
const response = await fetch('/api/eai/public/v3/some/endpoint', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data: 'value' }),
});
const data = await response.json();
Using the Address Lookup Hook
The template includes a ready-to-use address lookup hook:
import { useAddressLookup } from '@enterpriseaigroup/client';
function AddressSearch({ tenantId }: { tenantId: string }) {
const { suggestions, status, search } = useAddressLookup(
'/api/eai/public/v3/nsw/address-lookup',
3, // minimum characters before search
tenantId
);
return (
<input
type="text"
onChange={(e) => search(e.target.value)}
placeholder="Search address..."
/>
);
}
Available Endpoints
The proxy forwards all requests matching /api/eai/public/v3/* to the Public API:
| Proxy Route | Public API Endpoint |
|---|---|
/api/eai/public/v3/nsw/address-lookup | /v3/nsw/address-lookup |
/api/eai/public/v3/your/endpoint | /v3/your/endpoint |
Security Best Practices
Secrets Management
- Never commit
.env.local- It's gitignored by default - Use environment variables in deployment - Configure in Vercel/Azure/etc.
- Rotate secrets regularly - Set calendar reminder for expiry
- Use Azure Key Vault for production deployments
Token Security
- Tokens stay server-side - Client never sees access tokens
- Automatic refresh - Token service handles expiry
- No refresh tokens exposed - Client credentials flow doesn't use them
Access Control Options
The proxy routes are public by default (no user login required). To require user authentication:
// In your proxy route, add auth check:
import { auth } from '@/auth';
export async function GET(request: NextRequest) {
const session = await auth();
if (!session) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
// ... proceed with proxy
}
Troubleshooting
"Failed to acquire token" Error
Causes:
- Invalid client ID or secret
- Wrong tenant ID
- App not granted API permissions
- Missing API scope in
ENTRA_SCOPES
Solutions:
- Verify
ENTRA_*values in.env.localmatch Azure portal - Check if admin consent was granted for API permissions
- Verify app is registered in correct tenant
- Ensure
ENTRA_SCOPESincludes yourapi://scope
"401 Unauthorized" from Public API
Causes:
- Token scope doesn't match API expectations
- API permissions not configured
- Token expired (shouldn't happen with caching)
Solutions:
- Verify your API scope (e.g.,
api://eai-public-api) is inENTRA_SCOPES - Check if all required permissions are granted
- Clear token cache and retry
"CORS Error"
This shouldn't happen - The proxy runs server-side, so no CORS issues.
If you see CORS errors:
- You might be calling the Public API directly from browser (don't do this)
- Use the
/api/eai/public/v3/...proxy routes instead
Debugging Token Issues
Check server logs for token acquisition:
# In development, look for these log messages:
[PublicAPI] Token acquisition failed: ...
[PublicAPI Proxy] Error: ...
Implementation Details
Token Service
The token service (src/lib/public-api-token.ts) handles:
- Token acquisition using client credentials
- In-memory caching with 5-minute expiry buffer
- Automatic refresh when token nears expiry
Proxy Route
The proxy route (src/app/api/eai/public/v3/[...path]/route.ts) handles:
- All HTTP methods (GET, POST, PUT, PATCH, DELETE)
- Query parameter forwarding
- Request body forwarding for POST/PUT/PATCH
- Response status preservation
Next Steps
- Review the token service implementation in
src/lib/public-api-token.ts - Explore the proxy route in
src/app/api/eai/public/v3/[...path]/route.ts - Check available hooks in
packages/client/src/hooks/