Skip to main content

onehux-sso

A real, installable Django app wrapping OneHux Accounts' Authorization Code + PKCE flow against its real hosted login page — formalizing what the Django integration guide otherwise only shows as copy-paste example code.

Install

pip install onehux-sso

pypi.org/project/onehux-sso

Two hosts — don't mix them up

accounts.onehux.com serves the hosted login/logout pages a browser is redirected to. api-accounts.onehux.com serves the actual OAuth API your backend calls server-to-server. This package keeps them as two separate settings (LOGIN_BASE_URL / API_BASE_URL) precisely because collapsing them into one host was a real, confirmed bug in the original integration guides (see the backend repo's README.md, ADR-070) — the wrong host doesn't error loudly, it silently 404s.

If your Organization has a live custom domain (Dashboard → Settings → Branding, see the backend repo's README.md ADR-027), set LOGIN_BASE_URL to that domain instead — it's what your end users' browsers actually land on, so it should match whatever you've branded. Never override API_BASE_URL: it has no per-Organization customization and never needs any — every call there is server-to-server via your client_id/client_secret, never seen by an end user.

Setup

  1. Register a real confidential-client Application in your OneHux Accounts Organization (Dashboard → Applications), with a redirect_uri pointing at wherever you mount this package's callback/ URL, and your post_logout_redirect_uri registered in that same list — OneHux Accounts validates both against the one redirect_uris list, not two separate ones.

  2. Add to INSTALLED_APPS:

    INSTALLED_APPS = [
        ...,
        "onehux_sso",
    ]
    
  3. Add the settings block:

    ONEHUX_SSO = {
        "CLIENT_ID": "onehux_client_...",
        "CLIENT_SECRET": "onehux_secret_...",
        "REDIRECT_URI": "https://yourapp.example.com/auth/callback/",
        "POST_LOGOUT_REDIRECT_URI": "https://yourapp.example.com/auth/logged-out/",
        # Everything below is optional — these are the defaults:
        "LOGIN_BASE_URL": "https://accounts.onehux.com",
        "API_BASE_URL": "https://api-accounts.onehux.com",
        "SCOPE": "openid profile email",
        "LOGIN_SUCCESS_REDIRECT": "/",
        "LOGOUT_SUCCESS_REDIRECT": "/",
        "SESSION_ACCESS_TOKEN_KEY": "onehux_access_token",
    }
    
  4. Wire the URLs:

    # yourproject/urls.py
    from django.urls import include, path
    
    urlpatterns = [
        ...,
        path("auth/", include("onehux_sso.urls")),
    ]
    

This gives you four real, working endpoints: /auth/login/, /auth/callback/, /auth/logout/, and /auth/userinfo/ (a ready-to-use JSON endpoint your own frontend can call with credentials: 'include', matching the BFF pattern — your frontend never talks to OneHux directly).

Using the client directly

If you'd rather wire your own views instead of using the ones above:

from onehux_sso import OneHuxClient

client = OneHuxClient.from_settings()

pending = client.start_authorization()
# stash pending.state / pending.code_verifier in request.session, then:
# return HttpResponseRedirect(pending.authorization_url)

tokens = client.exchange_code(
    code=request.GET["code"],
    state=request.GET["state"],
    expected_state=request.session["onehux_sso_state"],
    code_verifier=request.session["onehux_sso_pkce_verifier"],
)

claims = client.get_userinfo(access_token=tokens.access_token)

logout_url = client.build_logout_url()

Public application launcher

GET /api/v1/organizations/{org_slug}/public-applications/ is a real, public, unauthenticated platform endpoint — no client_id/client_secret involved, usable for any Organization by its own slug, not just your own configured one. It returns only name/logo_url/home_url for Applications that Organization has opted into public listing — a pure "what can I launch" list, never a way to start a sign-in flow.

apps = client.get_public_applications(org_slug="onehux")
# [PublicApplication(name="ODS", logo_url="https://...", home_url="https://...")]

Rendering is entirely up to you — this package ships the data method only, no template or component. A plain, unstyled illustration (adapt this to your own design, don't copy it as-is):

{% for app in public_applications %}
  <a href="{{ app.home_url }}">
    <img src="{{ app.logo_url }}" alt="{{ app.name }}">
    {{ app.name }}
  </a>
{% endfor %}

Logging out — what actually happens, and how to hear about it immediately

Two distinct logout paths reach the platform's identical underlying session-revocation call (POST /api/v1/sessions/me/logout/), and OneHux Accounts genuinely, immediately revokes the platform-wide session either way — this was traced directly against the backend, not assumed. What differs is how this app finds out:

  • RP-initiated logout — the user clicks "log out" inside this app itself (client.build_logout_url() / /auth/logout/). This app already knows: it's the one that cleared request.session[SESSION_ACCESS_TOKEN_KEY] and drove the redirect. Nothing further to do.
  • IdP-initiated logout — the user logs out of a different app, or directly at accounts.onehux.com/dashboard. The platform-wide session is revoked immediately and correctly, exactly the same as the RP-initiated case — but this app only finds out if it's listening for it.

OneHux Accounts implements real OIDC Back-Channel Logout (spec: openid-connect-backchannel-1_0) to close that gap: BackchannelLogoutView receives a signed logout_token POST the instant any session tied to this app is revoked, anywhere, and clears the matching local Django session immediately — not on the next stale /userinfo call.

To turn this on:

  1. Mount the package's URLs as shown in Setup above — BackchannelLogoutView is already included at /auth/backchannel-logout/ (adjust for whatever prefix you mounted at).
  2. Register that exact URL with OneHux:
    PATCH /api/v1/applications/{id}/backchannel-logout/
    { "backchannel_logout_uri": "https://yourapp.example.com/auth/backchannel-logout/" }
    
    The response includes backchannel_logout_secret exactly once — this is a dedicated signing secret, deliberately not your CLIENT_SECRET (the backend stores that only as a one-way hash and can never read it back to sign anything with it).
  3. Set ONEHUX_SSO['BACKCHANNEL_LOGOUT_SIGNING_SECRET'] to that value.

Without steps 1–3, IdP-initiated logout is still real and immediate at the platform level — this app just won't hear about it until its own next /userinfo call fails with TokenExpiredError, bounded by the access token's 15-minute lifetime. With them wired up, both logout paths are functionally immediate from this app's point of view too.

No refresh token today — this is real, not a bug

OneHux Accounts access tokens are a 15-minute, single-issue lifetime. This platform does not currently issue a refresh token. client.get_userinfo() raises onehux_sso.TokenExpiredError when the token has expired or been revoked — catch it and send the user back through client.start_authorization() for a fresh login. There is no silent-refresh path to fall back to; this package makes that explicit rather than hiding it behind a generic error.

Example project

See example/ for a complete, runnable Django project using this package end-to-end — registered against a real disposable test Application and actually run through the full browser flow against production, not just unit-tested in isolation.

License

Apache License 2.0 — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

onehux_sso-0.1.2.tar.gz (23.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

onehux_sso-0.1.2-py3-none-any.whl (22.4 kB view details)

Uploaded Python 3

File details

Details for the file onehux_sso-0.1.2.tar.gz.

File metadata

  • Download URL: onehux_sso-0.1.2.tar.gz
  • Upload date:
  • Size: 23.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for onehux_sso-0.1.2.tar.gz
Algorithm Hash digest
SHA256 cb405dd6d93b59ef9dc8ae3fca86abe301657c5870a3e089d6725928036dd166
MD5 341ecaed5811b4e0755bb0fb6a9e1f63
BLAKE2b-256 f3287897218d0118c014c327c1fff23fd0d30cf53cd8c9eaacf45d1a2769e88d

See more details on using hashes here.

Provenance

The following attestation bundles were made for onehux_sso-0.1.2.tar.gz:

Publisher: publish.yml on Onehux/onehux-sso-django

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file onehux_sso-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: onehux_sso-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 22.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for onehux_sso-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 4939fc9a876d40ad2d88de03ef41bdea2c858b4f357cbd3c9a108945a9fce4fe
MD5 c464f2633d9c9d4c1c4e4c2340b759e0
BLAKE2b-256 7019cc87f62b4bd12a818a0434c7fff754c7f3018ea668f6b394b5360b69e446

See more details on using hashes here.

Provenance

The following attestation bundles were made for onehux_sso-0.1.2-py3-none-any.whl:

Publisher: publish.yml on Onehux/onehux-sso-django

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.0

2 files

0.1.3

2 files

This release

0.1.2 This release

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page