diff --git a/passkey/bootstrap.py b/passkey/bootstrap.py index 7f099a2..93b61c8 100644 --- a/passkey/bootstrap.py +++ b/passkey/bootstrap.py @@ -66,7 +66,7 @@ async def bootstrap_system( dict: Contains information about created entities and reset link """ # Create permission first - will fail if already exists - perm0 = Permission(id="auth/admin", display_name="Master Admin") + perm0 = Permission(id="auth:admin", display_name="Master Admin") await globals.db.instance.create_permission(perm0) org = Org(uuid7.create(), org_name or "Organization") @@ -122,7 +122,7 @@ async def check_admin_credentials() -> bool: try: # Get permission organizations to find admin users permission_orgs = await globals.db.instance.get_permission_organizations( - "auth/admin" + "auth:admin" ) if not permission_orgs: @@ -173,7 +173,7 @@ async def bootstrap_if_needed( """ try: # Check if the admin permission exists - if it does, system is already bootstrapped - await globals.db.instance.get_permission("auth/admin") + await globals.db.instance.get_permission("auth:admin") # Permission exists, system is already bootstrapped # Check if admin needs credentials (only for already-bootstrapped systems) await check_admin_credentials() diff --git a/passkey/db/sql.py b/passkey/db/sql.py index 3953edd..77dcbac 100644 --- a/passkey/db/sql.py +++ b/passkey/db/sql.py @@ -462,8 +462,7 @@ class DB(DatabaseInterface): ) # Automatically create an organization admin permission if not present. - # Pattern: auth/org: - auto_perm_id = f"auth/org:{org.uuid}" + auto_perm_id = f"auth:org:{org.uuid}" # Only create if it does not already exist (in case caller passed it) existing_perm = await session.execute( select(PermissionModel).where(PermissionModel.id == auto_perm_id) diff --git a/passkey/fastapi/api.py b/passkey/fastapi/api.py index 231f558..52bcc37 100644 --- a/passkey/fastapi/api.py +++ b/passkey/fastapi/api.py @@ -10,7 +10,7 @@ This module contains all the HTTP API endpoints for: from uuid import UUID, uuid4 -from fastapi import Body, Cookie, Depends, FastAPI, HTTPException, Response +from fastapi import Body, Cookie, Depends, FastAPI, HTTPException, Query, Response from fastapi.security import HTTPBearer from passkey.util import passphrase @@ -38,14 +38,12 @@ def register_api_routes(app: FastAPI): raise ValueError("Not authenticated") role_perm_ids = set(ctx.role.permissions or []) org_uuid_str = str(ctx.org.uuid) - is_global_admin = "auth/admin" in role_perm_ids - is_org_admin = f"auth/org:{org_uuid_str}" in role_perm_ids + is_global_admin = "auth:admin" in role_perm_ids + is_org_admin = f"auth:org:{org_uuid_str}" in role_perm_ids return ctx, is_global_admin, is_org_admin @app.post("/auth/validate") - async def validate_token( - response: Response, perm: list[str] | None = None, auth=Cookie(None) - ): + async def validate_token(perm=Query(None), auth=Cookie(None)): """Lightweight token validation endpoint. Query Params: @@ -137,9 +135,9 @@ def register_api_routes(app: FastAPI): "permissions": ctx.org.permissions, } effective_permissions = [p.id for p in (ctx.permissions or [])] - is_global_admin = "auth/admin" in role_info["permissions"] + is_global_admin = "auth:admin" in role_info["permissions"] is_org_admin = ( - f"auth/org:{org_info['uuid']}" in role_info["permissions"] + f"auth:org:{org_info['uuid']}" in role_info["permissions"] if org_info else False ) diff --git a/passkey/fastapi/authz.py b/passkey/fastapi/authz.py index be6701b..4428728 100644 --- a/passkey/fastapi/authz.py +++ b/passkey/fastapi/authz.py @@ -12,7 +12,7 @@ from ..globals import db from ..util.tokens import session_key -async def verify(auth: str | None, perms: list[str] | None): +async def verify(auth: str | None, perm: list[str] | str | None): """Validate session token and optional list of required permissions. Returns the Session object on success. Raises HTTPException on failure. @@ -22,14 +22,16 @@ async def verify(auth: str | None, perms: list[str] | None): if not auth: raise HTTPException(status_code=401, detail="Authentication required") session = await get_session(auth) - if perms: + if perm is not None: + if isinstance(perm, str): + perm = [perm] ctx = await db.instance.get_session_context(session_key(auth)) if not ctx: raise HTTPException(status_code=401, detail="Session not found") available = set(ctx.role.permissions or []) | ( set(ctx.org.permissions or []) if ctx.org else set() ) - if any(p not in available for p in perms): + if any(p not in available for p in perm): raise HTTPException(status_code=403, detail="Permission required") return session diff --git a/passkey/fastapi/mainapp.py b/passkey/fastapi/mainapp.py index c6aa08f..93b743b 100644 --- a/passkey/fastapi/mainapp.py +++ b/passkey/fastapi/mainapp.py @@ -4,7 +4,7 @@ import os from contextlib import asynccontextmanager from pathlib import Path -from fastapi import Cookie, FastAPI, HTTPException, Request, Response +from fastapi import Cookie, FastAPI, HTTPException, Query, Request, Response from fastapi.responses import FileResponse, JSONResponse from fastapi.staticfiles import StaticFiles @@ -72,9 +72,7 @@ app.mount("/auth/ws", ws.app) @app.get("/auth/forward-auth") -async def forward_authentication( - request: Request, perm: list[str] | None = None, auth=Cookie(None) -): +async def forward_authentication(request: Request, perm=Query(None), auth=Cookie(None)): """A validation endpoint to use with Caddy forward_auth or Nginx auth_request. Query Params: diff --git a/passkey/fastapi/session.py b/passkey/fastapi/session.py index 3c96917..78948a7 100644 --- a/passkey/fastapi/session.py +++ b/passkey/fastapi/session.py @@ -30,5 +30,4 @@ def set_session_cookie(response: Response, token: str) -> None: max_age=int(EXPIRES.total_seconds()), httponly=True, secure=True, - path="/auth/", )