security: fix passkey admin privilege escalation (issue #1)
CRITICAL: Passkey authentication was hardcoded to always grant admin role, completely bypassing RBAC and allowing any passkey user to gain full admin access. Changes: - Store username and role in passkey credential during registration - Retrieve and use stored role during authentication (not hardcoded admin) - Fallback to admin for legacy credentials without role field Security Impact: - Prevents privilege escalation via passkey authentication - Enforces proper RBAC for passkey users - Maintains backward compatibility with existing passkeys Tests: 214 passing
This commit is contained in:
@@ -95,6 +95,8 @@ async def passkey_register_verify(request: Request, current_user=Depends(auth.ge
|
|||||||
"sign_count": verification.sign_count,
|
"sign_count": verification.sign_count,
|
||||||
"name": body.get("name", "Passkey"),
|
"name": body.get("name", "Passkey"),
|
||||||
"created_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
"created_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||||
|
"username": current_user.username,
|
||||||
|
"role": current_user.role,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
@@ -149,19 +151,22 @@ async def passkey_login_verify(request: Request, response: Response):
|
|||||||
data["passkey_credentials"] = creds
|
data["passkey_credentials"] = creds
|
||||||
auth._save_auth_data(data)
|
auth._save_auth_data(data)
|
||||||
|
|
||||||
username = config.ADMIN_USER
|
# Use stored username and role from passkey credential
|
||||||
|
username = matched.get("username", config.ADMIN_USER)
|
||||||
|
role = matched.get("role", "admin")
|
||||||
|
|
||||||
access_token = auth.create_access_token(
|
access_token = auth.create_access_token(
|
||||||
data={"sub": username, "role": "admin"},
|
data={"sub": username, "role": role},
|
||||||
expires_delta=timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES),
|
expires_delta=timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES),
|
||||||
)
|
)
|
||||||
refresh_token = auth.create_refresh_token(data={"sub": username, "role": "admin"})
|
refresh_token = auth.create_refresh_token(data={"sub": username, "role": role})
|
||||||
auth.set_auth_cookies(response, access_token, refresh_token)
|
auth.set_auth_cookies(response, access_token, refresh_token)
|
||||||
return {
|
return {
|
||||||
"access_token": access_token,
|
"access_token": access_token,
|
||||||
"refresh_token": refresh_token,
|
"refresh_token": refresh_token,
|
||||||
"token_type": "bearer",
|
"token_type": "bearer",
|
||||||
"user": username,
|
"user": username,
|
||||||
"role": "admin",
|
"role": role,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user