A lightweight and extensible Single Sign-On (SSO) authentication library for Python.
Project description
more-sso-auth — Lightweight SSO for Python
A small, easy-to-use Single Sign-On (SSO) helper for Python apps and AWS Lambda.
What it gives you
- RS256 JWT validation using a public key fetched from a URL.
- In-memory caching for public keys (no constant network calls).
- A header-based decorator
@auth_requiredfor quick route protection. - A
root_auth_requireddecorator suitable for Lambda handlers. - Programmatic and environment-variable configuration.
- An extensible permission system via
BasePermissionso you can write custom rules.
Quick start (3 minutes)
- Install
pip install more-sso-auth
pip install PyJWT
pip install cryptography
- Configure (either method)
Programmatic
from more_sso import init_sso_config
init_sso_config(
public_key_uri="<your-kms-id>",
audience="<your-app>"
)
Environment variables
export PUBLIC_KEY_URI="<your-kms-id>"
export AUDIENCE="<your-app>"
- Protect a function
from more_sso import auth_required
@auth_required(permission="pma.role", value="admin")
def my_func(event, *args, **kwargs):
user = event["requestContext"]["user"]
return {"ok": True}
The decorator will validate the bearer JWT from the
Authorizationheader and inject the decoded payload consistently intoevent["requestContext"]["user"].
Core Concepts
Token validation
Use validate_token(token) to validate a JWT programmatically. It raises JWTValidationError on invalid tokens.
from more_sso import validate_token, JWTValidationError
try:
user = validate_token(token)
except JWTValidationError as e:
# handle unauthenticated
print("Invalid token:", str(e))
validate_token performs usual checks: signature (RS256), audience (the AUDIENCE you configured), and token expiry.
Decorators
@auth_required(...)— decorator for fine-grained route enforcement. It supports either a simple permission check or a custom permission class. After validation it injects the decoded token intoevent["requestContext"]["user"].@root_auth_required— a simple decorator for Lambda handlers that enforces authentication at the top level and injects the decoded user intoevent["requestContext"]["user"]. If authentication fails, the decorator returns a 401 response (for AWS Lambda-style handlers).
Example: root-level Lambda
from more_sso import root_auth_required
@root_auth_required
def lambda_handler(event, context):
user = event["requestContext"]["user"]
return {"statusCode": 200, "body": f"Hello {user['sub']}"}
Permissions
There are two ways to enforce authorization with auth_required:
-
Simple claim check — pass
permissionandvalue.permissionis a dotted path into the decoded JWT payload (for example:pma.rolewill look upuser['permissions']["pma"]["role"]).valueis the expected value.- the
userclaims will also contain thepermissionsas a json with app specific roles and permisson attributes
@auth_required(permission="my_app.role", value="admin")
def handler(event, *args, **kwargs):
user = event["requestContext"].get("user", {})
# ...
- Custom permission class — extend
BasePermissionand implementhas_access().
from more_sso import BasePermission, auth_required, AccessDeniedError
class CustomPermission(BasePermission):
def __init__(self,*args,**kwargs):
super.__init__(*args,**kwargs)
self.extras = kwargs
def check_something(self )
if self.extras['id'] == self.user['permissions']['id']
return "something"
...
# to be implemented
def has_access(self) -> bool:
# example: allow only admins
if self.check_something() =='something'
return self.user['permissions'].get("role") == "admin"
@auth_required(permission_class=CustomPermission,**kwargs)
# kwargs are stored in self.extras of permission class which can be accessed in has_access
def admin_only(event, *args, **kwargs):
return {"ok": "admin access granted"}
# caller example
try:
admin_only()
except AccessDeniedError:
# return 403
pass
Behavior: when permission check fails the decorator raises AccessDeniedError (or returns an appropriate 403 when used as a top-level Lambda decorator if configured that way).
AWS Lambda patterns
Top-level enforcement (recommended for simple APIs):
from more_sso import root_auth_required
@root_auth_required
def lambda_handler(event, context):
user = event["requestContext"]["user"]
# authorized
return {"statusCode": 200, "body": "ok"}
Function-level enforcement (fine-grained):
from more_sso import auth_required, AccessDeniedError
@auth_required(permission="my_app.role", value="admin")
def admin_action(event, *args, **kwargs):
return {"ok": True}
def lambda_handler(event, context):
try:
return admin_action(event)
except AccessDeniedError:
return {"statusCode": 403, "body": "Access denied"}
Note: Both auth_required and root_auth_required inject the decoded token consistently into event["requestContext"]["user"].
API reference (quick)
init_sso_config(public_key_uri: str, audience: str)— initialize the library programmatically.validate_token(token: str) -> dict— validate token and return decoded payload. RaisesJWTValidationErroron failure.auth_required(permission: str = None, value: Any = None, permission_class: Type[BasePermission] = None)— decorator to protect functions. Injects user intoevent["requestContext"]["user"].root_auth_required— decorator for Lambda handlers.BasePermission— extend this and implementhas_access(self) -> boolfor custom checks. The class has access toself.user.JWTValidationError— raised for invalid JWTs.AccessDeniedError— raised when permission checks fail.
Security notes
- Always validate
aud(audience) andexp(expiry).more-sso-authchecks these by default when configured. - Rotate keys at the Identity Provider (IdP) and ensure the IdP exposes the new public key at the configured
PUBLIC_KEY_URI.
Troubleshooting & FAQ
Q: Where does the library read the token from?
A: From the Authorization: Bearer <token> header in incoming requests.
Q: How do I check complex permissions (lists, nested claims)?
A: Use a BasePermission subclass and implement has_access() — you have full access to the decoded payload and can evaluate arbitrarily complex logic.
Contributing & License
- Source:
https://github.com/more-retail/moresso - License: MIT
Contributions are welcome via PRs. Please follow the repository's contribution guidelines for tests and code style.
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 more_sso_auth-1.4.0.tar.gz.
File metadata
- Download URL: more_sso_auth-1.4.0.tar.gz
- Upload date:
- Size: 9.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a7e95749c85993e91fea12935c3c19962767a9245984c2c21c5be5c6082464e6
|
|
| MD5 |
6587ae13ff1d66cf6dfb425b03a4688c
|
|
| BLAKE2b-256 |
c823d462950dafe78fd1ae61362f3d74a938d01dec9d26eea96df18c6572f366
|
Provenance
The following attestation bundles were made for more_sso_auth-1.4.0.tar.gz:
Publisher:
workflow.yml on more-retail/moresso
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
more_sso_auth-1.4.0.tar.gz -
Subject digest:
a7e95749c85993e91fea12935c3c19962767a9245984c2c21c5be5c6082464e6 - Sigstore transparency entry: 601750962
- Sigstore integration time:
-
Permalink:
more-retail/moresso@81bc50563b13e5097a83688426a26eb1a4a880fb -
Branch / Tag:
refs/tags/1.4.0 - Owner: https://github.com/more-retail
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@81bc50563b13e5097a83688426a26eb1a4a880fb -
Trigger Event:
release
-
Statement type:
File details
Details for the file more_sso_auth-1.4.0-py3-none-any.whl.
File metadata
- Download URL: more_sso_auth-1.4.0-py3-none-any.whl
- Upload date:
- Size: 8.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef875640ce99fc83e4af8e7f1efa8e522bd4388e0db1a218bce2665aad3c1290
|
|
| MD5 |
f57fa6201dda3786da6016541eb40961
|
|
| BLAKE2b-256 |
8a01f7afa9a4cac0f35aad4e47a6da1cdf3063cdfab728a2b234c0e62f56a6f4
|
Provenance
The following attestation bundles were made for more_sso_auth-1.4.0-py3-none-any.whl:
Publisher:
workflow.yml on more-retail/moresso
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
more_sso_auth-1.4.0-py3-none-any.whl -
Subject digest:
ef875640ce99fc83e4af8e7f1efa8e522bd4388e0db1a218bce2665aad3c1290 - Sigstore transparency entry: 601750992
- Sigstore integration time:
-
Permalink:
more-retail/moresso@81bc50563b13e5097a83688426a26eb1a4a880fb -
Branch / Tag:
refs/tags/1.4.0 - Owner: https://github.com/more-retail
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@81bc50563b13e5097a83688426a26eb1a4a880fb -
Trigger Event:
release
-
Statement type: