Add local Pinscope multi-user auth for shared projects.
Self-host email/password accounts enable the existing collaborator invite flow without Clerk; first admin inherits users/local projects. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""Local Pinscope auth endpoints (register / login / me)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Request
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from backend.config import settings
|
||||
from backend.services import local_jwt, local_users
|
||||
|
||||
router = APIRouter(prefix="/auth", tags=["auth"])
|
||||
|
||||
|
||||
class RegisterRequest(BaseModel):
|
||||
email: str
|
||||
password: str = Field(min_length=8)
|
||||
name: str | None = None
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
email: str
|
||||
password: str
|
||||
|
||||
|
||||
def _require_local_auth() -> None:
|
||||
if not settings.use_local_auth:
|
||||
raise HTTPException(
|
||||
400,
|
||||
"Local auth is not enabled. Set AUTH_JWT_SECRET on the server.",
|
||||
)
|
||||
|
||||
|
||||
@router.get("/mode")
|
||||
async def auth_mode():
|
||||
"""Public: how the frontend should authenticate."""
|
||||
if settings.use_clerk:
|
||||
return {"mode": "clerk", "auth_enabled": True}
|
||||
if settings.use_local_auth:
|
||||
return {"mode": "local", "auth_enabled": True}
|
||||
return {"mode": "off", "auth_enabled": False}
|
||||
|
||||
|
||||
@router.post("/register")
|
||||
async def register(body: RegisterRequest):
|
||||
_require_local_auth()
|
||||
try:
|
||||
user = local_users.create_user(body.email, body.password, body.name)
|
||||
except ValueError as e:
|
||||
raise HTTPException(400, str(e)) from e
|
||||
token = local_jwt.issue_token(user.user_id, user.email)
|
||||
return {"token": token, "user": user.public()}
|
||||
|
||||
|
||||
@router.post("/login")
|
||||
async def login(body: LoginRequest):
|
||||
_require_local_auth()
|
||||
user = local_users.authenticate(body.email, body.password)
|
||||
if not user:
|
||||
raise HTTPException(401, "Invalid email or password")
|
||||
token = local_jwt.issue_token(user.user_id, user.email)
|
||||
return {"token": token, "user": user.public()}
|
||||
|
||||
|
||||
@router.get("/me")
|
||||
async def me(request: Request):
|
||||
_require_local_auth()
|
||||
user_id = getattr(request.state, "user_id", None)
|
||||
if not user_id or user_id == "local" or user_id == "anonymous":
|
||||
raise HTTPException(401, "Authentication required")
|
||||
user = local_users.get_user(user_id)
|
||||
if not user:
|
||||
raise HTTPException(401, "User not found")
|
||||
return user.public()
|
||||
Reference in New Issue
Block a user