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.0.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.0-py3-none-any.whl (11.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: uvb_flask-0.2.0.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.0.tar.gz
Algorithm Hash digest
SHA256 3e56f1aeb9496726bd46fa37e282c63cf5becafa5b9502836ce31d5bca8868f6
MD5 1c0ffeaf18462e2c305ff28151fd9746
BLAKE2b-256 b9f306f2f44408131c2b262ecac3108f2c93b76116feca41debae249fa768776

See more details on using hashes here.

File details

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

File metadata

  • Download URL: uvb_flask-0.2.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c29002e55c941a02ad2b0ad8c4691e967b9212033e3768282fe36cc3a11407f1
MD5 94d7bfeca1a0b8314885a916ecab9426
BLAKE2b-256 b2e0ab546a040bc0afe8ae2fa62111fd33dcceed17f93a86ccab2ded30ec3807

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