2 Commits

Author SHA1 Message Date
ef733cb7bf SSO Pattern update
All checks were successful
CVE Scan & Docker Build / security-scan (push) Successful in 51s
CVE Scan & Docker Build / build-and-push (push) Successful in 46s
2026-05-13 06:31:00 -04:00
88afd5d307 docs(auth): add SSO signup template docs and update allauth imports 2026-05-13 06:30:59 -04:00

View File

@@ -1,4 +1,4 @@
# SSO with Allauth & Casdoor Pattern v1.0.0 # SSO with Allauth & Casdoor Pattern v1.02
Standardizes OIDC-based Single Sign-On using Django Allauth and Casdoor, covering adapter customization, user provisioning, group mapping, superuser protection, and configurable local-login fallback. Used by the `core` Django application. Standardizes OIDC-based Single Sign-On using Django Allauth and Casdoor, covering adapter customization, user provisioning, group mapping, superuser protection, and configurable local-login fallback. Used by the `core` Django application.
@@ -35,6 +35,7 @@ Every SSO implementation following this pattern must provide these files:
| Local account adapter | `<app>/adapters.py` | Disable local signup, authentication logging | | Local account adapter | `<app>/adapters.py` | Disable local signup, authentication logging |
| Management command | `<app>/management/commands/create_sso_groups.py` | Idempotent group + permission creation | | Management command | `<app>/management/commands/create_sso_groups.py` | Idempotent group + permission creation |
| Login template | `templates/account/login.html` | SSO button + conditional local login form | | Login template | `templates/account/login.html` | SSO button + conditional local login form |
| SSO signup template | `templates/socialaccount/signup.html` | Email confirmation step for first-time SSO users |
| Context processor | `<app>/context_processors.py` | Expose `CASDOOR_ENABLED` / `ALLOW_LOCAL_LOGIN` to templates | | Context processor | `<app>/context_processors.py` | Expose `CASDOOR_ENABLED` / `ALLOW_LOCAL_LOGIN` to templates |
| SSL patch (optional) | `<app>/ssl_patch.py` | Development-only SSL bypass | | SSL patch (optional) | `<app>/ssl_patch.py` | Development-only SSL bypass |
@@ -194,7 +195,7 @@ The social account adapter is the core of the pattern. It handles user provision
```python ```python
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from allauth.exceptions import ImmediateHttpResponse from allauth.core.exceptions import ImmediateHttpResponse
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.contrib import messages from django.contrib import messages
from django.shortcuts import redirect from django.shortcuts import redirect
@@ -440,6 +441,73 @@ The login template shows an SSO button when Casdoor is enabled and conditionally
--- ---
## SSO Signup Template
When a new SSO user has no existing account, allauth redirects them to `accounts/3rdparty/signup/` to confirm their email before the account is created. Without a custom template this page renders with no styling.
Create `templates/socialaccount/signup.html` extending the project base:
```html
{% extends "<app>/base.html" %}
{% block title %}Complete Sign Up — {{ themis_app_name }}{% endblock %}
{% block content %}
<div class="flex justify-center items-center min-h-[60vh]">
<div class="card bg-base-200 shadow-xl w-full max-w-md">
<div class="card-body">
<h2 class="card-title text-2xl justify-center mb-2">Complete Sign Up</h2>
<p class="text-center text-base-content/70 mb-4">
Confirm your email address to finish signing in with SSO.
</p>
{% if form.errors %}
<div class="alert alert-error mb-4">
<span>Please correct the errors below.</span>
</div>
{% endif %}
<form method="post" action="{{ action_url }}">
{% csrf_token %}
<div class="form-control mb-6">
<label class="label" for="id_email">
<span class="label-text">Email</span>
</label>
<input type="email" name="email" id="id_email"
class="input input-bordered w-full{% if form.email.errors %} input-error{% endif %}"
value="{{ form.email.value|default:'' }}"
autocomplete="email" required>
{% if form.email.errors %}
<label class="label">
<span class="label-text-alt text-error">{{ form.email.errors|join:", " }}</span>
</label>
{% endif %}
</div>
<div class="form-control mt-2">
<button type="submit" class="btn btn-primary w-full">Complete Sign Up</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
```
Key context variables allauth provides to this template:
| Variable | Description |
|----------|-------------|
| `form` | `SignupForm` with a single `email` field pre-populated from the OIDC claim |
| `action_url` | POST target (`/accounts/3rdparty/signup/`) — always use this, not a hard-coded path |
| `sociallogin` | The in-progress social login object (rarely needed in the template) |
> **Why this page exists:** `SOCIALACCOUNT_AUTO_SIGNUP = True` skips it when the IdP provides a valid email. It only appears when allauth cannot confirm the email (e.g. the IdP omitted it or there is a conflict with an existing account).
---
## Context Processor ## Context Processor
Exposes SSO settings to every template: Exposes SSO settings to every template:
@@ -701,7 +769,7 @@ class CasdoorAdapterTest(TestCase):
def test_superuser_sso_login_blocked(self): def test_superuser_sso_login_blocked(self):
"""pre_social_login must raise ImmediateHttpResponse for superusers.""" """pre_social_login must raise ImmediateHttpResponse for superusers."""
from allauth.exceptions import ImmediateHttpResponse from allauth.core.exceptions import ImmediateHttpResponse
user = User.objects.create_superuser( user = User.objects.create_superuser(
'admin@example.com', 'admin@example.com', 'pass' 'admin@example.com', 'admin@example.com', 'pass'
) )