Implement oidc login
This commit is contained in:
@ -13,7 +13,7 @@ from sshecret_admin.services import AdminBackend
|
||||
from starlette.datastructures import URL
|
||||
|
||||
from sshecret_admin.auth import (
|
||||
User,
|
||||
IdentityClaims,
|
||||
authenticate_user_async,
|
||||
create_access_token,
|
||||
create_refresh_token,
|
||||
@ -34,7 +34,16 @@ class LoginError(BaseModel):
|
||||
message: str
|
||||
|
||||
|
||||
async def audit_login_failure(admin: AdminBackend, username: str, request: Request) -> None:
|
||||
class OidcLogin(BaseModel):
|
||||
"""Small container to hold OIDC info for the login box."""
|
||||
|
||||
enabled: bool = False
|
||||
provider_name: str | None = None
|
||||
|
||||
|
||||
async def audit_login_failure(
|
||||
admin: AdminBackend, username: str, request: Request
|
||||
) -> None:
|
||||
"""Write login failure to audit log."""
|
||||
origin: str | None = None
|
||||
if request.client:
|
||||
@ -65,7 +74,16 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
||||
return RedirectResponse("/dashboard")
|
||||
login_error: LoginError | None = None
|
||||
if error_title and error_message:
|
||||
LOG.info("Got an error here: %s %s", error_title, error_message)
|
||||
login_error = LoginError(title=error_title, message=error_message)
|
||||
else:
|
||||
LOG.info("Got no errors")
|
||||
|
||||
oidc_login = OidcLogin()
|
||||
if dependencies.settings.oidc:
|
||||
oidc_login.enabled = True
|
||||
oidc_login.provider_name = dependencies.settings.oidc.name
|
||||
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"login.html",
|
||||
@ -73,6 +91,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
||||
"page_title": "Login",
|
||||
"page_description": "Login page.",
|
||||
"login_error": login_error,
|
||||
"oidc": oidc_login,
|
||||
},
|
||||
)
|
||||
|
||||
@ -100,7 +119,9 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
||||
},
|
||||
)
|
||||
|
||||
user = await authenticate_user_async(session, form_data.username, form_data.password)
|
||||
user = await authenticate_user_async(
|
||||
session, form_data.username, form_data.password
|
||||
)
|
||||
login_failed = RedirectException(
|
||||
to=URL("/login").include_query_params(
|
||||
error_title="Login Error", error_message="Invalid username or password"
|
||||
@ -143,16 +164,22 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
||||
@app.get("/refresh")
|
||||
async def get_refresh_token(
|
||||
response: Response,
|
||||
user: Annotated[User, Depends(dependencies.get_user_from_refresh_token)],
|
||||
refresh_claims: Annotated[
|
||||
IdentityClaims, Depends(dependencies.get_refresh_claims)
|
||||
],
|
||||
next: Annotated[str, Query()],
|
||||
):
|
||||
"""Refresh tokens.
|
||||
|
||||
We might as well refresh the long-lived one here.
|
||||
"""
|
||||
token_data: dict[str, str] = {"sub": user.username}
|
||||
access_token = create_access_token(dependencies.settings, data=token_data)
|
||||
refresh_token = create_refresh_token(dependencies.settings, data=token_data)
|
||||
token_data: dict[str, str] = {"sub": refresh_claims.sub}
|
||||
access_token = create_access_token(
|
||||
dependencies.settings, data=token_data, provider=refresh_claims.provider
|
||||
)
|
||||
refresh_token = create_refresh_token(
|
||||
dependencies.settings, data=token_data, provider=refresh_claims.provider
|
||||
)
|
||||
response = RedirectResponse(url=next, status_code=status.HTTP_302_FOUND)
|
||||
response.set_cookie(
|
||||
"access_token",
|
||||
@ -176,8 +203,12 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
||||
):
|
||||
"""Log out user."""
|
||||
response = RedirectResponse(url="/login", status_code=status.HTTP_302_FOUND)
|
||||
response.delete_cookie("refresh_token", httponly=True, secure=False, samesite="strict")
|
||||
response.delete_cookie("access_token", httponly=True, secure=False, samesite="strict")
|
||||
response.delete_cookie(
|
||||
"refresh_token", httponly=True, secure=False, samesite="strict"
|
||||
)
|
||||
response.delete_cookie(
|
||||
"access_token", httponly=True, secure=False, samesite="strict"
|
||||
)
|
||||
return response
|
||||
|
||||
return app
|
||||
|
||||
Reference in New Issue
Block a user