Metadata-Version: 2.4
Name: django-sso-oauth
Version: 2.0.0
Summary: Django OAuth authentication middleware for the admin interface and the frontend
Author-email: Hiep Ho Minh <hiephm@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Hiep Ho Minh
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/hiephm/django-sso-oauth
Project-URL: Issues, https://github.com/hiephm/django-sso-oauth/issues
Keywords: django,oauth,sso,authentication,admin
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 3.2
Classifier: Framework :: Django :: 4.0
Classifier: Framework :: Django :: 4.1
Classifier: Framework :: Django :: 4.2
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.6
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django>=3.2
Requires-Dist: requests>=2.20
Requires-Dist: PyJWT>=2.0
Dynamic: license-file

# django-sso-oauth

Django OAuth authentication for the admin interface **and** the frontend.

Replaces the default Django admin login with an OAuth 2.0 / OpenID Connect flow, and provides the
same flow for regular (non-staff) site users. After a successful OAuth exchange, the user's session
is maintained by a lightweight middleware that maps the OAuth identity to a Django user.

## Requirements

- Python >= 3.6
- Django >= 3.2
- `requests`
- `PyJWT`

## Installation

```bash
pip install django-sso-oauth
```

## Configuration

### 1. Environment variables

Set the following variables in your `.env` file or environment:

| Variable | Description                                                                                          |
|---|------------------------------------------------------------------------------------------------------|
| `DJANGO_SSO_OAUTH_BASE_URL` | Base URL of the OAuth provider (e.g. `https://sso.example.com`)                                      |
| `DJANGO_SSO_OAUTH_CLIENT_ID` | OAuth client ID                                                                                      |
| `DJANGO_SSO_OAUTH_CLIENT_SECRET` | OAuth client secret                                                                                  |
| `DJANGO_SSO_OAUTH_REDIRECT_URL` | Redirect URI registered with the OAuth provider (e.g. `https://yourapp.example.com/sso/callback/`)   |
| `DJANGO_SSO_OAUTH_AFTER_LOGIN_URL` | Where to land after an **admin** login (default: `admin:index`)                                      |
| `DJANGO_SSO_OAUTH_USER_AFTER_LOGIN_URL` | Where to land after a **frontend** login (default: `index`)                                          |
| `DJANGO_SSO_OAUTH_AFTER_LOGOUT_URL` | Where to land after logout (default: same as `DJANGO_SSO_OAUTH_USER_AFTER_LOGIN_URL`)                |

The three URL variables accept either a URL name (`admin:index`, `dashboard`) or an absolute path
(`/dashboard/`). A single `DJANGO_SSO_OAUTH_REDIRECT_URL` is shared by both the admin and the
frontend flow.

### 2. Add to `INSTALLED_APPS`

```python
INSTALLED_APPS = [
    ...
    "django_sso_oauth",
]
```

### 3. Add middleware

Add `OauthSessionMiddleware` **after** `AuthenticationMiddleware` in your `MIDDLEWARE` setting:

```python
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django_sso_oauth.middleware.OauthSessionMiddleware",  # <-- add here
    ...
]
```

The middleware only sets `request.user` when an SSO session is present, so ordinary Django
authentication (e.g. a local superuser) keeps working alongside SSO.

### 4. Wire up URLs

In your project's `urls.py`, override the default admin login, add the frontend login/logout
routes, and add the shared OAuth callback:

```python
from django.contrib import admin
from django.urls import path, include
from django_sso_oauth import views as sso_views

urlpatterns = [
    path("admin/login/", sso_views.login_admin),            # replaces default admin login
    path("user/login/", sso_views.login_user, name="userlogin"),
    path("user/logout/", sso_views.logout_user, name="userlogout"),
    path("admin/", admin.site.urls),
    path("sso/callback/", sso_views.oauth_redirect),        # OAuth callback
    ...
]
```

> **Important:** `admin/login/` must be declared **before** `admin.site.urls` so it takes precedence.
> The `sso/callback/` path must match `DJANGO_SSO_OAUTH_REDIRECT_URL`.

### 5. Frontend usage

Point Django's `LOGIN_URL` at the frontend login view so `@login_required` sends users into the SSO
flow:

```python
LOGIN_URL = "/user/login/"
```

`@login_required` appends `?next=<original path>`, which is carried through the OAuth round trip and
honoured on the way back, so users land on the page they originally asked for.

## How it works

1. A user visiting `/admin/` is redirected to `/admin/login/`; a user visiting a `@login_required`
   page is redirected to `/user/login/`.
2. The login view redirects to the OAuth provider's authorization endpoint with a signed `state`
   parameter carrying the target (`admin` or `user`) and the `next` URL.
3. The provider redirects back to the callback with an authorization code and the `state`.
4. `oauth_redirect` verifies the `state` (rejecting forged or expired ones), exchanges the code for
   an access token, decodes the JWT to extract the user's email (`upn` or `unique_name` claim), and
   looks up the corresponding Django user.
5. The user must exist and be active. For the `admin` target the user must also be `is_staff`;
   otherwise the request is rejected with 403 and no session is created.
6. The email is stored in the session; `OauthSessionMiddleware` restores the user on every
   subsequent request.
7. `/user/logout/` flushes the Django session. The provider's own session is left untouched, so a
   subsequent login is a silent SSO re-login.

> The Django user must already exist in the database. User provisioning is not handled by this package.

## Upgrading from 1.x

Version 2.0 is a breaking change:

| 1.x | 2.x |
|---|---|
| `views.login` | `views.login_admin` |
| — | `views.login_user`, `views.logout_user` (new) |
| `middleware.OauthAdminSessionMiddleware` | `middleware.OauthSessionMiddleware` |
| middleware placed after `SessionMiddleware` | must be placed after `AuthenticationMiddleware` |

Also note:

- The callback now **requires** a valid signed `state` parameter. Since the login views generate
  it, no configuration change is needed — but bookmarked or replayed callback URLs will now 400.
- The middleware no longer forces `request.user` to `AnonymousUser` when there is no SSO session.
- Error responses now return their intended status code (1.x returned `200` for every error path).

## Running the tests

```bash
python runtests.py
```

## License

MIT
