Metadata-Version: 2.4
Name: shinyhub-identity
Version: 0.1.0
Summary: Read the signed ShinyHub identity a Shiny/Streamlit/Dash app receives, in one call.
Project-URL: Homepage, https://github.com/rvben/shinyhub
Project-URL: Repository, https://github.com/rvben/shinyhub
Project-URL: Issues, https://github.com/rvben/shinyhub/issues
Author-email: "Ruben J. Jongejan" <ruben.jongejan@gmail.com>
License-Expression: MIT
Keywords: auth,identity,jwt,shiny,shinyhub
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Requires-Python: >=3.9
Requires-Dist: pyjwt>=2.0
Provides-Extra: test
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# shinyhub-identity

Read the signed identity [ShinyHub](https://github.com/rvben/shinyhub) forwards
to your app, in one call. No per-app JWT plumbing.

ShinyHub injects a short-lived, per-app HS256 JWT
(`X-Shinyhub-Identity-Token`) into every request it proxies, and hands your app
its verification key via `SHINYHUB_IDENTITY_KEY` and `SHINYHUB_APP_SLUG`. This
package verifies that token and returns the identity.

```
pip install shinyhub-identity
# or: uv add shinyhub-identity
```

## Use it

```python
from shinyhub_identity import current_user

def server(input, output, session):
    user = current_user(session.http_conn.headers)   # None when anonymous
    if user is None:
        ...  # logged-out visitor
    elif "platform-admins" in user.groups:
        ...  # gate admin features on the VERIFIED groups
```

`current_user(headers)` returns an `Identity` (`user_id`, `username`, `role`,
`email`, `groups`, `groups_truncated`, and the raw `claims`) or `None`. `email`
is `""` unless the deployment's forward-auth SSO asserts one. It works with any
header mapping - Shiny for Python's `session.http_conn.headers`, a
Starlette/Flask request's headers, or a plain `dict`.

**Every failure is anonymous, never a crash.** No token, bad signature, wrong
audience/issuer, an expired token, or no ShinyHub in front at all (running the
app locally) all return `None`, so your app stays testable without SSO.

`current_user` reads `SHINYHUB_IDENTITY_KEY`/`SHINYHUB_APP_SLUG` from the
environment by default; pass `key=`/`slug=` explicitly for tests.

## Why verify, not just read the plain headers?

ShinyHub also forwards convenience plain headers (`X-Shinyhub-User`, `-Role`,
`-Groups`, ...) and strips any client-supplied ones. But app processes listen on
host-local ports, so a co-located process can bypass the proxy and forge plain
headers. **Anything that gates access must verify the token** - which is exactly
what this package does. See ShinyHub's `docs/identity.md` for the full trust
model.
