Identity and access primitives: OAuth2/OIDC, SAML, passwords, JWT sessions, DPoP, TOTP, WebAuthn, and the anti-automation controls that guard them
Project description
3tears-iam
threetears.iam -- the identity and access primitives every authenticating
service in the platform needs: password handling, OAuth2/OIDC, SAML, GitHub
sign-in, session tokens, DPoP, TOTP, WebAuthn, and the anti-automation
controls that keep all of it from being brute-forced.
Why this exists
Two services in this ecosystem grew their own identity layers independently. Both wrote argon2id password hashing with anti-enumeration timing. Both wrote a GitHub OAuth2 authorization-code flow. Both wrote a NATS-KV login throttle, a single-use SHA-256 ticket store, and a JWT mint/verify pair that pins its claim set. Neither could use the other's, because each was welded to its own database schema, its own transport, and its own config prefix.
That is the failure this package exists to stop. The protocol work -- RFC 7636
PKCE, RFC 9449 DPoP, RFC 6238 TOTP, OIDC discovery and id_token verification,
SAML assertion handling, the OAuth2 code exchange -- is the same everywhere.
Getting it subtly wrong is a security bug, and getting it subtly wrong twice
means fixing it twice, in two repos, on two schedules, and finding out the
second one was missed during an incident.
Model
The package owns protocol, crypto, and policy. It owns nobody's database schema and nobody's wire DTOs.
That line is deliberate. The two services that seeded this package disagree on
almost everything below the protocol layer -- one is NATS-RPC-native with a
multi-tenant Postgres identity schema, the other is a FastAPI app with its own
control plane -- and any attempt to unify their persistence would have produced
an abstraction neither could use. So state lives behind narrow Protocols
(SingleUseTicketStore, AttemptLimiter, StateStore), with a NATS-KV
implementation shipped for the common case and nothing stopping a caller from
supplying its own.
Everything else follows from that:
- Pure functions where the protocol allows it. PKCE verification, password policy, step-up freshness, claim mapping, and API-key hashing take arguments and return answers. No I/O, no clock you cannot inject, no global state.
- Algorithms are pinned from literals, never read from the input. A DPoP
proof does not get to say which algorithm verifies it. An
id_tokendoes not get to selectnone. This mirrorsthreetears.core.security.identity_token's discipline, and the pins are written so a static reader can audit them. - Fail closed by default, and without a side channel. A malformed stored hash is an
authentication failure, not a 500. The one place a caller may choose otherwise is
NatsKvAttemptLimiter'sfail_open, which exists for a cheap throttle sitting in front of an authoritative check -- it defaults to closed, and a counter with nothing behind it must leave it that way. A rejected password never says which rule it broke when saying so would build an oracle. Errors carry structural reasons only -- never token strings, key material, or credentials -- so they are safe to log at a verification boundary. - Builds on core, does not fork it.
jwk_thumbprint,build_jwks,generate_signing_keypair,ReplayGuard,RevocationGuard,WindowedCounterandseal/open_secretalready exist inthreetears.core. This package imports them.
Public surface
Imported per module -- threetears.iam itself exports only __version__, so reach for the
submodule that owns the thing:
from threetears.iam.passwords import hash_password
from threetears.iam.tokens import SessionClaims, mint_session_token
from threetears.iam.stores.nats_kv import state_store, ticket_store
- Passwords (
.passwords,.breach) --hash_password,verify_password,validate_new_password,normalize_password,PasswordVerifyResult,PasswordPolicyError, plusBreachCorpusfor k-anonymity breach screening. argon2id for new hashes, bcrypt verify-then-upgrade for migrated ones, NFKC normalization always. - OAuth2 / OIDC (
.pkce,.oidc,.github) --PkceChallengeand the RFC 7636 verifier,OidcDiscoveryClient,verify_id_token,OidcIdentity,GithubOAuth2Client,GithubProfile. - SAML (
.saml, extra:saml) --SamlMetadataResolver, assertion identity extraction, relay-state validation. - Sessions (
.tokens,.rotation) --SessionClaims,mint_session_token,verify_session_tokenover EdDSA or HS256,mint_token_pair,TokenPair,sole_audience, androtate_refresh_tokenwith reuse detection. - Proof of possession (
.dpop) --validate_dpop_proof(RFC 9449, ES256/P-256). - Second factors (
.totp,.webauthn) -- TOTP enrolment and verification, backup codes, and (extra:webauthn) passkey registration/assertion helpers. - Anti-automation (
.stores,.clientip) -- theAttemptLimiterProtocol and itsNatsKvAttemptLimiterimplementation overthreetears.core.coordination.WindowedCounter, plusresolve_client_ipfor trusted-proxy-aware rate-limit keying. - Storage seams (
.stores) --SingleUseTicketStoreandStateStoreProtocols,hash_ticket/new_ticket_secret, thethreetears.iam.stores.nats_kvimplementations with theirstate_store/ticket_storefactories, and in-memory doubles inthreetears.iam.stores.memoryfor consumer tests.
Install
pip install 3tears-iam
pip install '3tears-iam[saml]' # adds pysaml2; needs the xmlsec1 system binary
pip install '3tears-iam[webauthn]' # adds passkey support
Versioning policy
3tears-iam versions in lockstep with the rest of the 3tears monorepo: every
package shares one version, tracking the framework git tag. All packages move
together.
Project details
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 3tears_iam-0.22.1.tar.gz.
File metadata
- Download URL: 3tears_iam-0.22.1.tar.gz
- Upload date:
- Size: 108.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e9d6b4844bfb31aa16fa9a4aba1e67189404fd835e22f321f7ad4a531ddec006
|
|
| MD5 |
fbb7b67dd09240970bf7ada92fe1863d
|
|
| BLAKE2b-256 |
cfbb8b678e23c34daf04b16479b9aed1eb827cd82cfc5227f7e1a096ac2c9a01
|
Provenance
The following attestation bundles were made for 3tears_iam-0.22.1.tar.gz:
Publisher:
release.yml on pacepace/3tears
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
3tears_iam-0.22.1.tar.gz -
Subject digest:
e9d6b4844bfb31aa16fa9a4aba1e67189404fd835e22f321f7ad4a531ddec006 - Sigstore transparency entry: 2283352309
- Sigstore integration time:
-
Permalink:
pacepace/3tears@39504e3078abf4da283e655ea853fe476ab6f892 -
Branch / Tag:
refs/tags/v0.22.1 - Owner: https://github.com/pacepace
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@39504e3078abf4da283e655ea853fe476ab6f892 -
Trigger Event:
push
-
Statement type:
File details
Details for the file 3tears_iam-0.22.1-py3-none-any.whl.
File metadata
- Download URL: 3tears_iam-0.22.1-py3-none-any.whl
- Upload date:
- Size: 82.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b30c71848545fe7ea32a2645e0e1cadeac056c617b3898e0d8448d0560059278
|
|
| MD5 |
ac93914b6c1eafe630bda978c282cb33
|
|
| BLAKE2b-256 |
5e2965caad17336925b36562942e42832e067a3462d5825ec841b6e3f7f14215
|
Provenance
The following attestation bundles were made for 3tears_iam-0.22.1-py3-none-any.whl:
Publisher:
release.yml on pacepace/3tears
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
3tears_iam-0.22.1-py3-none-any.whl -
Subject digest:
b30c71848545fe7ea32a2645e0e1cadeac056c617b3898e0d8448d0560059278 - Sigstore transparency entry: 2283360084
- Sigstore integration time:
-
Permalink:
pacepace/3tears@39504e3078abf4da283e655ea853fe476ab6f892 -
Branch / Tag:
refs/tags/v0.22.1 - Owner: https://github.com/pacepace
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@39504e3078abf4da283e655ea853fe476ab6f892 -
Trigger Event:
push
-
Statement type: