Coverage for seedboxsync/front/views/auth/authorize.py: 90%
48 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-26 17:14 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-26 17:14 +0000
1#
2# Copyright (C) 2015-2026 Guillaume Kulakowski <guillaume@kulakowski.fr>
3#
4# For the full copyright and license information, please view the LICENSE
5# file that was distributed with this source code.
6#
7"""SeedboxSync Flask view for authentication handling."""
9import secrets
10from flask import abort, flash, redirect, url_for
11from flask_login import login_user
12from werkzeug.exceptions import HTTPException
13from werkzeug.security import generate_password_hash
14from werkzeug.wrappers.response import Response
15from seedboxsync.core import current_app
16from seedboxsync.core.database.models.user import User
17from seedboxsync.front.babel import gettext as _
18from seedboxsync.front.oauth2 import oauth
19from seedboxsync.front.utils import toast
20from seedboxsync.front.views import bp_auth as bp
22view_login = "auth.login"
25@bp.route("/oauth2/oidc/callback")
26def authorize() -> Response:
27 """
28 Process OAuth authorization callback.
30 Exchanges authorization code for tokens, retrieves user information,
31 synchronizes or creates the local user entity, and logs the user in.
33 Returns:
34 Response: HTTP redirect to homepage or error destination.
35 """
36 try:
37 # Get client name from configuration and create OAuth client
38 oauth_name = current_app.seedboxsync_config.get("oauth_name")
39 client = oauth.create_client(oauth_name)
40 if client is None:
41 flash(_("OAuth provider is not properly configured."), "danger")
42 return redirect(url_for(view_login))
44 # Get token and user info from OAuth provider
45 token = client.authorize_access_token()
46 user_info = client.userinfo(token=token)
47 current_app.logger.debug("OAuth user info retrieved: %s", user_info)
48 email = user_info.get("email")
49 username = user_info.get("preferred_username") or user_info.get("name") or email
50 if not email:
51 flash(_("Failed to obtain email from OAuth provider."), "danger")
52 return redirect(url_for(view_login))
54 # Check if user exists or create a new one based on configuration
55 if current_app.seedboxsync_config.get("oauth_auto_create_user_enabled"):
56 random_password = secrets.token_urlsafe(32)
57 user, _created = User.get_or_create(
58 email=email,
59 defaults={
60 "username": username,
61 "origin": User.ORIGIN_OIDC,
62 "password": generate_password_hash(random_password),
63 },
64 ) # type: ignore[no-untyped-call]
65 else:
66 try:
67 user = User.get(User.email == email)
68 except User.DoesNotExist: # pyright: ignore [reportAttributeAccessIssue]
69 abort(403, description=_("No user account exists for email '%(email)s'.") % {"email": email})
70 raise
72 # Connect user with Flask-Login
73 login_user(user)
74 toast(_("Logged in successfully."), _("Login"), "success")
76 # Update last login timestamp
77 user.update_last_login()
79 return redirect(url_for("frontend.homepage"))
81 except HTTPException as e:
82 current_app.logger.error("Authentication failed: %s", str(e))
83 raise
84 except Exception as e:
85 current_app.logger.error("Authentication failed: %s", str(e))
86 toast(_("Authentication failed. Please try again."), _("Login"), "danger")
87 return redirect(url_for(view_login))