Skip to main content

OAuth2 providers pack (Google, GitHub, Microsoft, Apple, Discord, Facebook, GitLab, LinkedIn, Twitch) for the Asok framework.

Project description

Asok Logo + Auth Logo

asok-auth-providers

PyPI version

OAuth2 Providers pack for the Asok Framework.

Ships ready-to-use Google, GitHub, Microsoft, Apple, Discord, Facebook, GitLab, LinkedIn, and Twitch sign-in flows, templates, and style sheets.

Resources

Installation

pip install asok-auth-providers

Quick start

# wsgi.py
from asok import Asok
from asok_auth_providers import AsokAuthProvidersExtension

app = Asok()

app.config["OAUTH_PROVIDERS"] = {
    "google": {
        "client_id": "...", 
        "client_secret": "...", 
        "label": "Google Workspace"
    },
    ...
}

def on_oauth_login(request, user_info):
    """Find or create a local user, then call request.login(user)."""
    from src.models.user import User
    user = User.find(email=user_info["email"]) or User.query().create(
        email=user_info["email"],
        name=user_info["name"],
    )
    request.login(user)

app.register_extension(AsokAuthProvidersExtension(
    app,
    on_login=on_oauth_login,
    login_redirect="/dashboard",
    login_failed_redirect="/login",
    compact=True,
    label_format="Connecter avec {}",
))

Extension Anatomy

When you register AsokAuthProvidersExtension with your Asok app, it registers and manages the following components:

1. Routes & Page Handlers

The extension dynamically registers the following directory-based routes using the dynamic parameter [provider] folder:

  • GET /auth/<provider>/login: Redirects the user to the provider's authorization consent screen.
  • GET /auth/<provider>/callback: Receives the callback, validates security state keys, performs backend token exchanges, retrieves user profiles, executes your custom on_login callback, and redirects the user.

2. Template Helpers & Globals

The extension exposes the following context helper variables and callable functions to all templates:

  • oauth_providers: The dictionary of brand names and default capitalized labels.
  • oauth_icon(provider): Outputs the inline SVG logo element for the selected provider.
  • oauth_login_url(provider, next_url): Generates the routing login URLs (e.g., /auth/google/login?next=/dashboard).
  • oauth_providers_css(): Emits the standard <link> stylesheet element for the buttons.
  • oauth_compact & oauth_label_format: The default layout preferences configured on your Python extension registry.

3. Static Assets

  • /css/oauth-buttons.css: The stylesheet specifying sizes, hover scaling transitions, active states, flex grids, and brand background colors.

Sign-in buttons

The template renders the button elements directly without a wrapper div. This allows complete freedom to wrap and style the layout however you want (for example, wrapping them in a flex container using Tailwind CSS or custom styles):

<div class="flex flex-wrap gap-2">
    {% include "auth_providers/buttons.html" %}
</div>

To customize the provider list, set providers before the include:

{% set providers = ["google", "github"] %}
{% include "auth_providers/buttons.html" %}

To customize the button labels, set a labels dictionary before the include (which maps provider names to custom strings), or provide a label_format template string (where {} is replaced by the provider's label):

{% set labels = {"google": "Google Workspace"} %}
{% set label_format = "Connecter avec {}" %}
{% include "auth_providers/buttons.html" %}

To render compact buttons (only showing the brand icons without text):

{% set compact = true %}
{% include "auth_providers/buttons.html" %}

To pass custom CSS classes directly to the elements (perfect for Tailwind CSS layouts):

{# Completely override the default button classes #}
{% set button_class = "flex items-center rounded-lg px-4 py-2 text-white font-medium" %}

{# Or append extra classes to the default layout styles #}
{% set extra_class = "shadow hover:shadow-md transition-shadow" %}

{# Append classes to the inner label text span (e.g. for responsive hiding) #}
{% set label_class = "hidden md:inline-block ml-2" %}

{% include "auth_providers/buttons.html" %}

To preserve a post-login redirect:

{% set next_url = "/dashboard" %}
{% include "auth_providers/buttons.html" %}

Include the default CSS:

<link rel="stylesheet" href="/css/oauth-buttons.min.css">

Configuration

Set app.config["OAUTH_PROVIDERS"] to a dict mapping provider names to {client_id, client_secret} credentials.

To avoid hardcoding sensitive credentials in your codebase, it is highly recommended to load them from environment variables (defined in your .env file).

Example .env file

GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret

Loading in wsgi.py

import os

app.config["OAUTH_PROVIDERS"] = {
    "google": {
        "client_id": os.environ.get("GOOGLE_CLIENT_ID"),
        "client_secret": os.environ.get("GOOGLE_CLIENT_SECRET"),
        "label": "Google Workspace"
    },
    "github": {
        "client_id": os.environ.get("GITHUB_CLIENT_ID"),
        "client_secret": os.environ.get("GITHUB_CLIENT_SECRET"),
    },
}

Only providers explicitly present in the dict are enabled — the extension will never auto-enable a provider without credentials.

[!IMPORTANT] Understanding client_id and client_secret

These are not your personal user credentials (like your email address, username, login password, or SMTP app-specific passwords).

Instead, they are the OAuth2 Application Credentials generated by registering a web application on each provider's developer portal:

During registration on those developer portals, you must configure the Redirect URI / Callback URL to point to your application callback URL: https://<your-domain>/auth/<provider>/callback (or http://127.0.0.1:8000/auth/github/callback during local development).

Customizing Labels and Layout Defaults

You can configure default layout parameters directly when initializing AsokAuthProvidersExtension in your Python code:

  • compact (bool): If True, renders only the brand logo icons without text by default. (Default: False)
  • label_format (str): A format pattern string (e.g., "Connecter avec {}") to format button text by default. (Default: "")

For example:

app.register_extension(AsokAuthProvidersExtension(
    app,
    on_login=on_oauth_login,
    compact=True,
    label_format="Se connecter avec {}"
))

Additionally, you can specify custom labels for individual providers directly in app.config["OAUTH_PROVIDERS"] using the label key:

app.config["OAUTH_PROVIDERS"] = {
    "google": {
        "client_id": "...",
        "client_secret": "...",
        "label": "Google Workspace"  # Will render as: "Se connecter avec Google Workspace"
    }
}

You can also set APP_URL to lock the callback host (recommended in production to prevent host-header tampering):

app.config["APP_URL"] = "https://app.example.com"

Otherwise the callback URL is reconstructed from the current request's scheme and Host header.

Supported providers

Provider Notes
google Already in asok.auth.OAuth core, re-exposed here.
github Already in asok.auth.OAuth core, re-exposed here.
microsoft Common (multi-tenant) authority endpoint.
apple id_token JWT decode (no userinfo endpoint).
discord Requires identify email scopes.
facebook Requires email public_profile scopes.
gitlab Hosted gitlab.com only; self-hosted needs a custom config.
linkedin OpenID Connect userinfo endpoint.
twitch Requires user:read:email scope.

Security notes

  • The OAuth state parameter is generated with secrets.token_urlsafe(32) and stored in the session. It is popped during the callback, so a callback URL cannot be replayed.
  • Apple's id_token is JWT-decoded for claim extraction; signature verification against Apple's JWKS should be layered on top in production.
  • APP_URL is honored when present, so the callback URL is not subject to Host header tampering.

Hooks

AsokAuthProvidersExtension(on_login=...) takes a callback (request, user_info) -> None that is executed when the OAuth provider returns user profile information successfully.

The user_info Structure

The user_info argument is a normalized dictionary:

{
    "provider":    "google",
    "provider_id": "1234567890",
    "email":       "user@example.com",
    "name":        "User Name",
    "picture":     "https://...",          # URL to their profile avatar
    "raw":         {...},                  # The raw, unfiltered provider response
}

Implementing on_login

In your login hook, you can query your database to find or create the user, update their profile metadata (such as their name or picture URL), and log them into the Asok session:

def on_oauth_login(request, user_info):
    from src.models.user import User

    # Find an existing user by email
    user = User.find(email=user_info["email"])

    if not user:
        # Create a new user account if they don't exist
        user = User.create(
            email=user_info["email"],
            name=user_info["name"],
            picture=user_info["picture"],
            oauth_provider=user_info["provider"],
            oauth_id=user_info["provider_id"]
        )
    else:
        # Update existing user profile assets (e.g. name or avatar URL)
        user.update(
            name=user_info["name"] or user.name,
            picture=user_info["picture"] or user.picture,
            oauth_provider=user_info["provider"],
            oauth_id=user_info["provider_id"]
        )

    # Log the user into the Asok session
    request.login(user)

Once request.login(user) is called, the logged-in user becomes globally accessible in all your templates and controllers via the request.user property:

<!-- Inside any page template -->
<div class="profile-card">
    {% if request.user.picture %}
        <img src="{{ request.user.picture }}" alt="{{ request.user.name }}">
    {% endif %}
    <h2>Welcome back, {{ request.user.name }}!</h2>
    <p>Logged in via {{ request.user.oauth_provider | capitalize }}</p>
</div>

If no on_login callback is provided, the normalized user_info dict is stashed in request.session["oauth_user_info"], and the user is redirected to login_redirect.

License

This project is licensed under the MIT License. See the LICENSE file for details.

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

asok_auth_providers-1.0.0.tar.gz (25.4 kB view details)

Uploaded Source

Built Distribution

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

asok_auth_providers-1.0.0-py3-none-any.whl (22.6 kB view details)

Uploaded Python 3

File details

Details for the file asok_auth_providers-1.0.0.tar.gz.

File metadata

  • Download URL: asok_auth_providers-1.0.0.tar.gz
  • Upload date:
  • Size: 25.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for asok_auth_providers-1.0.0.tar.gz
Algorithm Hash digest
SHA256 67a077cb19e1bfec9f69d459093fe6246244d8f113df6908a662bcc7a998141c
MD5 0c75d069e3d9e553e89b9b4c4a992cb0
BLAKE2b-256 698396d740beb8e351fb8b532813dc0bc453be6b6d1b6bf683fa82d942e3cf09

See more details on using hashes here.

Provenance

The following attestation bundles were made for asok_auth_providers-1.0.0.tar.gz:

Publisher: release.yml on codewithmpia/asok-auth-providers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file asok_auth_providers-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for asok_auth_providers-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 328f9e216b2cecc4c63862ff639b9fe3a49643b3705e01a559ffcd8d0b1fbb48
MD5 193461ae2bc9a42c05fb01f68fd00f73
BLAKE2b-256 4f43e7a1c052eeb1ff5d5f9f44471d297b8067e8971c44501dd8170261b70b2e

See more details on using hashes here.

Provenance

The following attestation bundles were made for asok_auth_providers-1.0.0-py3-none-any.whl:

Publisher: release.yml on codewithmpia/asok-auth-providers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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