(20241125) Testing OAuth2.0 with Google.
This commit is contained in:
@@ -0,0 +1,184 @@
|
|||||||
|
"""
|
||||||
|
|
||||||
|
AUTHOR:
|
||||||
|
|
||||||
|
Khushal P Soonderji
|
||||||
|
|
||||||
|
DATE:
|
||||||
|
|
||||||
|
Thursday, 21st Nov., 2024
|
||||||
|
|
||||||
|
OBJECTIVE:
|
||||||
|
|
||||||
|
To receive callbacks (webhooks).
|
||||||
|
|
||||||
|
REFERENCES:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
DOWNLOADS:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
NOTES:
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** IMPORT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# To make sibling directories accessible for imports:
|
||||||
|
import sys
|
||||||
|
sys.path.append(".")
|
||||||
|
sys.path.append("..")
|
||||||
|
|
||||||
|
# For using Quart:
|
||||||
|
from quart import Blueprint, current_app, request
|
||||||
|
|
||||||
|
# My utils:
|
||||||
|
from utils_v2.string import json
|
||||||
|
from utils_v2.api.codes import StatusCodes, HttpCodes
|
||||||
|
from utils_v2.api.response import ResponseModel
|
||||||
|
from utils_v2.api.async_quart import (
|
||||||
|
set_api_version,
|
||||||
|
read_input,
|
||||||
|
get_session_info,
|
||||||
|
log_request_to_mongo,
|
||||||
|
log_chain_to_mongo,
|
||||||
|
should_not_be_under_maintenance,
|
||||||
|
only_whitelisted_ips,
|
||||||
|
limit_rate,
|
||||||
|
validate_input,
|
||||||
|
handle_cancelled_request
|
||||||
|
)
|
||||||
|
|
||||||
|
# Common:
|
||||||
|
from shared import constants
|
||||||
|
|
||||||
|
# For asynchronous activities:
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MACROS / ONE-TIME INIT ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# Related to Quart:
|
||||||
|
test_callback_bp = Blueprint("user_cb", __name__)
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** VARIABLES ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# --- Nothing Yet
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** FUNCTIONS ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
@test_callback_bp.record_once
|
||||||
|
def init(blueprint_setup_state):
|
||||||
|
|
||||||
|
# This gets called when the blueprint is registered.
|
||||||
|
# Consider this to be a one-time setup for the whole blueprint:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@test_callback_bp.route("/callback", methods = ["POST", "GET"])
|
||||||
|
@set_api_version(api_version = "1.0.0")
|
||||||
|
@read_input(sanitize_headers = False, sanitize_data = False)
|
||||||
|
@log_request_to_mongo(
|
||||||
|
attr_name = "logs_mongo",
|
||||||
|
project = constants.PROJECT_NAME,
|
||||||
|
log_type = constants.MODULE_NAME,
|
||||||
|
operation = "testCllBckApi",
|
||||||
|
log_input = True,
|
||||||
|
log_output = True,
|
||||||
|
sensitive_keys = None
|
||||||
|
)
|
||||||
|
@log_chain_to_mongo(attr_name = "logs_mongo")
|
||||||
|
@should_not_be_under_maintenance(attr_name = "is_under_maintenance")
|
||||||
|
@handle_cancelled_request()
|
||||||
|
async def callback_test(
|
||||||
|
inbound_headers: dict = None,
|
||||||
|
inbound_data: dict = None,
|
||||||
|
inbound_files: dict = None,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
|
|
||||||
|
"""
|
||||||
|
This URL does nothing, just captures data on webhooks and logs it for documentation.
|
||||||
|
:param inbound_headers: auto-extracted by the decorators.
|
||||||
|
:param inbound_data: auto-extracted by the decorators.
|
||||||
|
:param inbound_files: auto-extracted by the decorators.
|
||||||
|
:param kwargs: Any number of extra inputs supplied by the decorators.
|
||||||
|
:return: A standard response structure.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Construct a message:
|
||||||
|
message = "🪝 *WEBHOOK/CALLBACK ALERT!* 🪝\n\n"
|
||||||
|
message += f"Method: *{request.method}*\nLog Id.: `{kwargs.get('log_id')}`\n\n"
|
||||||
|
message += "*Headers:*\n```json\n"
|
||||||
|
message += json.to_string({k: v for k, v in request.headers.items()})
|
||||||
|
message += "\n```\n"
|
||||||
|
message += "*Query Args:*\n```json\n"
|
||||||
|
message += json.to_string(request.args.to_dict())
|
||||||
|
message += "\n```\n"
|
||||||
|
message += "*JSON:*\n```json\n"
|
||||||
|
message += json.to_string(await request.get_json())
|
||||||
|
message += "\n```\n"
|
||||||
|
message += "*Form-Data:*\n```json\n"
|
||||||
|
message += json.to_string((await request.form).to_dict())
|
||||||
|
message += "\n```\n"
|
||||||
|
message += "*Form-Files:*\n```json\n"
|
||||||
|
message += json.to_string(inbound_files, default = str)
|
||||||
|
message += "\n```\n"
|
||||||
|
|
||||||
|
# Send a message on Telegram:
|
||||||
|
api_response = await current_app.http_client.post(
|
||||||
|
url = current_app.script_data["alerts"]["url"],
|
||||||
|
json = {
|
||||||
|
"message": message,
|
||||||
|
"type": "info",
|
||||||
|
"chatId": "1275560043" # ... KPS
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return a success response:
|
||||||
|
return ResponseModel(
|
||||||
|
status_code = StatusCodes.OK,
|
||||||
|
data = {"accepted": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
# ***** ****
|
||||||
|
# *** MAIN PROGRAM ***
|
||||||
|
# ***** ****
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
pass
|
||||||
@@ -43,7 +43,7 @@ from functools import wraps
|
|||||||
|
|
||||||
# My utils:
|
# My utils:
|
||||||
from utils_v2.string import json
|
from utils_v2.string import json
|
||||||
from utils_v2.datetime import datetime
|
from utils_v2.date_time import date_time
|
||||||
from utils_v2.security import sanitizers
|
from utils_v2.security import sanitizers
|
||||||
from utils_v2.api.codes import StatusCodes, HttpCodes
|
from utils_v2.api.codes import StatusCodes, HttpCodes
|
||||||
from utils_v2.api.log import APILogModel
|
from utils_v2.api.log import APILogModel
|
||||||
@@ -79,6 +79,7 @@ import asyncio
|
|||||||
|
|
||||||
# For timekeeping:
|
# For timekeeping:
|
||||||
import time
|
import time
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
# *****************************************************************************************************************
|
# *****************************************************************************************************************
|
||||||
@@ -811,7 +812,7 @@ def log_request_to_mongo(
|
|||||||
# Make variables and extract available info.:
|
# Make variables and extract available info.:
|
||||||
exception = None
|
exception = None
|
||||||
response = None
|
response = None
|
||||||
request_ts = datetime.get_current_utc_date_time()
|
request_ts = date_time.get_current_utc_date_time()
|
||||||
start_ts = time.perf_counter()
|
start_ts = time.perf_counter()
|
||||||
cpu_start_ts = time.process_time()
|
cpu_start_ts = time.process_time()
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import os
|
|||||||
|
|
||||||
# My utils:
|
# My utils:
|
||||||
from utils_v2.string import json
|
from utils_v2.string import json
|
||||||
from utils_v2.datetime import datetime
|
from utils_v2.date_time import date_time
|
||||||
from utils_v2.security import sanitizers
|
from utils_v2.security import sanitizers
|
||||||
from utils_v2.api.codes import StatusCodes, HttpCodes
|
from utils_v2.api.codes import StatusCodes, HttpCodes
|
||||||
from utils_v2.api.log import APILogModel
|
from utils_v2.api.log import APILogModel
|
||||||
@@ -330,7 +330,7 @@ class AsyncLoggerContext:
|
|||||||
# Make variables and extract available info.:
|
# Make variables and extract available info.:
|
||||||
exception = None
|
exception = None
|
||||||
response = None
|
response = None
|
||||||
request_ts = datetime.get_current_utc_date_time()
|
request_ts = date_time.get_current_utc_date_time()
|
||||||
start_ts = time.perf_counter()
|
start_ts = time.perf_counter()
|
||||||
cpu_start_ts = time.process_time()
|
cpu_start_ts = time.process_time()
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ import io
|
|||||||
# For defining the class's structure:
|
# For defining the class's structure:
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
# For working with datatypes:
|
||||||
|
from typing import List
|
||||||
|
|
||||||
# For debugging:
|
# For debugging:
|
||||||
from icecream import IceCreamDebugger
|
from icecream import IceCreamDebugger
|
||||||
|
|
||||||
@@ -115,6 +118,41 @@ class OAuthBase(ABC):
|
|||||||
def debug_everything(self):
|
def debug_everything(self):
|
||||||
self._debug_only_errors = False
|
self._debug_only_errors = False
|
||||||
|
|
||||||
|
# ┏┓┓ ┳┳┓ ┓ ┓
|
||||||
|
# ┣┫┣┓┏╋┏┓┏┓┏╋ ┃┃┃┏┓╋┣┓┏┓┏┫┏
|
||||||
|
# ┛┗┗┛┛┗┛ ┗┻┗┗ ┛ ┗┗ ┗┛┗┗┛┗┻┛
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def initialize(
|
||||||
|
self,
|
||||||
|
scopes: List
|
||||||
|
) -> bool:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To initialize the service-specific OAuth2.0 class. For example, in Google's case, we need to initialize an app
|
||||||
|
flow that was created for an app through its Cloud Console panel.
|
||||||
|
:param scopes: The list of permissions being requested. The word 'scopes' has been borrowed from Google's OAuth
|
||||||
|
documentation (which was implemented first).
|
||||||
|
:return: True if the initialization succeeded, False if it failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def get_authorization_url(
|
||||||
|
self,
|
||||||
|
**kwargs
|
||||||
|
) -> str | None:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To create an authorization URL which will be then sent to the front-end for the user to click and grant/decline
|
||||||
|
various permissions.
|
||||||
|
:param kwargs: The identifiers of the user who wants to use your service (where your service needs access to
|
||||||
|
their second-party account).
|
||||||
|
:return: The authorization URL if successful, or None if failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# *****************************************************************************************************************
|
# *****************************************************************************************************************
|
||||||
# ***** ****
|
# ***** ****
|
||||||
|
|||||||
+207
-12
@@ -15,6 +15,8 @@
|
|||||||
REFERENCES:
|
REFERENCES:
|
||||||
|
|
||||||
1. https://developers.google.com/calendar/api/quickstart/python
|
1. https://developers.google.com/calendar/api/quickstart/python
|
||||||
|
2. https://developers.google.com/identity/protocols/oauth2/web-server#python
|
||||||
|
3. https://www.youtube.com/watch?v=vQQEaSnQ_bs&t=940s&pp=ygUVb2F1dGgyIHB5dGhvbiB5b3V0dWJl
|
||||||
|
|
||||||
DOWNLOADS:
|
DOWNLOADS:
|
||||||
|
|
||||||
@@ -40,6 +42,7 @@ import io
|
|||||||
|
|
||||||
# My utils:
|
# My utils:
|
||||||
from utils_v2.string import json
|
from utils_v2.string import json
|
||||||
|
from utils_v2.date_time import date_time
|
||||||
|
|
||||||
# The base model:
|
# The base model:
|
||||||
from utils_v2.oauth.base import OAuthBase
|
from utils_v2.oauth.base import OAuthBase
|
||||||
@@ -52,6 +55,12 @@ from google_auth_oauthlib.flow import InstalledAppFlow
|
|||||||
# For asynchronous activities:
|
# For asynchronous activities:
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
# To make deep-copies:
|
||||||
|
import copy
|
||||||
|
|
||||||
|
# To work with date and time:
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
# *****************************************************************************************************************
|
# *****************************************************************************************************************
|
||||||
# ***** ****
|
# ***** ****
|
||||||
@@ -100,28 +109,191 @@ class GoogleOAuth(OAuthBase):
|
|||||||
# Class variables:
|
# Class variables:
|
||||||
__flow = None
|
__flow = None
|
||||||
|
|
||||||
async def init(
|
# ┏┓┓ ┳┳┓ ┓ ┓
|
||||||
|
# ┣┫┣┓┏╋┏┓┏┓┏╋ ┃┃┃┏┓╋┣┓┏┓┏┫┏
|
||||||
|
# ┛┗┗┛┛┗┛ ┗┻┗┗ ┛ ┗┗ ┗┛┗┗┛┗┻┛
|
||||||
|
|
||||||
|
async def initialize(
|
||||||
self,
|
self,
|
||||||
scopes = None
|
scopes,
|
||||||
):
|
raise_exception = False
|
||||||
|
) -> bool:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Initialize the Google OAuth mechanism by creating an app-flow. This defines the app that you are trying to
|
||||||
|
deploy. You must create this app in Google's Cloud Platform's console.
|
||||||
|
:param scopes: The scopes (permissions) needed by this app.
|
||||||
|
:param raise_exception: If set to True, any exception that occurs will be propagated. If set to false, any
|
||||||
|
exception that occurs will be suppressed.
|
||||||
|
:return: True if the initialization succeeded, False if it failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Start by assuming success:
|
||||||
|
success = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
# Initialize your Google App:
|
# Initialize your Google App:
|
||||||
self._printer("Initializing flow.")
|
self._printer("Initializing flow.")
|
||||||
self.__flow = InstalledAppFlow.from_client_config(
|
self.__flow = InstalledAppFlow.from_client_config(
|
||||||
self._config,
|
self._config,
|
||||||
scopes = scopes or SCOPES_GMAIL_MAIL_MANAGEMENT,
|
scopes = scopes,
|
||||||
redirect_uri = self._redirect_url
|
redirect_uri = self._redirect_url
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_authorization_url(self):
|
# If something goes wrong:
|
||||||
|
except Exception as exception:
|
||||||
|
self._printer(exception)
|
||||||
|
if raise_exception: raise
|
||||||
|
success = False
|
||||||
|
|
||||||
authorization_url, state = self.__flow.authorization_url(
|
# Done here:
|
||||||
access_type = "offline",
|
return success
|
||||||
include_granted_scopes = "true"
|
|
||||||
|
async def get_authorization_url(
|
||||||
|
self,
|
||||||
|
raise_exception = False,
|
||||||
|
**kwargs,
|
||||||
|
) -> str | None:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To generate an authorization URL that can be sent to the front end. When the
|
||||||
|
:param raise_exception: If set to True, any exception that occurs will be propagated. If set to false, any
|
||||||
|
exception that occurs will be suppressed.
|
||||||
|
:param kwargs: Any no. of keyword args that you might want to give to this specific service.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Start by assuming failure:
|
||||||
|
authorization_url = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# Request a URL that will be sent to the user to request
|
||||||
|
# permissions to access their account:
|
||||||
|
authorization_url, _ = self.__flow.authorization_url(
|
||||||
|
access_type = kwargs.get("access_type", "offline"),
|
||||||
|
approval_prompt = kwargs.get("approval_prompt", "force"),
|
||||||
|
include_granted_scopes = kwargs.get("include_granted_scopes", "true"),
|
||||||
|
login_hint = kwargs.get("email"),
|
||||||
|
state = kwargs.get("user_id")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Done here:
|
||||||
return authorization_url
|
return authorization_url
|
||||||
|
|
||||||
|
# If something goes wrong:
|
||||||
|
except Exception as exception:
|
||||||
|
self._printer(exception)
|
||||||
|
if raise_exception: raise
|
||||||
|
authorization_url = None
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return authorization_url
|
||||||
|
|
||||||
|
async def get_tokens(
|
||||||
|
self,
|
||||||
|
raise_exception = False,
|
||||||
|
**kwargs
|
||||||
|
) -> Credentials | dict | None:
|
||||||
|
|
||||||
|
# Start by assuming failure:
|
||||||
|
tokens = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# Fetch the tokens:
|
||||||
|
credentials = self.__flow.fetch_token(authorization_response = kwargs["redirect_url"])
|
||||||
|
tokens = {
|
||||||
|
"access_token": credentials.get("access_token"),
|
||||||
|
"refresh_token": credentials.get("refresh_token"),
|
||||||
|
"expires_in": (ttl := credentials["expires_in"] - 60),
|
||||||
|
"expires_at": date_time.get_current_utc_date_time() + datetime.timedelta(seconds = ttl),
|
||||||
|
"scopes": credentials.get("scope"),
|
||||||
|
}
|
||||||
|
|
||||||
|
# If something goes wrong:
|
||||||
|
except Exception as exception:
|
||||||
|
self._printer(exception)
|
||||||
|
if raise_exception: raise
|
||||||
|
tokens = None
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
async def refresh_tokens(
|
||||||
|
self,
|
||||||
|
old_tokens: dict,
|
||||||
|
raise_exception = False
|
||||||
|
) -> dict | None:
|
||||||
|
|
||||||
|
# Start by assuming failure:
|
||||||
|
tokens = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# If the tokens haven't expired, just return the existing tokens back:
|
||||||
|
tokens_expired = True if date_time.get_current_utc_date_time() >= old_tokens["expires_at"] else False
|
||||||
|
if not tokens_expired: return old_tokens
|
||||||
|
|
||||||
|
# Construct the credentials and request a refresh:
|
||||||
|
credentials = await self.credentials_from_tokens(old_tokens)
|
||||||
|
if tokens_expired and credentials.refresh_token:
|
||||||
|
credentials.refresh(Request())
|
||||||
|
tokens = {
|
||||||
|
"access_token": credentials.token,
|
||||||
|
"refresh_token": credentials.refresh_token,
|
||||||
|
"expires_at": (exp_at := date_time.as_if_timezone(
|
||||||
|
credentials.expiry,
|
||||||
|
timezone = date_time.TIMEZONE_UTC
|
||||||
|
)),
|
||||||
|
"expires_in": (exp_at - date_time.get_current_utc_date_time()).total_seconds(),
|
||||||
|
"scopes": credentials.scopes,
|
||||||
|
}
|
||||||
|
|
||||||
|
# If something goes wrong:
|
||||||
|
except Exception as exception:
|
||||||
|
self._printer(exception)
|
||||||
|
if raise_exception: raise
|
||||||
|
tokens = None
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
# ┏┓ • ┏┓ •┏•
|
||||||
|
# ┗┓┏┓┏┓┓┏┓┏┏┓ ┗┓┏┓┏┓┏┓╋┓┏
|
||||||
|
# ┗┛┗ ┛ ┗┛┗┗┗ ┗┛┣┛┗ ┗┗┛┗┗
|
||||||
|
# ┛
|
||||||
|
|
||||||
|
async def credentials_from_tokens(
|
||||||
|
self,
|
||||||
|
tokens: dict,
|
||||||
|
raise_exception = False
|
||||||
|
) -> Credentials | None:
|
||||||
|
|
||||||
|
# Start by assuming failure:
|
||||||
|
credentials = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# Make a deep-copy and add some fields from the config:
|
||||||
|
tokens_copy = copy.deepcopy(tokens)
|
||||||
|
first_key = list(self._config.keys())[0]
|
||||||
|
tokens_copy["client_id"] = self._config.get(first_key, {}).get("client_id")
|
||||||
|
tokens_copy["client_secret"] = self._config.get(first_key, {}).get("client_secret")
|
||||||
|
|
||||||
|
# Create the credentials:
|
||||||
|
credentials = Credentials.from_authorized_user_info(info = tokens_copy)
|
||||||
|
|
||||||
|
# If something goes wrong:
|
||||||
|
except Exception as exception:
|
||||||
|
self._printer(exception)
|
||||||
|
if raise_exception: raise
|
||||||
|
credentials = None
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return credentials
|
||||||
|
|
||||||
|
|
||||||
# *****************************************************************************************************************
|
# *****************************************************************************************************************
|
||||||
# ***** ****
|
# ***** ****
|
||||||
@@ -132,21 +304,44 @@ class GoogleOAuth(OAuthBase):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
import dateparser
|
||||||
|
|
||||||
secrets_file = r"../../../creds/google_converse_test_oauth.json"
|
secrets_file = r"../../../creds/google_converse_test_oauth.json"
|
||||||
secrets_dict = json.from_file(secrets_file)
|
secrets_dict = json.from_file(secrets_file)
|
||||||
|
|
||||||
my_goog = GoogleOAuth(
|
my_goog = GoogleOAuth(
|
||||||
config = secrets_dict,
|
config = secrets_dict,
|
||||||
# redirect_url = r"https://v2.api.bicree.com/user/callback/test",
|
redirect_url = r"https://nexcom.ditscentre.in/converse/test/callback",
|
||||||
redirect_url = r"127.0.0.1",
|
|
||||||
debug = True,
|
debug = True,
|
||||||
debug_prefix = "OAuth (Goog) | "
|
debug_prefix = "OAuth (Goog) | "
|
||||||
)
|
)
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
|
|
||||||
await my_goog.init()
|
await my_goog.initialize(scopes = SCOPES_GMAIL_MAIL_MANAGEMENT)
|
||||||
print("AUTH URL:", await my_goog.get_authorization_url())
|
# await asyncio.sleep(1.0)
|
||||||
|
# print("AUTH URL 0:", await my_goog.get_authorization_url(
|
||||||
|
# user_id = "BHOPLI",
|
||||||
|
# # email = "pskhushal@gmail.com"
|
||||||
|
# ))
|
||||||
|
#
|
||||||
|
# redirect_url = input("Paste the redirect URL here: ")
|
||||||
|
# print("TOKENS:", json.to_string(await my_goog.get_tokens(redirect_url = redirect_url), default = str))
|
||||||
|
|
||||||
|
old_tok = {
|
||||||
|
"access_token": "ya29.a0AeDClZAjmLUZ1hh0aTbddz4ThjRzwQNMwdi_H4AYO-C4ETvWpK8aHYz5eV9PTUlFDJKQoHYtFgu4u2XfoiOdIEMVNwAKFbb8sakIWef7Yk5HdiDYC0A-MUp5XZnoNGLuP_GW_O3IxfCLH7cC0fb3AfHx4OsBpa_Qu1X-IpvfaCgYKAaoSARMSFQHGX2MiX81qoRex393eWE229OMCUg0175",
|
||||||
|
"refresh_token": "1//0g4mVQrydnc1ECgYIARAAGBASNgF-L9Irk_UxcRjgkz_YyK5Ujs1qCaj8nKL7bqQ0jHnHYlpVRh_Pwm77X4Angp8R-o-fgmKfzg",
|
||||||
|
"expires_in": 3539,
|
||||||
|
"expires_at": dateparser.parse("2024-11-25 09:24:56.690876+00:00"),
|
||||||
|
"token_type": "Bearer",
|
||||||
|
"scopes": [
|
||||||
|
"https://www.googleapis.com/auth/gmail.labels",
|
||||||
|
"https://www.googleapis.com/auth/gmail.modify"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
new_tok = await my_goog.refresh_tokens(old_tokens = old_tok)
|
||||||
|
print("NEW TOKENS:", json.to_string(new_tok, default = str))
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
Reference in New Issue
Block a user