Skip to main content

Flask extension for Universal Verification Broker (UVB)

Project description

uvb-flask

Flask extension for Universal Verification Broker (UVB) authentication.

Installation

pip install uvb-flask

Quick Start

from flask import Flask, jsonify
from uvb_flask import UVBFlask, get_session, require_factors, uvb_required

app = Flask(__name__)

# Initialize UVB extension
uvb = UVBFlask(
    app,
    tenant_id='my-tenant',
    uvb_url='http://localhost:8080',
    exclude_paths=['/login', '/health', '/public']
)

# Access authenticated user
@app.route('/profile')
def profile():
    session = get_session()
    return jsonify({
        'user_id': session.user_id,
        'factors': session.factors_verified
    })

# Require authentication with decorator
@app.route('/protected')
@uvb_required
def protected():
    user_id = g.uvb_session.user_id
    return jsonify({'message': f'Hello {user_id}'})

# Require specific MFA factors
@app.route('/admin/delete', methods=['POST'])
@require_factors('totp', 'webauthn')
def admin_delete():
    return jsonify({'message': 'Admin action completed'})

if __name__ == '__main__':
    app.run(port=3000)

API Reference

UVBFlask Extension

Direct initialization:

from flask import Flask
from uvb_flask import UVBFlask

app = Flask(__name__)
uvb = UVBFlask(
    app,
    tenant_id='my-tenant',
    uvb_url='http://localhost:8080',  # Optional, defaults to http://localhost:8080
    api_key='optional-api-key',  # Optional
    cookie_name='uvb_session',  # Optional, defaults to uvb_session
    exclude_paths=['/login', '/health']  # Optional, defaults to []
)

Factory pattern with init_app:

from flask import Flask
from uvb_flask import UVBFlask

uvb = UVBFlask()

def create_app():
    app = Flask(__name__)
    uvb.init_app(
        app,
        tenant_id='my-tenant',
        uvb_url='http://localhost:8080'
    )
    return app

Configuration from app.config:

app = Flask(__name__)
app.config['UVB_TENANT_ID'] = 'my-tenant'
app.config['UVB_URL'] = 'http://localhost:8080'
app.config['UVB_API_KEY'] = 'optional-api-key'
app.config['UVB_COOKIE_NAME'] = 'uvb_session'
app.config['UVB_EXCLUDE_PATHS'] = ['/login', '/health']

uvb = UVBFlask(app)

Session Access:

After successful authentication, g.uvb_session contains a UVBSession object:

from flask import g

@app.route('/user')
def user():
    session = g.uvb_session
    return {
        'user_id': session.user_id,
        'tenant_id': session.tenant_id,
        'session_id': session.session_id,
        'factors_verified': session.factors_verified,
        'expires_at': session.expires_at.isoformat()
    }

Decorators

@uvb_required

Require UVB authentication for a route.

from uvb_flask import uvb_required

@app.route('/profile')
@uvb_required
def profile():
    user_id = g.uvb_session.user_id
    return {'user_id': user_id}

@require_factors(*factors)

Require specific MFA factors for a route.

from uvb_flask import require_factors

@app.route('/sensitive', methods=['POST'])
@require_factors('totp', 'webauthn')
def sensitive():
    return {'message': 'Sensitive operation completed'}

Helper Functions

get_session()

Get the UVB session from Flask's g object.

from uvb_flask import get_session

@app.route('/info')
def info():
    session = get_session()
    if session:
        return {'user_id': session.user_id}
    return {'error': 'Not authenticated'}, 401

Examples

Protecting Specific Routes

from flask import Flask
from uvb_flask import UVBFlask, uvb_required

app = Flask(__name__)
uvb = UVBFlask(app, tenant_id='my-tenant')

# Public routes (no authentication required)
@app.route('/health')
def health():
    return {'status': 'ok'}

# Protected routes (authentication required)
@app.route('/api/data')
@uvb_required
def data():
    return {'data': 'protected'}

Using Blueprints

from flask import Blueprint, g
from uvb_flask import uvb_required, require_factors

# API Blueprint
api = Blueprint('api', __name__, url_prefix='/api')

@api.route('/profile')
@uvb_required
def profile():
    return {'user_id': g.uvb_session.user_id}

# Admin Blueprint with strict MFA
admin = Blueprint('admin', __name__, url_prefix='/admin')

@admin.before_request
@require_factors('totp', 'webauthn')
def require_admin_auth():
    pass

@admin.route('/users')
def admin_users():
    return {'users': [...]}

# Register blueprints
app.register_blueprint(api)
app.register_blueprint(admin)

Conditional MFA Requirements

from flask import request
from uvb_flask import get_session

@app.route('/transfer', methods=['POST'])
def transfer():
    session = get_session()
    if not session:
        return {'error': 'Not authenticated'}, 401

    amount = request.json.get('amount', 0)

    # Require additional auth for large transfers
    if amount > 10000:
        if not all(f in session.factors_verified for f in ['totp', 'webauthn']):
            return {
                'error': 'Additional authentication required',
                'required': ['totp', 'webauthn'],
                'verified': session.factors_verified
            }, 403

    return {'message': 'Transfer initiated'}

Custom Error Handling

@app.errorhandler(401)
def unauthorized(error):
    return {
        'error': 'Unauthorized',
        'message': 'Please authenticate to access this resource'
    }, 401

@app.errorhandler(403)
def forbidden(error):
    return {
        'error': 'Forbidden',
        'message': 'Insufficient permissions'
    }, 403

Flask-RESTful Integration

from flask import Flask
from flask_restful import Api, Resource
from uvb_flask import UVBFlask, get_session, require_factors

app = Flask(__name__)
api = Api(app)
uvb = UVBFlask(app, tenant_id='my-tenant')

class UserProfile(Resource):
    def get(self):
        session = get_session()
        if not session:
            return {'error': 'Not authenticated'}, 401
        return {'user_id': session.user_id}

class AdminResource(Resource):
    method_decorators = [require_factors('totp', 'webauthn')]

    def delete(self, user_id):
        return {'message': f'User {user_id} deleted'}

api.add_resource(UserProfile, '/profile')
api.add_resource(AdminResource, '/admin/user/<string:user_id>')

Application Factory Pattern

from flask import Flask
from uvb_flask import UVBFlask

uvb = UVBFlask()

def create_app(config=None):
    app = Flask(__name__)

    if config:
        app.config.from_object(config)

    # Initialize extensions
    uvb.init_app(
        app,
        tenant_id=app.config.get('UVB_TENANT_ID'),
        uvb_url=app.config.get('UVB_URL')
    )

    # Register blueprints
    from .api import api_bp
    app.register_blueprint(api_bp)

    return app

# Run the app
app = create_app()
if __name__ == '__main__':
    app.run()

Testing

import pytest
from flask import Flask
from uvb_flask import UVBFlask

@pytest.fixture
def app():
    app = Flask(__name__)
    app.config['TESTING'] = True
    app.config['UVB_TENANT_ID'] = 'test-tenant'

    uvb = UVBFlask(app)

    @app.route('/test')
    def test_route():
        return {'message': 'test'}

    return app

@pytest.fixture
def client(app):
    return app.test_client()

def test_authentication_required(client):
    response = client.get('/test')
    assert response.status_code == 401

def test_with_valid_token(client):
    response = client.get(
        '/test',
        headers={'Authorization': 'Bearer valid-token'}
    )
    assert response.status_code == 200

Development

# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Type checking
mypy uvb_flask

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

uvb_flask-0.2.1.tar.gz (10.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

uvb_flask-0.2.1-py3-none-any.whl (11.1 kB view details)

Uploaded Python 3

File details

Details for the file uvb_flask-0.2.1.tar.gz.

File metadata

  • Download URL: uvb_flask-0.2.1.tar.gz
  • Upload date:
  • Size: 10.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for uvb_flask-0.2.1.tar.gz
Algorithm Hash digest
SHA256 f3dcee1f07a8c53d5a0ac0d4119347b0cd96963e55cb6020f15b2b1b1089272c
MD5 2e4ef6c1e54232706e22a94543304f6b
BLAKE2b-256 328b2214fdba2e81f202fb7f4953f1200ddc42d031dcc64716db716e3b5a498e

See more details on using hashes here.

File details

Details for the file uvb_flask-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: uvb_flask-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 11.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for uvb_flask-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 73fef38c4b7f86008c8ac9a9549ba9f270a07ed8835a394de957e28ef5f10b38
MD5 67cab3e88f5e38b70cb08500bd05fca5
BLAKE2b-256 7d074d080526e116e7f8a75cc13c68ef1b97fbfadae8aa47fc71664121b06012

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page