IndieAuth Implementation¶
Django-IndieWeb provides a complete IndieAuth implementation that supports both authentication (logging into sites) and authorization (granting permissions to apps).
Overview¶
IndieAuth is a federated login protocol that enables users to sign in to websites using their own domain name. Django-IndieWeb implements all three IndieAuth endpoints:
Authorization Endpoint (
/indieweb/auth/) - Handles user consent and generates auth codesToken Endpoint (
/indieweb/token/) - Exchanges auth codes for access tokensAuthentication - Verifies auth codes for login-only flows
Customizing the Consent Screen¶
The consent screen template can be customized by overriding indieweb/consent.html in your project:
Create the directory structure in your templates folder:
templates/ └── indieweb/ └── consent.htmlCopy the default template as a starting point:
{% extends "base.html" %} {% block content %} <div class="authorization-request"> <h1>Authorization Request</h1> <p><strong>{{ client_id }}</strong> is requesting access to your site.</p> {% if scope_list %} <p>Requested permissions:</p> <ul> {% for permission in scope_list %} <li>{{ permission }}</li> {% endfor %} </ul> {% endif %} <form method="post"> {% csrf_token %} <input type="hidden" name="client_id" value="{{ client_id }}"> <input type="hidden" name="redirect_uri" value="{{ redirect_uri }}"> <input type="hidden" name="state" value="{{ state }}"> <input type="hidden" name="me" value="{{ me }}"> {% if scope %} <input type="hidden" name="scope" value="{{ scope }}"> {% endif %} <button type="submit" name="action" value="approve">Approve</button> <button type="submit" name="action" value="deny">Deny</button> </form> </div> {% endblock %}
Available template context variables:
client_id- The application requesting accessredirect_uri- Where to redirect after authorizationstate- State parameter for CSRF protectionme- The user’s identity URLscope- Space-separated list of requested scopesscope_list- Python list of individual scopes
Security Considerations¶
HTTPS Required: Always use HTTPS in production for all IndieAuth endpoints
Auth Code Timeout: Auth codes expire after 60 seconds by default
CSRF Protection: The consent form includes Django’s CSRF token
User Authentication: Users must be logged in to approve/deny requests
Configuration¶
Configure IndieAuth behavior in your Django settings:
# Auth code expiration time in seconds (default: 60)
INDIEAUTH_CODE_TIMEOUT = 60
# Login URL for redirecting unauthenticated users
LOGIN_URL = "/accounts/login/"
Testing IndieAuth¶
Test your IndieAuth implementation using:
Web-based testers:
Micropub clients (for authorization):
Quill (https://quill.p3k.io/)
Indigenous for iOS/Android
Micropublish (https://micropublish.net/)
Unit tests:
def test_consent_screen_displays(client, user): client.login(username=user.username, password="password") response = client.get("/indieweb/auth/", { "me": "https://example.com", "client_id": "https://app.example.com", "redirect_uri": "https://app.example.com/callback", "state": "12345", "scope": "create" }) assert response.status_code == 200 assert "Authorization Request" in response.content.decode()
Example: Using IndieAuth with a Micropub Client¶
Configure your site’s homepage to advertise the endpoints:
<link rel="authorization_endpoint" href="https://mysite.com/indieweb/auth/"> <link rel="token_endpoint" href="https://mysite.com/indieweb/token/"> <link rel="micropub" href="https://mysite.com/indieweb/micropub/">
When a Micropub client tries to authenticate:
It discovers your endpoints from your homepage
Redirects you to the authorization endpoint
You see the consent screen and approve/deny
The client receives an auth code
The client exchanges the code for an access token
The client can now create posts using the token
Troubleshooting¶
- “Missing parameter” errors
Ensure all required parameters are provided:
me,client_id,redirect_uri,state- Consent screen not showing
Check that you’re logged in and all parameters are valid
- Auth code expired
Auth codes are only valid for 60 seconds. The client must exchange them quickly.
- No scopes shown on consent screen
This is normal for authentication-only flows. Scopes are only shown when the client requests permissions.