# Auth

> Understand and customize authentication in this project. Use when: adding auth to routes, swapping auth providers (Clerk, Auth0, custom SSO), debugging auth issues, or understanding the UserInfo contract and get_current_user dependency.

- **Type:** Skill
- **Install:** `agentstack add skill-benja-pauls-serpentstack-auth`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Benja-Pauls](https://agentstack.voostack.com/s/benja-pauls)
- **Installs:** 0
- **Category:** [Search](https://agentstack.voostack.com/c/search)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Benja-Pauls](https://github.com/Benja-Pauls)
- **Source:** https://github.com/Benja-Pauls/SerpentStack/tree/main/.skills/auth
- **Website:** https://www.npmjs.com/package/serpentstack

## Install

```sh
agentstack add skill-benja-pauls-serpentstack-auth
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Auth

SerpentStack ships with working local JWT authentication (register, login, token validation). This skill explains the auth architecture and how to swap providers.

## How Auth Works (Built-in)

### Architecture

```
POST /api/v1/auth/register  →  UserService.register()  →  bcrypt hash  →  DB  →  JWT
POST /api/v1/auth/login     →  UserService.authenticate()  →  verify hash  →  JWT
GET  /api/v1/auth/me        →  get_current_user dependency  →  decode JWT  →  UserResponse

Any protected route:
    @router.delete("/{id}")
    async def delete(user: UserInfo = Depends(get_current_user)):
        # user.user_id, user.email available here
```

### Key Files

| File | Role |
|---|---|
| `backend/app/routes/auth.py` | Auth routes + `get_current_user` dependency |
| `backend/app/services/user.py` | Registration, authentication, password hashing |
| `backend/app/models/user.py` | User SQLAlchemy model (email, hashed_password) |
| `backend/app/schemas/user.py` | Request/response schemas (register, login, token) |
| `backend/app/middleware/auth.py` | Optional global auth middleware (not enabled by default) |

### The `UserInfo` Contract

All protected routes receive a `UserInfo` object via dependency injection:

```python
class UserInfo(BaseModel):
    user_id: str
    email: str | None = None
    name: str | None = None
    raw_claims: dict[str, Any] = {}
```

**This is the interface between auth and the rest of the app.** When swapping providers, keep this shape — every route that uses `Depends(get_current_user)` depends on it.

## Protecting a Route

Add `Depends(get_current_user)` to any route that requires authentication:

```python
from app.routes.auth import UserInfo, get_current_user

@router.post("")
async def create_thing(
    payload: ThingCreate,
    user: UserInfo = Depends(get_current_user),  # ← requires valid JWT
    db: AsyncSession = Depends(get_db),
    service: ThingService = Depends(get_thing_service),
) -> ThingResponse:
    thing = await service.create(payload, owner_id=user.user_id)
    await db.commit()
    return ThingResponse.model_validate(thing)
```

For optional auth (authenticated if token present, anonymous otherwise):

```python
from app.routes.auth import get_optional_user

@router.get("")
async def list_things(
    user: UserInfo | None = Depends(get_optional_user),
) -> list[ThingResponse]:
    # user is None if no token, UserInfo if authenticated
    ...
```

## Swapping to an External Provider

To replace the built-in JWT auth with Clerk, Auth0, or another provider:

### Step 1: Replace `get_current_user` in `routes/auth.py`

The **only function you need to change** is `get_current_user`. Replace JWT decode with your provider's token validation:

**For Clerk** — see [Clerk FastAPI guide](https://clerk.com/docs/references/fastapi/overview):
```python
from jwt import PyJWKClient

jwks_client = PyJWKClient("https://your-clerk-domain/.well-known/jwks.json")

async def get_current_user(
    credentials: HTTPAuthorizationCredentials | None = Security(bearer_scheme),
) -> UserInfo:
    if credentials is None:
        raise HTTPException(status_code=401, detail="Not authenticated")

    token = credentials.credentials
    signing_key = jwks_client.get_signing_key_from_jwt(token)
    payload = jwt.decode(token, signing_key.key, algorithms=["RS256"])

    return UserInfo(
        user_id=payload["sub"],
        email=payload.get("email"),
        name=payload.get("name"),
        raw_claims=payload,
    )
```

**For Auth0** — see [Auth0 FastAPI guide](https://auth0.com/docs/quickstart/backend/python/01-authorization):
```python
jwks_client = PyJWKClient("https://your-tenant.auth0.com/.well-known/jwks.json")

async def get_current_user(...) -> UserInfo:
    # Same pattern, add audience validation:
    payload = jwt.decode(
        token, signing_key.key, algorithms=["RS256"],
        audience="your-api-audience",
        issuer="https://your-tenant.auth0.com/",
    )
    return UserInfo(user_id=payload["sub"], email=payload.get("email"), ...)
```

### Step 2: Remove unused files (optional)

If you no longer need local registration/login:
- Remove `backend/app/services/user.py`
- Remove `backend/app/models/user.py` (and its import in `models/__init__.py`)
- Remove `backend/app/schemas/user.py`
- Remove the `/register` and `/login` routes from `routes/auth.py`
- Remove `passlib[bcrypt]` from `pyproject.toml`

### Step 3: Update environment variables

Add your provider's config to `.env`:
```bash
# For Clerk:
CLERK_JWKS_URL=https://your-clerk-domain/.well-known/jwks.json

# For Auth0:
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_AUDIENCE=your-api-audience
```

### What stays the same

- `UserInfo` shape — all routes keep working
- `Depends(get_current_user)` pattern — no route changes needed
- `get_optional_user` — works with any provider
- Frontend token storage pattern — still sends `Authorization: Bearer `

## Testing Auth

```bash
# Register a user
curl -X POST http://localhost:8000/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com", "password": "testpass123"}'

# Login
curl -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com", "password": "testpass123"}'

# Use the token
TOKEN=""
curl http://localhost:8000/api/v1/auth/me \
  -H "Authorization: Bearer $TOKEN"

# Protected route (delete item)
curl -X DELETE http://localhost:8000/api/v1/items/ \
  -H "Authorization: Bearer $TOKEN"
```

## Troubleshooting

| Problem | Cause | Fix |
|---|---|---|
| 401 on every request | Missing or malformed `Authorization: Bearer ` header | Check header format, ensure token isn't expired |
| 422 on register | Password too short or invalid email | Password must be ≥8 chars, email must be valid |
| 409 on register | Email already taken | Use a different email or login instead |
| `jwt.InvalidTokenError` in logs | Token signed with wrong key or expired | Check `SECRET_KEY` matches between token creation and validation |

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Benja-Pauls](https://github.com/Benja-Pauls)
- **Source:** [Benja-Pauls/SerpentStack](https://github.com/Benja-Pauls/SerpentStack)
- **License:** MIT
- **Homepage:** https://www.npmjs.com/package/serpentstack

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-benja-pauls-serpentstack-auth
- Seller: https://agentstack.voostack.com/s/benja-pauls
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
