Service Authentication
Deploying Services with DTZ Authentication
DTZ provides built-in authentication capabilities for your deployed services, making it easy to build secure applications without managing your own authentication infrastructure.
Overview
When you deploy a service to DTZ, the platform automatically provides authentication credentials and context information through environment variables. Your service can use these to:
- Authenticate requests to other DTZ services (API calls, object storage, etc.)
- Verify incoming user requests
- Access context-specific resources
- Implement secure service-to-service communication
Environment Variables
DTZ automatically injects the following environment variables into your service containers:
Variable | Description | Example |
---|---|---|
DTZ_ACCESS_TOKEN |
JWT token for accessing DTZ services within your context | eyJhbGciOiJSUzI1NiI... |
DTZ_CONTEXT_ID |
Your DTZ context identifier | context-3cd84429-64a4-4226-b868-c83feeff0f46 |
PORT |
Port your service should listen on | 80 |
Authentication Methods for Incoming Requests
Your deployed service can authenticate incoming requests using several methods:
API Key Authentication
Users can authenticate with your service using DTZ API keys:
curl -H "X-API-KEY: your-api-key" https://yourservice.dtz.rocks/api/endpoint
Bearer Token Authentication
JWT tokens can be passed via Authorization header:
curl -H "Authorization: Bearer your-jwt-token" https://yourservice.dtz.rocks/api/endpoint
Basic Authentication
API keys can also be passed via basic auth:
curl -u apikey:your-api-key https://yourservice.dtz.rocks/api/endpoint
Cookie-based Authentication
For web applications, authentication can work through browser cookies set by DTZ Identity service.
OAuth Flow
DTZ provides automatic OAuth authentication for web applications. When unauthenticated users access your service, they are automatically redirected to the DTZ login page and redirected back after successful authentication.
Using DTZ Authentication in Your Service
Making Authenticated Requests to DTZ Services
Use the DTZ_ACCESS_TOKEN
environment variable to make authenticated calls to other DTZ services:
import os
import requests
# Get the DTZ access token from environment
token = os.environ.get('DTZ_ACCESS_TOKEN')
context_id = os.environ.get('DTZ_CONTEXT_ID')
# Make authenticated request to DTZ API
headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
response = requests.get(
'https://api.dtz.rocks/v1/containers/services',
headers=headers
)
// Node.js example
const token = process.env.DTZ_ACCESS_TOKEN;
const contextId = process.env.DTZ_CONTEXT_ID;
const response = await fetch('https://api.dtz.rocks/v1/containers/services', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
# Bash example
curl -H "Authorization: Bearer $DTZ_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
https://api.dtz.rocks/v1/containers/services
Verifying Incoming Authentication
DTZ automatically handles authentication verification for incoming requests. When a user makes an authenticated request to your service, DTZ:
- Validates the authentication credentials
- Converts API keys to JWT tokens
- Forwards the request with a
Authorization: Bearer <token>
header
Your service receives the JWT token and can extract user information:
import jwt
import os
from flask import Flask, request
app = Flask(__name__)
@app.route('/protected')
def protected_endpoint():
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Bearer '):
return {'error': 'No authentication provided'}, 401
token = auth_header.split(' ')[1]
try:
# The token is already verified by DTZ
# You can extract claims without verification
payload = jwt.decode(token, options={"verify_signature": False})
user_id = payload.get('sub') # Identity ID
context_id = payload.get('scope') # Context ID
roles = payload.get('roles', []) # User roles
return {
'user_id': user_id,
'context_id': context_id,
'roles': roles,
'message': 'Access granted'
}
except jwt.InvalidTokenError:
return {'error': 'Invalid token'}, 401
// Express.js example
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.get('/protected', (req, res) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No authentication provided' });
}
const token = authHeader.split(' ')[1];
try {
// Token is already verified by DTZ
const payload = jwt.decode(token);
const userId = payload.sub; // Identity ID
const contextId = payload.scope; // Context ID
const roles = payload.roles || []; // User roles
res.json({
user_id: userId,
context_id: contextId,
roles: roles,
message: 'Access granted'
});
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
});
JWT Token Structure
DTZ JWT tokens contain the following claims:
Claim | Description | Example |
---|---|---|
iss |
Issuer (always “dtz.rocks”) | "dtz.rocks" |
sub |
Subject (user identity ID) | "identity-abc123..." |
aud |
Audience (always “dtz.rocks”) | "dtz.rocks" |
scope |
Context ID | "context-3cd84429..." |
roles |
User roles/permissions | ["https://dtz.rocks/context/admin/{context_id}"] |
contexts |
Available contexts | ["context-3cd84429..."] |
exp |
Expiration time | 1640995200 |
iat |
Issued at time | 1640908800 |
Role-Based Access Control
DTZ uses role-based access control with URI-based role identifiers. Common role patterns:
https://dtz.rocks/context/admin/{context_id}
- Context administratorhttps://dtz.rocks/containers/admin/{context_id}
- Container service administratorhttps://dtz.rocks/objectstore/admin/{context_id}
- Object store administrator
You can check roles in your service:
def check_role(token, required_role_pattern):
payload = jwt.decode(token, options={"verify_signature": False})
roles = payload.get('roles', [])
context_id = payload.get('scope')
required_role = required_role_pattern.replace('{context_id}', context_id)
return required_role in roles
# Usage
if check_role(token, 'https://dtz.rocks/containers/admin/{context_id}'):
# User has container admin permissions
pass
Best Practices
Security
- Always validate JWT tokens contain expected claims
- Check user roles before granting access to sensitive operations
- Use HTTPS for all communications
- Don’t log sensitive authentication tokens
Error Handling
- Return appropriate HTTP status codes (401 for unauthorized, 403 for forbidden)
- Provide meaningful error messages without exposing sensitive information
Performance
- Cache JWT token validation results when possible
- Use connection pooling for DTZ API calls
- Consider implementing request rate limiting
Example: Complete Authenticated Service
Here’s a complete example of a Python Flask service with DTZ authentication:
import os
import jwt
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
DTZ_TOKEN = os.environ.get('DTZ_ACCESS_TOKEN')
DTZ_CONTEXT_ID = os.environ.get('DTZ_CONTEXT_ID')
def get_user_from_token(token):
"""Extract user information from DTZ JWT token."""
try:
payload = jwt.decode(token, options={"verify_signature": False})
return {
'user_id': payload.get('sub'),
'context_id': payload.get('scope'),
'roles': payload.get('roles', [])
}
except jwt.InvalidTokenError:
return None
def require_auth(f):
"""Decorator to require authentication."""
def decorated(*args, **kwargs):
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Bearer '):
return jsonify({'error': 'Authentication required'}), 401
token = auth_header.split(' ')[1]
user = get_user_from_token(token)
if not user:
return jsonify({'error': 'Invalid token'}), 401
request.user = user
return f(*args, **kwargs)
decorated.__name__ = f.__name__
return decorated
@app.route('/health')
def health():
"""Public health check endpoint."""
return jsonify({'status': 'healthy'})
@app.route('/profile')
@require_auth
def profile():
"""Protected endpoint returning user profile."""
return jsonify({
'user_id': request.user['user_id'],
'context_id': request.user['context_id'],
'roles': request.user['roles']
})
@app.route('/admin/users')
@require_auth
def admin_users():
"""Admin-only endpoint."""
required_role = f'https://dtz.rocks/context/admin/{DTZ_CONTEXT_ID}'
if required_role not in request.user['roles']:
return jsonify({'error': 'Admin access required'}), 403
# Make authenticated request to DTZ API
headers = {'Authorization': f'Bearer {DTZ_TOKEN}'}
response = requests.get(
'https://identity.dtz.rocks/api/2021-02-21/users',
headers=headers
)
return jsonify(response.json())
if __name__ == '__main__':
port = int(os.environ.get('PORT', 80))
app.run(host='0.0.0.0', port=port)
Troubleshooting
Common Issues
Authentication token not found: Ensure your service is deployed through DTZ containers service and the environment variables are being read correctly.
Invalid token errors: Check that you’re correctly extracting the token from the Authorization header and that the JWT parsing is working.
403 Forbidden errors: Verify the user has the required roles in their JWT token.
Service-to-service authentication failing: Ensure you’re using the DTZ_ACCESS_TOKEN
environment variable for outbound requests.
Testing Authentication
You can test your service authentication using curl:
# Test with API key
curl -H "X-API-KEY: your-api-key" https://yourservice.dtz.rocks/profile
# Test with bearer token
curl -H "Authorization: Bearer your-jwt-token" https://yourservice.dtz.rocks/profile
# Test unauthenticated (should return 401)
curl https://yourservice.dtz.rocks/profile
For more detailed information about DTZ authentication, see the Authentication documentation.