Skip to content

Add Bearer token (JWT) authentication via PAS plugin#88

Draft
xispa wants to merge 3 commits into
2.xfrom
bearer-token-auth
Draft

Add Bearer token (JWT) authentication via PAS plugin#88
xispa wants to merge 3 commits into
2.xfrom
bearer-token-auth

Conversation

@xispa

@xispa xispa commented Apr 28, 2026

Copy link
Copy Markdown
Member

Description of the issue/feature this PR addresses

Adds Bearer token (JWT) authentication to SENAITE.JSONAPI via a dedicated PAS plugin, so external clients (CI scripts, dashboards, AI assistants, mobile apps) can authenticate stateless requests without juggling Plone session cookies.

Current behavior before PR

  • The only auth mechanisms exposed by senaite.jsonapi are HTTP Basic and the Plone session cookie.
  • /login returns the user info but no token; clients must keep the session cookie alive for every subsequent request.
  • There is no way to revoke an issued credential server-side without disabling the user.

Desired behavior after PR is merged

  • New JWTAuthenticationPlugin PAS plugin (registered on top for IExtractionPlugin + IAuthenticationPlugin) installed via a senaite.jsonapi:default GenericSetup profile.
  • Token is extracted, in this order, from:
    1. Authorization: Bearer <token> header
    2. token cookie (HttpOnly)
    3. X-JWT-Auth-Token header
  • Each user gets their own signing secret, stored in an OOBTree annotation on the portal. rotate_secret(userid) invalidates every token issued for that user — the revocation primitive that was missing.
  • /login now accepts either HTTP Basic or __ac_name/__ac_password form fields and returns {token, expires, ...user info}. The token is also set as an HttpOnly cookie so cookie-based clients keep working transparently.
  • /logout expires the cookie on the client; full server-side invalidation is done with rotate_secret.
  • pyjwt<2.0.0 pinned (Python 2.7 compatibility).

Tests

Two new doctests under src/senaite/jsonapi/tests/doctests/:

  • bearer.rst — covers extraction, valid/invalid/forged/expired tokens and secret rotation against the PAS plugin directly.
  • login.rst — exercises the full /login/auth/users/current flow with Bearer tokens, plus rejection of invalid/expired/revoked tokens.

Run with:

bin/test test_doctests -t bearer
bin/test test_doctests -t login

I confirm I have tested the PR thoroughly and coded it according to PEP8 standards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants