Back to Articles

Authomatic: The Framework-Agnostic OAuth Library That Refuses to Play Favorites

[ View on GitHub ]

Authomatic: The Framework-Agnostic OAuth Library That Refuses to Play Favorites

Hook

Most Python OAuth libraries marry you to a single framework. Authomatic took the road less traveled—and then made a controversial decision to drop OAuth 1.0 entirely in version 2.0, removing Twitter, Tumblr, and Bitbucket support overnight.

Context

In the early 2010s, Python web development was fragmenting. Django dominated enterprise, Flask appealed to minimalists, Pyramid served the Pylons refugees, and Google App Engine pushed webapp2. Each framework had its own OAuth implementation, and switching frameworks meant rewriting your entire authentication layer. If you built a reusable component or worked across multiple projects, you'd implement OAuth three different ways.

Authomatic emerged in 2013 to solve this portability problem. Instead of binding to a specific framework's request/response cycle, it used an adapter pattern to translate between its own interface and whatever framework you happened to be using. The promise was simple: write your OAuth logic once, run it anywhere. This mattered especially for library authors, agencies serving diverse clients, and teams evaluating frameworks without wanting to commit immediately. The library supported both OAuth 1.0a (Twitter, Tumblr) and OAuth 2.0 (Facebook, Google), covering the social login landscape comprehensively.

Technical Insight

Login Request

Request + Response Objects

Normalized Interface

Select Provider

Authorization URL

Redirect

Auth Flow

Callback + Code

Callback Request

Process Callback

Extract Code

Exchange Code

Token Exchange

Access Token

Fetch User Data

User Profile

Result Object

User Info

User Browser

Web Application

Authomatic Core

Framework Adapter

OAuth Provider

Social Login Service

System architecture — auto-generated

Authomatic's architecture centers on three key abstractions: providers, adapters, and the login process orchestrator. Providers encapsulate OAuth flow details for specific services—each provider class knows how to construct authorization URLs, exchange codes for tokens, and fetch user profiles. Adapters translate between Authomatic's framework-agnostic interface and the specific request/response objects of Django, Flask, Pyramid, or whatever you're using.

Here's how the adapter pattern works in practice. Instead of accessing request.GET (Django) or request.args (Flask) directly, Authomatic uses an adapter to normalize these differences:

from authomatic import Authomatic
from authomatic.adapters import WerkzeugAdapter
from flask import Flask, request, make_response

app = Flask(__name__)

CONFIG = {
    'google': {
        'class_': 'authomatic.providers.oauth2.Google',
        'consumer_key': 'YOUR_CLIENT_ID',
        'consumer_secret': 'YOUR_CLIENT_SECRET',
        'scope': ['profile', 'email'],
    },
    'github': {
        'class_': 'authomatic.providers.oauth2.GitHub',
        'consumer_key': 'YOUR_CLIENT_ID',
        'consumer_secret': 'YOUR_CLIENT_SECRET',
        'scope': ['user:email'],
    }
}

authomatic = Authomatic(CONFIG, 'your-random-secret-string')

@app.route('/login/<provider_name>')
def login(provider_name):
    response = make_response()
    result = authomatic.login(WerkzeugAdapter(request, response), provider_name)
    
    if result:
        if result.error:
            return f'Login failed: {result.error.message}'
        
        if result.user:
            result.user.update()  # Fetch user profile
            return f'Welcome {result.user.name}, {result.user.email}'
    
    return response

The same configuration and nearly identical code works with Django—just swap WerkzeugAdapter for DjangoAdapter and adjust the view signature. This portability comes from the adapter translating framework-specific request/response handling into Authomatic's internal representation.

The provider architecture is equally elegant. Each provider inherits from either OAuth1 or OAuth2 base classes that implement the protocol-level details. Individual providers like Google or GitHub only specify their authorization endpoints, token URLs, and user info parsing:

class Google(OAuth2):
    user_authorization_url = 'https://accounts.google.com/o/oauth2/auth'
    access_token_url = 'https://accounts.google.com/o/oauth2/token'
    user_info_url = 'https://www.googleapis.com/oauth2/v1/userinfo'
    
    @staticmethod
    def _x_user_parser(user, data):
        user.id = data.get('id')
        user.email = data.get('email')
        user.name = data.get('name')
        user.picture = data.get('picture')
        return user

This inheritance model means adding a new OAuth 2.0 provider requires minimal code—typically just endpoint URLs and a parser function. The base class handles the authorization code flow, PKCE extensions, token refresh, and error handling.

Version 2.0's most significant change is dropping OAuth 1.0 entirely. OAuth 1.0a was notoriously complex, requiring request signing with HMAC-SHA1, nonce generation, and timestamp management. By 2024, most major providers had migrated to OAuth 2.0. Authomatic 2.0 removed all OAuth 1.0 code and migrated Twitter, Bitbucket, and Tumblr to their OAuth 2.0 implementations. This simplified the codebase dramatically but broke anyone relying on providers that never made the OAuth 2.0 transition.

The library's testing strategy is worth noting. It uses pytest-httpx to mock HTTP exchanges in a ping-pong pattern—recording real OAuth flows and replaying them in tests. This catches provider API changes without requiring actual credentials in CI:

@pytest.mark.httpx_mock(non_mocked_hosts=[])
def test_google_login_flow(httpx_mock):
    httpx_mock.add_response(
        url='https://accounts.google.com/o/oauth2/token',
        json={'access_token': 'mock_token', 'token_type': 'Bearer'}
    )
    httpx_mock.add_response(
        url='https://www.googleapis.com/oauth2/v1/userinfo',
        json={'id': '123', 'email': 'test@example.com'}
    )
    # Test executes full OAuth flow with mocked responses

The framework-agnostic approach has trade-offs. You lose framework-specific conveniences like Django's session integration or Flask-Login compatibility. Authomatic handles the OAuth dance but stops there—session management, user database persistence, and permission checking remain your responsibility. It's authentication without the batteries-included account management that django-allauth provides.

Gotcha

Version 2.0's breaking changes are more severe than typical major version bumps. If you're upgrading from 1.x, you'll lose OAuth 1.0 support entirely—not just deprecated, but removed. Any providers that never implemented OAuth 2.0 simply disappear. Twitter, Tumblr, and Bitbucket work only if you update to their OAuth 2.0 implementations, which require new API credentials and different configuration. The documentation still references OAuth 1.0 features in places, creating confusion about what's actually supported.

The library is community-maintained by volunteers after the original author stepped back. This shows in the issue response times and pull request velocity. The 2.0 release happened in late 2024, but the changelog remains incomplete, and migration guides are sparse. You're somewhat on your own figuring out what changed and why your code broke. For production applications requiring SLA-level support, this maintenance model is a risk.

Provider coverage is another limitation. Authomatic supports 30+ providers out of the box, but popular services like Auth0, Okta, and Apple Sign In aren't included. Adding custom providers is straightforward if you understand OAuth, but you're implementing and maintaining that integration yourself. More actively developed alternatives like Authlib ship updates when providers change their APIs—with Authomatic, you might discover a broken integration only when users report login failures.

Verdict

Use if: You're building reusable components that need to work across Flask, Django, Pyramid, and other frameworks without framework-specific dependencies. You need simple OAuth 2.0 social logins with common providers (Google, GitHub, Facebook) and want minimal dependencies. You're comfortable maintaining provider integrations yourself if APIs change. You value code simplicity over comprehensive feature sets and don't need the account management extras that come with framework-specific solutions. Skip if: You require OAuth 1.0 support or need providers that haven't migrated to OAuth 2.0. You want actively maintained, commercially-backed support with rapid responses to provider API changes. You're using Django exclusively and would benefit from django-allauth's built-in user model integration, email verification, and account management. You need modern features like OIDC certification, JWK handling, or authorization server capabilities that Authlib provides. The framework-agnostic promise matters less than having a complete, well-documented, actively maintained authentication solution.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/automation/authomatic-authomatic.svg)](https://starlog.is/api/badge-click/automation/authomatic-authomatic)