(20241203) Cleaning-up the callback-handling blueprint.

This commit is contained in:
2024-12-03 08:54:36 +05:30
parent a7d33260b3
commit bc5ede8f0a
+96 -77
View File
@@ -40,7 +40,7 @@ sys.path.append(".")
sys.path.append("..") sys.path.append("..")
# For using Quart: # For using Quart:
from quart import Blueprint, current_app, request, render_template from quart import Blueprint, current_app, g, request, render_template
# My utils: # My utils:
from utils_v2.string import json from utils_v2.string import json
@@ -108,6 +108,81 @@ def init(blueprint_setup_state):
# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
async def handle_gmail_callback():
"""
To handle the callbacks from GMail specifically. Refer to the individual comments to check what hap[pens at each
step of the process.
:return: A rendered template of the final status of the authorization.
"""
# Start by assuming failure:
tokens_saved = False
# In case the user cancelled halfway through (on Google's screen):
if g.inbound_data.get("error") == "access_denied": return await render_template(
"/mail/oauth/oauth_cancelled_v2.html",
mail_client = g.mail_client.title()
)
# Generate the tokens from the callback. Google sends all the needed params in the callback as the URL's query
# params. We can simply use the exact URL that was hit to generate the tokens. In Quart (and Flask) this can be
# achieved by 'request.url' like this:
tokens = await current_app.gmail_client.get_authorization_tokens(
redirect_url = request.url,
scopes = g.inbound_data["scope"]
)
if tokens:
# Get the e-mail id that granted authorization. We will be comparing this to the e-mail id that had been given
# to us when the authorization was initiated. We don't mind any e-mail id being used, but we need them to be the
# same at both ends:
user_profile = await current_app.gmail_client.get_user_profile(tokens = tokens)
tokens.email = user_profile.data["emailAddress"] if user_profile.success else None
# Here's where we do the checking of the e-mails,
# if they don't match, we reject the authorization:
placeholder_token = await current_app.mail_oauth_model.get_token(
mongo_conn = current_app.data_mongo,
account_identifier = g.inbound_data["state"]
)
if (
(not placeholder_token) or
placeholder_token["clientUserId"] != str(tokens.email)
): return await render_template(
"/mail/oauth/oauth_failure_v2.html",
mail_client = g.mail_client.title(),
failure_hint = f"We were expecting authorization from '{placeholder_token['clientUserId']['email']}' but got authorization from '{tokens.email}' instead."
)
# Now that we have passed the check,
# we save the tokens to the database:
tokens_saved = await current_app.mail_oauth_model.set_token(
db_conn = current_app.sql_writer,
mongo_conn = current_app.data_mongo,
session_token = g.inbound_headers.get("X-Session-Token"),
account_identifier = g.inbound_data["state"],
token = tokens.model_dump()
)
# Return an HTML response for success:
if tokens_saved: return await render_template(
"/mail/oauth/oauth_success_v2.html",
mail_client = g.mail_client.title()
)
# Return an HTML response for failure:
else: return await render_template(
"/mail/oauth/oauth_failure_v2.html",
mail_client = g.mail_client.title(),
failure_hint = f"Unknown error. Please use log-id '{g.log_id}' to check with the support team."
)
# ---------------------------------------------------------------------------------------------------------------------
@mail_callback_bp.route("/callback/<mail_client>", methods = ["POST", "GET"]) @mail_callback_bp.route("/callback/<mail_client>", methods = ["POST", "GET"])
@set_api_version(api_version = "1.0.0") @set_api_version(api_version = "1.0.0")
@read_input(sanitize_headers = False, sanitize_data = False) @read_input(sanitize_headers = False, sanitize_data = False)
@@ -141,87 +216,31 @@ async def mail_callback(
:return: A standard response structure. :return: A standard response structure.
""" """
# Start by assuming failure: # ┓┏ ┓┓ ┓┏ • ┓ ┓
tokens_saved = False # ┣┫┏┓┏┓┏┫┃┏┓ ┃┃┏┓┏┓┓┏┓┣┓┃┏┓┏
# ┛┗┗┻┛┗┗┻┗┗ ┗┛┗┻┛ ┗┗┻┗┛┗┗ ┛
# ┏┓ ┏┓┳┳┓ •┓ # Here we make various variables available in the scope of the current request through 'g':
# ┣ ┏┓┏┓ ┃┓┃┃┃┏┓┓┃ g.log_id = kwargs.get("log_id")
# ┻ ┗┛┛ ┗┛┛ ┗┗┻┗┗ g.inbound_headers = inbound_headers
g.inbound_data = inbound_data
g.mail_client = mail_client
if mail_client == "gmail": # ┓┏ ┓┓ ┏┓ ┓┓┓ ┓
# ┣┫┏┓┏┓┏┫┃┏┓ ┃ ┏┓┃┃┣┓┏┓┏┃┏┏
# ┛┗┗┻┛┗┗┻┗┗ ┗┛┗┻┗┗┗┛┗┻┗┛┗┛
# In case the user cancelled halfway through (on Google's screen): if mail_client == "gmail": return await handle_gmail_callback()
if inbound_data.get("error") == "access_denied": return await render_template(
"/mail/oauth/oauth_cancelled_v2.html",
mail_client = mail_client.title()
)
# Generate the tokens from the callback: # ┓┏ ┓┓ ┳ ┓• ┓ ┏┓┓•
tokens = await current_app.gmail_client.get_authorization_tokens( # ┣┫┏┓┏┓┏┫┃┏┓ ┃┏┓┓┏┏┓┃┓┏┫ ┃ ┃┓┏┓┏┓╋
redirect_url = request.url, # ┛┗┗┻┛┗┗┻┗┗ ┻┛┗┗┛┗┻┗┗┗┻ ┗┛┗┗┗ ┛┗┗
scopes = inbound_data["scope"]
)
if tokens: return await render_template(
"/mail/oauth/oauth_failure_v2.html",
# Get the e-mail id that granted authorization: mail_client = mail_client.title(),
user_profile = await current_app.gmail_client.get_user_profile(tokens = tokens) failure_hint = f"Invalid client '{mail_client}' selected. Please use log-id '{g.log_id}' to check with the support team."
tokens.email = user_profile.data["emailAddress"] if user_profile.success else None )
# The e-mail id that we requested access to and the one that granted us access should be the same:
placeholder_token = await current_app.mail_oauth_model.get_token(
mongo_conn = current_app.data_mongo,
account_identifier = inbound_data["state"]
)
if (
(not placeholder_token) or
placeholder_token["clientUserId"] != str(tokens.email)
): return await render_template(
"/mail/oauth/oauth_failure_v2.html",
mail_client = mail_client.title(),
failure_hint = f"We were expecting authorization from '{placeholder_token['clientUserId']}' but got authorization from '{tokens.email}' instead."
)
# Save the tokens to the database
tokens_saved = await current_app.mail_oauth_model.set_token(
db_conn = current_app.sql_writer,
mongo_conn = current_app.data_mongo,
session_token = inbound_headers.get("X-Session-Token"),
account_identifier = inbound_data["state"],
token = tokens.model_dump()
)
# tokens_saved = False
# ┳┓
# ┣┫┏┓┏┏┓┏┓┏┓┏┏┓
# ┛┗┗ ┛┣┛┗┛┛┗┛┗
# ┛
# # Return a JSON response:
# return ResponseModel(
# status_code = StatusCodes.OK if tokens_saved else StatusCodes.FAILED,
# http_code = HttpCodes.SUCCESS if tokens_saved else HttpCodes.INTERNAL_SERVER_ERROR,
# data = {
# "mailClient": mail_client,
# "authorized": True
# }
# )
# Return an HTML response for success:
if tokens_saved:
return await render_template(
"/mail/oauth/oauth_success_v2.html",
mail_client = mail_client.title()
)
# Return an HTML response for failure:
else:
return await render_template(
"/mail/oauth/oauth_failure_v2.html",
mail_client = mail_client.title(),
failure_hint = f"Unknown error. Please use log-id '{kwargs['logId']}' to check with the support team."
)
# ***************************************************************************************************************** # *****************************************************************************************************************