This guide covers setting up authentication for the Order API on different cloud providers.
aws cognito-idp create-user-pool \
--pool-name ecommerce-users \
--policies "PasswordPolicy={MinimumLength=8,RequireUppercase=true,RequireLowercase=true,RequireNumbers=true}" \
--auto-verified-attributes email \
--schema Name=email,Required=true Name=tenant_id,AttributeDataType=String,Mutable=true \
--region us-east-1In the AWS Console:
- Go to Cognito User Pools
- Select your pool
- Go to "Attributes"
- Add custom attribute:
custom:tenant_id(String, Mutable)
aws cognito-idp create-user-pool-client \
--user-pool-id <your-pool-id> \
--client-name order-api-client \
--explicit-auth-flows ALLOW_USER_PASSWORD_AUTH ALLOW_REFRESH_TOKEN_AUTH \
--read-attributes email custom:tenant_id \
--region us-east-1# Create user
aws cognito-idp admin-create-user \
--user-pool-id <your-pool-id> \
--username testuser@example.com \
--user-attributes Name=email,Value=testuser@example.com Name=email_verified,Value=true \
--temporary-password TempPass123! \
--region us-east-1
# Set tenant_id
aws cognito-idp admin-update-user-attributes \
--user-pool-id <your-pool-id> \
--username testuser@example.com \
--user-attributes Name=custom:tenant_id,Value=tenant-123 \
--region us-east-1
# Set permanent password
aws cognito-idp admin-set-user-password \
--user-pool-id <your-pool-id> \
--username testuser@example.com \
--password MySecurePass123! \
--permanent \
--region us-east-1# Using AWS CLI
aws cognito-idp initiate-auth \
--auth-flow USER_PASSWORD_AUTH \
--client-id <your-app-client-id> \
--auth-parameters USERNAME=testuser@example.com,PASSWORD=MySecurePass123! \
--region us-east-1
# Extract the IdToken from the responseTOKEN="<IdToken-from-previous-step>"
API_URL="<your-api-gateway-url>"
curl -X POST $API_URL/orders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"customer_name": "John Doe",
"customer_email": "john@example.com",
"total_amount": 99.99,
"items": [
{
"product_name": "Test Product",
"quantity": 1,
"unit_price": 99.99
}
]
}'When properly configured, your API will receive claims like:
{
"sub": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"email": "testuser@example.com",
"email_verified": true,
"custom:tenant_id": "tenant-123",
"aud": "your-app-client-id",
"token_use": "id",
"auth_time": 1234567890,
"iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXXXXX",
"exp": 1234571490,
"iat": 1234567890
}# Create resource group
az group create --name ecommerce-rg --location eastus
# Create B2C tenant (do this in Azure Portal)
# Portal -> Create a resource -> Azure Active Directory B2CIn Azure Portal:
- Go to Azure AD B2C
- User attributes -> Add custom attribute
- Name:
tenant_id, Data Type: String
- User flows -> New user flow
- Sign up and sign in
- Collect attributes: Email, tenant_id
- Return claims: Email, tenant_id, Object ID
# In Azure Portal
# App registrations -> New registration
# Redirect URI: https://your-function-app.azurewebsites.net/.auth/login/aad/callback
# After registration, note:
# - Application (client) ID
# - Directory (tenant) IDaz functionapp auth update \
--name your-function-app \
--resource-group ecommerce-rg \
--enabled true \
--action LoginWithAzureActiveDirectory \
--aad-client-id <your-app-client-id> \
--aad-token-issuer-url https://login.microsoftonline.com/<tenant-id>/v2.0# Get token using OAuth2 flow
# Then call function
curl -X POST https://your-function-app.azurewebsites.net/api/orders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @test_order.json# In Firebase Console
# console.firebase.google.com
# Create new project or select existing# In Firebase Console:
# Authentication -> Sign-in method
# Enable Email/PasswordCreate a Cloud Function to set custom claims:
// setClaims.js
const admin = require('firebase-admin');
admin.initializeApp();
exports.setTenantClaim = async (uid, tenantId) => {
await admin.auth().setCustomUserClaims(uid, {
tenant_id: tenantId
});
};# Using Firebase CLI
firebase auth:import users.json --project your-project-idusers.json:
{
"users": [
{
"uid": "user123",
"email": "test@example.com",
"passwordHash": "...",
"customClaims": {
"tenant_id": "tenant-123"
}
}
]
}# Create API Gateway
gcloud api-gateway apis create order-api
# Configure authentication
# In openapi.yaml, add security scheme:openapi.yaml:
securityDefinitions:
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://securetoken.google.com/YOUR_PROJECT_ID"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken@system.gserviceaccount.com"
x-google-audiences: "YOUR_PROJECT_ID"
paths:
/orders:
post:
security:
- firebase: []// Client-side code
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';
const auth = getAuth();
const userCredential = await signInWithEmailAndPassword(
auth,
'test@example.com',
'password123'
);
const token = await userCredential.user.getIdToken();
// Use token in API requests
fetch('https://your-api-url/orders', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(orderData)
});Create a test script that simulates authenticated requests:
# test_auth.py
import requests
import json
# For local testing, bypass auth temporarily
# or use a mock token
def test_create_order():
# In production, get real token from auth provider
token = "mock-token-for-testing"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
order_data = {
"customer_name": "Test User",
"customer_email": "test@example.com",
"total_amount": 99.99,
"items": [
{
"product_name": "Test Product",
"quantity": 1,
"unit_price": 99.99
}
]
}
response = requests.post(
"http://localhost:3000/orders",
headers=headers,
json=order_data
)
print(f"Status: {response.status_code}")
print(f"Response: {response.json()}")
if __name__ == "__main__":
test_create_order()-
Token Validation
- Always validate tokens on the server
- Check token expiration
- Verify token signature
- Validate issuer
-
Tenant Isolation
- Extract tenant_id from verified claims only
- Never accept tenant_id from request body
- Filter all queries by tenant_id
-
HTTPS Only
- Enforce HTTPS in production
- Use secure headers
- Enable CORS appropriately
-
Rate Limiting
- Implement rate limiting per user/tenant
- Use API Gateway/Azure APIM features
-
Logging
- Log authentication failures
- Monitor for suspicious patterns
- Never log tokens or passwords
-
"Missing tenant_id in claims"
- Verify custom attribute is configured
- Check token includes custom claims
- Decode JWT to inspect claims
-
"Invalid token"
- Check token hasn't expired
- Verify token is for correct client
- Ensure token is ID token, not access token
-
"401 Unauthorized"
- Verify Bearer token format
- Check API Gateway authorizer is configured
- Validate token issuer matches configuration
Decode JWT token:
# Using jwt.io or
echo $TOKEN | cut -d'.' -f2 | base64 -d | jqTest token validity:
# AWS
aws cognito-idp get-user --access-token $ACCESS_TOKEN
# Verify claims
python -c "import jwt; print(jwt.decode('$TOKEN', options={'verify_signature': False}))"