Service Authentication

last updated: 2025-09-10

Authenticating Deployed Services

DownToZero (DTZ) provides built-in authentication 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 A JWT token for accessing DTZ services within your context. eyJhbGciOiJSUzI1NiI...
DTZ_CONTEXT_ID Your DTZ context identifier. context-3cd84429-64a4-4226-b868-c83feeff0f46
PORT The port your service should listen on. 80

Authenticating 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 by providing the key in the X-API-KEY header.

curl -H "X-API-KEY: your-api-key" https://yourservice.dtz.rocks/api/endpoint

Bearer Token Authentication

Provide a JWT token in the Authorization header to authenticate.

curl -H "Authorization: Bearer your-jwt-token" https://yourservice.dtz.rocks/api/endpoint

Basic Authentication

You can also pass API keys using basic authentication.

curl -u apikey:your-api-key https://yourservice.dtz.rocks/api/endpoint

For web applications, the DTZ Identity service can handle authentication through browser cookies.

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 then redirected back after a successful login.

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 the environment
token = os.environ.get('DTZ_ACCESS_TOKEN')
context_id = os.environ.get('DTZ_CONTEXT_ID')

# Make an authenticated request to the 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 for incoming requests. When a user makes an authenticated request to your service, DTZ:

  1. Validates the authentication credentials.
  2. Converts API keys to JWT tokens.
  3. Forwards the request with an Authorization: Bearer <token> header.

Your service receives the JWT token and can extract user information from it.

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, so you can 
        # extract claims without signature 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 {
        // The 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 include:

  • https://dtz.rocks/context/admin/{context_id} - Context administrator
  • https://dtz.rocks/containers/admin/{context_id} - Container service administrator
  • https://dtz.rocks/objectstore/admin/{context_id} - Object store administrator

You can check for 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

# Example:
if check_role(token, 'https://dtz.rocks/containers/admin/{context_id}'):
    # User has container admin permissions
    pass

Best Practices

Security

  • Always validate that JWT tokens contain the expected claims.
  • Check user roles before granting access to sensitive operations.
  • Use HTTPS for all communications.
  • Do not log sensitive authentication tokens.

Error Handling

  • Return appropriate HTTP status codes (e.g., 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 is 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):
    """Extracts user information from a 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):
    """A 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():
    """A public health check endpoint."""
    return jsonify({'status': 'healthy'})

@app.route('/profile')
@require_auth
def profile():
    """A protected endpoint that returns the user's 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():
    """An 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 an authenticated request to the 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 the DTZ containers service and that the environment variables are being read correctly.
  • Invalid token errors: Check that you are correctly extracting the token from the Authorization header and that the JWT parsing is working.
  • 403 Forbidden errors: Verify that the user has the required roles in their JWT token.
  • Service-to-service authentication failing: Ensure you are using the DTZ_ACCESS_TOKEN environment variable for outbound requests.

Testing Authentication

You can test your service’s authentication using curl:

# Test with an API key
curl -H "X-API-KEY: your-api-key" https://yourservice.dtz.rocks/profile

# Test with a 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.