Coverage for heritrace / routes / auth.py: 100%
61 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-21 12:56 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-21 12:56 +0000
1# SPDX-FileCopyrightText: 2024-2025 Arcangelo Massari <arcangelo.massari@unibo.it>
2#
3# SPDX-License-Identifier: ISC
5import os
6from datetime import timedelta
8from flask import (Blueprint, current_app, flash, redirect, request, session,
9 url_for)
10from flask_babel import gettext
11from flask_login import current_user, login_user, logout_user
12from heritrace.apis.orcid import extract_orcid_id, is_orcid_url
13from heritrace.models import User
14from requests_oauthlib import OAuth2Session
16auth_bp = Blueprint("auth", __name__)
19@auth_bp.route("/login")
20def login():
21 if current_user.is_authenticated:
22 return redirect(url_for("main.catalogue"))
24 if os.environ.get("FLASK_ENV") == "demo":
25 user_id_from_env = os.environ.get("USER_ID", "demo_user")
26 demo_uri = f"http://example.org/demo/{user_id_from_env}"
27 user_name = f"Demo User ({user_id_from_env})"
29 user = User(id=demo_uri, name=user_name, orcid=demo_uri)
30 session["user_name"] = user_name
31 session.permanent = True
32 current_app.permanent_session_lifetime = timedelta(days=30)
33 login_user(user)
35 flash(gettext("Welcome! You've been automatically logged in to the demo"), "info")
36 return redirect(url_for("main.catalogue"))
38 callback_url = url_for("auth.callback", _external=True, _scheme="https")
39 orcid = OAuth2Session(
40 current_app.config["ORCID_CLIENT_ID"],
41 redirect_uri=callback_url,
42 scope=[current_app.config["ORCID_SCOPE"], "openid"],
43 )
44 authorization_url, state = orcid.authorization_url(
45 current_app.config["ORCID_AUTHORIZE_URL"],
46 prompt="login", # Forza il re-login
47 nonce=os.urandom(16).hex(), # Aggiungiamo un nonce per sicurezza
48 )
49 session["oauth_state"] = state
50 return redirect(authorization_url)
53@auth_bp.route("/callback")
54def callback():
55 if request.url.startswith("http://"):
56 secure_url = request.url.replace("http://", "https://", 1)
57 else:
58 secure_url = request.url
60 orcid = OAuth2Session(
61 current_app.config["ORCID_CLIENT_ID"], state=session["oauth_state"]
62 )
63 try:
64 token = orcid.fetch_token(
65 current_app.config["ORCID_TOKEN_URL"],
66 client_secret=current_app.config["ORCID_CLIENT_SECRET"],
67 authorization_response=secure_url,
68 )
69 except Exception as e:
70 flash(
71 gettext("An error occurred during authentication. Please try again"),
72 "danger",
73 )
74 return redirect(url_for("auth.login"))
75 orcid_id = token["orcid"]
77 safelist = current_app.config["ORCID_SAFELIST"]
78 if safelist:
79 normalized_safelist = {
80 extract_orcid_id(item) if is_orcid_url(item) else item for item in safelist
81 }
82 if orcid_id not in normalized_safelist:
83 flash(
84 gettext("Your ORCID is not authorized to access this application"),
85 "danger",
86 )
87 return redirect(url_for("auth.login"))
89 session["user_name"] = token["name"]
90 user = User(id=orcid_id, name=token["name"], orcid=orcid_id)
91 session.permanent = True
92 current_app.permanent_session_lifetime = timedelta(days=30)
93 login_user(user)
94 flash(gettext("Welcome back %(name)s!", name=current_user.name), "success")
95 return redirect(url_for("main.catalogue"))
98@auth_bp.route("/logout")
99def logout():
100 if not current_user.is_authenticated:
101 return "", 401
102 logout_user()
103 flash(gettext("You have been logged out"), "info")
104 return redirect(url_for("main.index"))