asok-auth-providers
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
- PyPI Project: pypi.org/project/asok-auth-providers
- Asok Framework: asok-framework.com
- Asok Repository: github.com/asok-framework/asok
- Extension Repository: github.com/codewithmpia/asok-auth-providers
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 customon_logincallback, 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_idandclient_secretThese 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:
- Google: Register on the Google Cloud Console (APIs & Services > Credentials)
- GitHub: Register a new OAuth App on GitHub Developer Settings
- Microsoft: Register under App Registrations in the Microsoft Entra ID Portal
- Apple: Configure a Service ID on the Apple Developer Account (Certificates, Identifiers & Profiles)
- Discord: Create an application on the Discord Developer Portal
- Facebook: Register an App on the Meta for Developers portal
- GitLab: Add an application in GitLab User Applications Settings
- LinkedIn: Register on the LinkedIn Developer Portal
- Twitch: Register your application in the Twitch Developer Console
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(orhttp://127.0.0.1:8000/auth/github/callbackduring local development).
Customizing Labels and Layout Defaults
You can configure default layout parameters directly when initializing AsokAuthProvidersExtension in your Python code:
compact(bool): IfTrue, 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 |
|---|---|
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. |
Requires email public_profile scopes. |
|
| gitlab | Hosted gitlab.com only; self-hosted needs a custom config. |
| OpenID Connect userinfo endpoint. | |
| twitch | Requires user:read:email scope. |
Security notes
- The OAuth
stateparameter is generated withsecrets.token_urlsafe(32)and stored in the session. It is popped during the callback, so a callback URL cannot be replayed. - Apple's
id_tokenis JWT-decoded for claim extraction; signature verification against Apple's JWKS should be layered on top in production. APP_URLis honored when present, so the callback URL is not subject toHostheader 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.
Metadata
Release files for asok-auth-providers 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| asok_auth_providers-1.0.0.tar.gz | 25.4 kB | With provenance | Details for asok_auth_providers-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| asok_auth_providers-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for asok_auth_providers-1.0.0-py3-none-any.whl |
Total release size: 47.9 kB
Release files / asok_auth_providers-1.0.0.tar.gz
| Download URL | asok_auth_providers-1.0.0.tar.gz |
|---|---|
| Size | 25.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
67a077cb19e1bfec9f69d459093fe6246244d8f113df6908a662bcc7a998141c
|
|
BLAKE2b-256 checksum How to use checksums |
698396d740beb8e351fb8b532813dc0bc453be6b6d1b6bf683fa82d942e3cf09
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 22, 2026.
Transparency logRelease files / asok_auth_providers-1.0.0-py3-none-any.whl
| Download URL | asok_auth_providers-1.0.0-py3-none-any.whl |
|---|---|
| Size | 22.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
328f9e216b2cecc4c63862ff639b9fe3a49643b3705e01a559ffcd8d0b1fbb48
|
|
BLAKE2b-256 checksum How to use checksums |
4f43e7a1c052eeb1ff5d5f9f44471d297b8067e8971c44501dd8170261b70b2e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 22, 2026.
Transparency logSecurity for asok-auth-providers 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for asok_auth_providers-1.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 22, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.