diff --git a/backend/app/api/api_v1/endpoints/auth.py b/backend/app/api/api_v1/endpoints/auth.py index 91a1c0c..802a342 100644 --- a/backend/app/api/api_v1/endpoints/auth.py +++ b/backend/app/api/api_v1/endpoints/auth.py @@ -7,6 +7,8 @@ GET /sign-in – Initiate the Logto sign-in flow. GET /callback – Handle the Logto authorization-code callback. GET /sign-out – Sign the user out (clears session + redirects to Logto). GET /me – Return the currently authenticated user's profile. +GET /forgot-password – Redirect to Logto's forgot-password screen. +GET /account-portal – Redirect to the Logto account portal (MFA management). """ from __future__ import annotations @@ -197,6 +199,53 @@ async def sign_out(request: Request) -> RedirectResponse: return response +@router.get("/forgot-password") +async def forgot_password(request: Request) -> RedirectResponse: + """ + Redirect the user to Logto's forgot-password screen. + + Builds a standard Logto authorization URL and appends the + ``first_screen=forgot_password`` parameter so that Logto shows the + password-reset form immediately instead of the normal sign-in form. + After the user resets their password they are returned via the normal + callback flow and land on the app dashboard. + """ + if not settings.logto_configured: + raise _logto_not_configured() + + storage = CookieStorage(request) + client = make_logto_client(storage) + + sign_in_url: str = await client.signIn(redirectUri=_get_redirect_uri(request)) + + # Append the Logto-specific first_screen parameter so the password-reset + # form is shown directly. The sign-in URL normally already contains a "?" + # but we defensively detect the right separator in case the structure varies. + separator = "&" if "?" in sign_in_url else "?" + forgot_url = f"{sign_in_url}{separator}first_screen=forgot_password" + + response = RedirectResponse(url=forgot_url, status_code=302) + storage.apply_to_response(response) + return response + + +@router.get("/account-portal") +async def account_portal(request: Request) -> RedirectResponse: + """ + Redirect an authenticated user to the Logto account portal. + + The Logto account portal (``{LOGTO_ENDPOINT}/account``) lets users manage + their profile, linked identities, and multi-factor authentication settings + without leaving the Logto-hosted UI. After updating their settings, users + can simply navigate back to the app. + """ + if not settings.logto_configured: + raise _logto_not_configured() + + portal_url = f"{settings.LOGTO_ENDPOINT.rstrip('/')}/account" + return RedirectResponse(url=portal_url, status_code=302) + + @router.get("/me", response_model=None) async def get_current_user( request: Request, diff --git a/backend/app/main.py b/backend/app/main.py index 5ff61c1..ac3f06a 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -461,6 +461,19 @@ async def settings_page(request: Request): return templates.TemplateResponse(request, "settings.html") +@app.get("/profile", response_class=HTMLResponse) +async def profile_page(request: Request): + return templates.TemplateResponse( + request, + "profile.html", + { + "app_name": settings.PROJECT_NAME, + "logto_configured": settings.logto_configured, + "auth_disabled": settings.AUTH_DISABLED, + }, + ) + + @app.get("/mail-sources", response_class=HTMLResponse) async def mail_sources_page(request: Request): return templates.TemplateResponse(request, "mail_sources.html") diff --git a/backend/app/templates/layouts/base.html b/backend/app/templates/layouts/base.html index 7fdb424..4928bab 100644 --- a/backend/app/templates/layouts/base.html +++ b/backend/app/templates/layouts/base.html @@ -61,6 +61,7 @@ +
Logto securely handles authentication. Your credentials are never sent to {{ app_name }}. diff --git a/backend/app/templates/profile.html b/backend/app/templates/profile.html new file mode 100644 index 0000000..63e218b --- /dev/null +++ b/backend/app/templates/profile.html @@ -0,0 +1,182 @@ +{% extends "layouts/base.html" %} +{% from "components/ui/card.html" import card, card_header, card_title, card_description, card_content %} +{% from "components/ui/button.html" import button_link %} + +{% block title %}My Profile – {{ app_name }}{% endblock %} + +{% block content %} +
+ + Admin + + + User + +
++ + Auth disabled + + + Logto OIDC + +
+Password
++ Reset your Logto account password via email verification. +
+Multi-Factor Authentication
++ Enable, configure, or remove MFA methods such as TOTP + authenticator apps and backup codes in your Logto account portal. +
+Authentication is disabled
+
+ Password reset and MFA management require Logto to be configured.
+ Set AUTH_DISABLED=false
+ and configure Logto to enable these features.
+
Logto not configured
++ Password reset and MFA management require Logto. Visit the + setup page + to configure it. +
+