ID Austria identification for Django via OpenID Connect
Project description
django-idaustria
A reusable Django app for ID Austria identification via OpenID Connect.
It deliberately separates identification (proving a user corresponds to a real Austrian citizen) from authentication (logging in). A typical use case is a signup flow where the site needs to be sure a new account belongs to a specific person; after identification, the user continues to log in with their regular credentials.
Features
- OIDC Authorization Code Flow against the ID Austria reference and production IdP
- JWT ID token validation via the IdP's JWKS
- Optional PKCE support
- Typed
IDAustriaIdentityview over the claims (bPK, names, birthdate, address, eIDAS level, …) with automatic decoding of the Base64-JSONmainAddress identification_completedsignal fired on every outcome (success and error)- Session-based "pending identification" decoupled from user auth — bind to a user later with
bind_pending_identification() - Structured
AuthorizationErrorfor IdP-reported failures (e.g.access_denied)
Installation
pip install django-idaustria
# settings.py
INSTALLED_APPS = [
# ...
"idaustria",
]
# Mandatory — from your IDA-SP registration
IDAUSTRIA_CLIENT_ID = "https://your.app.identifier" # a URL, not a random string
IDAUSTRIA_CLIENT_SECRET = "<client-secret>"
IDAUSTRIA_REDIRECT_URI = "https://your.site/idaustria/callback/" # must match IDA-SPR verbatim
# Optional
IDAUSTRIA_ENV = "ref" # or "prod" (default: "ref")
IDAUSTRIA_SCOPES = ("openid", "profile")
IDAUSTRIA_USE_PKCE = False
IDAUSTRIA_TIMEOUT = 10.0
Wire the URLs:
# urls.py
from django.urls import include, path
urlpatterns = [
# ...
path("idaustria/", include(("idaustria.urls", "idaustria"), namespace="idaustria")),
]
Migrate the database:
python manage.py migrate
The django-idaustria app only provides one Django model: Identification. It keeps a record of successful ID Austria identifications as a 1:n
relation to your application's user model (it uses settings.AUTH_USER_MODEL).
Usage
1. Trigger the flow
Render a link or button pointing to reverse("idaustria:start"). The user is redirected to the ID Austria IdP and, after completion, back to idaustria:callback. The callback endpoint:
- exchanges the authorization code for tokens (
client_secret_post) - validates the
id_tokenagainst the IdP's JWKS - stores the result in the session under a random
pending_id - fires the
identification_completedsignal - returns JSON:
{"ok": true, "claims": {...}, "pending_id": "..."}
On failure the callback returns a structured error, e.g. {"ok": false, "error": "access_denied", "error_description": "..."}.
2. Consume the identification
from django.dispatch import receiver
from idaustria import IDAustriaIdentity, bind_pending_identification
from idaustria.signals import identification_completed
@receiver(identification_completed)
def on_idaustria_completed(sender, request, success, identity, tokens, error, **kwargs):
if not success:
# error is an IDAustriaError subclass (AuthorizationError, StateMismatchError, …)
return
# identity is an IDAustriaIdentity — typed view over the claims
bpk = identity.bpk # stable person identifier
name = identity.full_name # "Alice Example"
birthdate = identity.birthdate # datetime.date | None
address = identity.main_address # MainAddress | None (auto-decoded)
is_adult = identity.age_over(18) # bool | None
raw = identity.claims # raw dict, if you need a claim not surfaced
# You probably want to persist the identification. Do it later, when
# you know which user to bind it to (e.g. after signup completes):
# later, in your signup finalization view:
def finalize_signup(request):
user = ... # the user you just created or logged in
identification = bind_pending_identification(request, user)
# identification is now an Identification row, or None if nothing was pending
3. Use bpk, not sub
At ID Austria the OIDC sub claim is transient — it changes on every login. The only stable identifier for a person is the bPK (bereichsspezifisches Personenkennzeichen), exposed as identity.bpk (underlying claim urn:pvpgvat:oidc.bpk). Use bpk for recognizing returning users.
Notes
- ID Austria supports only the Authorization Code Flow and requires a
client_secret. client_idmust be a URL inside your application (this is an IDA-SPR convention, not an OAuth requirement).- Attributes (name, address, age-over, …) are not selected via OIDC scopes — they are configured per service provider in IDA-SPR. Request only
openid profilefor compatibility. - The callback endpoint does not require authentication — it deliberately runs anonymously so sign-up flows work.
- Up to 5 pending identifications are retained per session; older entries are pruned.
Development
See CLAUDE.md for architecture notes and the demo/ project for a runnable example.
cd demo
uv run python manage.py migrate
uv run python manage.py runserver
Tests:
uv run python -m pytest
License
MIT — see LICENSE.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_idaustria-0.1.0.tar.gz.
File metadata
- Download URL: django_idaustria-0.1.0.tar.gz
- Upload date:
- Size: 20.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"KDE neon","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6254a6f060f919a407585e4eed929df90d07296b1eba701ee41a8fde5d0e90ad
|
|
| MD5 |
a377f09fb7b7d09fbc01f305046fe2ce
|
|
| BLAKE2b-256 |
10e558c5e6bf2aace7a9ef8d139c995e786856c7fb3dc71691871ad9184bcc63
|
File details
Details for the file django_idaustria-0.1.0-py3-none-any.whl.
File metadata
- Download URL: django_idaustria-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"KDE neon","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7bb2782ecc47afefb4394ecfc9f5e96b6f198522a85c09fcb24bf97430f3bf44
|
|
| MD5 |
76489afc533677ec7b2eede5fadb8393
|
|
| BLAKE2b-256 |
af9512df266afe39aa6d4ca6abf504f9247a8c3f7d3b1ac3c1145f6e78f3573f
|