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

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.""" 

8 

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 

21 

22view_login = "auth.login" 

23 

24 

25@bp.route("/oauth2/oidc/callback") 

26def authorize() -> Response: 

27 """ 

28 Process OAuth authorization callback. 

29 

30 Exchanges authorization code for tokens, retrieves user information, 

31 synchronizes or creates the local user entity, and logs the user in. 

32 

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)) 

43 

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)) 

53 

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 

71 

72 # Connect user with Flask-Login 

73 login_user(user) 

74 toast(_("Logged in successfully."), _("Login"), "success") 

75 

76 # Update last login timestamp 

77 user.update_last_login() 

78 

79 return redirect(url_for("frontend.homepage")) 

80 

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))