Skip to content
Agentmart
Submit a tool

auth-and-rbac

FastAPI auth patterns — session/JWT chains, argon2id hashing, pyotp TOTP/Email OTP, role-based access control, x-user-data gateway forwarding.

Skill by ajyadav013

IndexedMIT★ 14
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/ajyadav013-auth-and-rbac

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

FastAPI auth patterns — session/JWT chains, argon2id hashing, pyotp TOTP/Email OTP, role-based access control, x-user-data gateway forwarding. Use when building auth dependencies, MFA, or role hierarchies.

Use cases: backend, data, security, productivity

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 26, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/auth-and-rbac

What's inside

Skill name
auth-and-rbac
Description
FastAPI auth patterns — session/JWT chains, argon2id hashing, pyotp TOTP/Email OTP, role-based access control, x-user-data gateway forwarding. Use when building auth dependencies, MFA, or role hierarchies.
License
MIT
Instructions
343 lines in SKILL.md · view on GitHub

Files (8)

  • SKILL.md
  • auth-dependencies.md · references/
  • htn-design-an-authentication-system.md · references/
  • htn-serialization-deserialization-authentication-and-authorizati.md · references/
  • rbac-and-permissions.md · references/
  • repo-evidence.md · references/
  • security-hardening.md · references/
  • tokens-and-hashing.md · references/

SKILL.md (first lines)

# Auth and RBAC

FastAPI authentication and authorization patterns derived from production identity services and multi-tenant backends.

## When to use

- Implementing session-based or JWT authentication in FastAPI
- Building role-based access control (RBAC) with hierarchical roles (sys_admin > org_admin > sub_org_admin > tenant_admin > member)
- Implementing multi-factor authentication (TOTP or Email OTP) with pyotp
- Designing auth dependency chains with `Depends(require_auth)` and role-specific guards
- Integrating with API gateways that forward identity via custom headers (x-user-data JSON)
- Hashing passwords with argon2id in async thread pool to avoid blocking the event loop
- Enforcing org/tenant access checks with materialized path hierarchies
- Issuing RS256 JWT tokens with key rotation and JWKS endpoint
- Implementing refresh token rotation for secure long-lived sessions
- Adding rate limiting to login endpoints to prevent brute-force attacks
- Enforcing password expiry policies and rotation tracking
- Implementing session fingerprinting to detect hijacking attempts

## Core conventions

### Authentication Dependency Chain

**Session-based auth chain**: `get_current_session` extracts session ID from cookie → validates in Redis → decrypts → `require_auth` verifies fingerprint + request signature → role dependencies (`require_sys_admin`, `require_org_admin_or_above`, `require_sub_org_admin_or_above`) enforce RBAC.

**JWT auth chain**: `get_current_user_jwt` extracts `Authorization: Bearer` header → decodes with PyJWT → verifies signature with RS256 public key → loads user from DB → returns session-shaped dict `{"user": {...}}` compatible with downstream dependencies.

**Helper extractors**: `get_caller_user_id(session)`, `get_caller_org_id(session)`, `get_caller_role(session)`, `is_sys_admin(session)` for inline role checks in endpoint logic.

**Fingerprint validation**: Store `_fingerprint` (IP + User-Agent hash) in session; on each request, recompute and compare — mismatch deletes session and returns 401.

**Request signing (optional)**: Store client ECDSA public key `_public_key` in session; require `x-request-signature` + `x-request-timestamp` headers; verify ECDSA signature over `method|path|body|timestamp`.

### Password Hashing (argon2id)

**Async hashing**: Use `argon2.PasswordHasher` with `time_cost=3, memory_cost=65536, parallelism=4, hash_len=32, salt_len=16`; wrap `ph.hash(password)` and `ph.verify(hash, password)` in `asyncio.to_thread()` to avoid blocking the event loop.

**Password rotation tracking**: Store hashed password history in `password_history` table; `check_password_reuse` queries the last N passwords and verifies new password doesn't match any via `verify_password`.

**Rehash check**: Call `ph.check_needs_rehash(hash)` on login; if params have changed, rehash and update DB.

### JWT Tokens (RS256)

**Token issuance**: Generate RS256 JWT with `pyjwt.encode(payload, private_key, algorithm='RS256', headers={'kid': key_id})`; include `sub` (user_id), `email`, `role`, `organization_id`, `exp`, `iat`, `iss`, `aud` in payload.

**Refresh tokens**: Issue opaque UUID refresh tokens; store `{user_id, issued_at}` in Redis `refresh_token:{uuid}` with expiry; on refresh, delete old token (rotation), validate user is still active, issue new pair.

**JWKS endpoint**: Expose `/.well-known/jwks.json` with active public keys as JWK Set; include `kid`, `kty`, `use`, `alg`, `n`, `e` for each key.

**Key rotation**: Store private keys encrypted at rest (Fernet); mark old keys as inactive; new tokens use latest active key; verification accepts any active key by `kid`.

**Token verification flow**: Extract `kid` from unverified header → fetch signing key from DB → decode with `pyjwt.decode(token, public_key, algorithms=['RS256'], audience=..., issuer=...)` → validate claims → check revocation (optional).

### MFA with pyotp

**TOTP setup**: Generate secret with `pyotp.random_base32(length=32)` → create QR URI with `pyotp.totp.TOTP(secret).provisioning_uri(name=user.email, issuer_name="AppName")` → return to client once.

**TOTP verification**: Verify code with `pyotp.TOTP(secret).verify(code, valid_window=1)` (accepts ±30s drift).

**Email OTP**: Generate 6-digit code, store in Redis `email_otp:{user_id}` with TTL=300s and attempt counter; verify code + attempt count; implement resend cooldown (60s).

**Backup codes**: Generate 10 random 8-char alphanumeric codes; hash each with SHA-256; store hashes; on use, verify one-time then mark as used.

**MFA enforcement**: Check org-level MFA policy table; if user role or org requires MFA and user has no active enrollments, block login with 403 and message "MFA enrollment required".

**MFA pending session**: On login with MFA, create temp `mfa_pending:{session_token}` in Redis (TTL=300s) with `{user_id, mfa_methods: ['TOTP', 'EMAIL_OTP'], type: 'mfa_pending'}` → return `{mfa_required: true, session_token, methods}` → client submits code → verify → upgrade to full session.

### Role-Based Access Control

**Role hierarchy**: `sys_admin` (platform superuser) > `org_admin` (org tree root admin) > `sub_org_admin` (subtree admin) > `tenant_admin` (single org/tenant) > `member` (read-only or limited scope).

**Role dependency pattern**: Create FastAPI dependencies that accept `session: dict = Depends(require_auth)` and check `session.get("user", {}).get("role")` — raise 403 if insufficient role.

**Hierarchical org access checks**: `assert_org_access_async(session, target_org_id, connection_handler)` — `sys_admin` always passes; `org_admin` checks root path prefix match; `sub_org_admin` checks subtree prefix match; `tenant_admin`/`member` require exact org_id match.

**Org path pattern**: Store org hierarchy as materialized path `/root/parent/child/`; admins can manage orgs in their subtree by path prefix comparison.

**Role enum**: Define `class Role(str, Enum): L1 = "L1"; L2 = "L2"; ADMIN = "ADMIN"` for domain-specific roles; store as string column with enum constraint.

### Choosing an authorization model & OAuth 2.0 delegation

Everything above assumes RBAC. That is usually right, but treat the authorization model as a decision, not a default — and know when to delegate identity to an external provider instead of owning credentials at all.

**The three models**:

- **RBAC (roles → permissions)** — users hold roles; roles bundle permission sets (admin = full CRUD, editor = read+write, member = read-only). Easiest to audit and administer; the right default for org/team products where a small, stable set of permission tiers answers most access questions.
- **ABAC (attribute predicates)** — decisions are policy functions over attributes of the subject, resource, and context: "the author may edit their own draft", "department matches", "within business hours", "resource not archived". Reach for it when access depends on ownership, tenancy, time, or resource state — conditions a static role cannot express. More expressive, harder to audit.
- **ACL (per-object grant lists)** — each resource carries its own principal→rights list, the document-sharing / filesystem model. The natural fit for sharing-centric products (docs, files, folders); maximally precise, but management cost scales with resources × users, so it needs tooling.

**Decision heuristics**:

- Permission tiers are few, stable, and org-wide → RBAC (this skill's role dependencies and hierarchy checks are exactly this).
- Rules keep referencing who owns the resource, which tenant it belongs to, what state it is in, or when the request happens → ABAC predicates alongside your roles.
- Users grant each other access object-by-object → ACL rows on those resources.

**The hybrid most systems actually run**: RBAC for coarse tiers plus ABAC-style ownership checks in business logic (`if resource.owner_id != caller_user_id and not is_sys_admin(session): 403`). Name it explicitly — "RBAC + ownership" — so reviewers treat both layers as intentional, and enforce it at every depth (route guard, service-level ownership check, query scoping to the caller's rows); relying on the route guard alone is the classic IDOR root cause.

**Migration pressure signals**:

- **Role explosion** — compound roles multiplying (`editor_finance_emea_readonly`) means your rules are really attribute predicates; move those conditions into ABAC checks instead of minting more roles.
- **Per-resource sharing requests** — users wanting to grant specific people access to specific objects cannot be expressed as a role tier; those objects need ACL entries.

**OAuth 2.0 authorization-code flow** — for social login ("Login with Google/GitHub") or calling a third-party API on the user's behalf:

1. Your app redirects the browser to the provider's authorization server (`client_id`, `redirect_uri`, requested scopes, `state`).
2. The user authenticates and consents at the provider — your system never sees their password.
3. The provider redirects back to your `redirect_uri` with a short-lived, single-use authorization code.
4. Your backend exchanges code + client secret for tokens in a server-to-server call.
5. Your backend verifies the returned identity, then links or creates the local user row.

**Why the code indirection**: browser redirects are observable (URLs land in history, proxies, logs, referrers), so nothing in a redirect may be a usable credential. An intercepted code is worthless without the client secret held server-side. Never use the legacy implicit flow, which returns the access token directly in the URL fragment.

**PKCE for public clients**: SPAs and mobile apps cannot hold a client secret; use authorization-code + PKCE, where a per-request code verifier/challenge pair replaces the secret in the exchange.

**Callback CSRF protection**: generate a random `state` value per authorization request, bind it to the initiating session, and reject any callback whose `state` does not match — otherwise an attacker can splice their own authorization code into a victim's login.

**What to persist**: the provider's stable subject identifier linked to your own user row (`provider` + `provider_subject_id` columns); match returning users on that pair, not on email, which can change or be reassigned at the provider.

**The provider's access token is not your session**: it is scoped to the provider's API, has a lifetime you do not control, and your services cannot validate it. After linking identity, issue your own session or JWT via the chains above; store the provider token (encrypted at rest) only if you actually call the provider's API on the user's behalf.

From ajyadav013/claude-kit, MIT.

More from ajyadav013/claude-kit

Signals

Stars
14
Forks
5
Open issues
9
Last commit (this folder)
Aug 22, 2026 (2 months ago)
Last release
Sep 7, 2026 (1 month ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from ajyadav013

Cite this page

auth-and-rbac skill. Agent Mart. https://agentmart.dev/skills/ajyadav013-auth-and-rbac (last updated 2026-10-24)