-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauth.py
More file actions
135 lines (94 loc) · 4.09 KB
/
auth.py
File metadata and controls
135 lines (94 loc) · 4.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import os
import gspread
import requests as http_requests
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from utils import encrypt, decrypt, resource_path
import json
import uuid
from constants import SCOPES
# Authentication for the googleAuth, this requires more security to prevent vulnerabilities to the application.
DEVICE_ID = hex(uuid.getnode())
BASE_DIR = os.path.join(os.getenv("APPDATA"), "TimeTracker")
TOKEN_FILE = os.path.join(BASE_DIR, "tokens")
CLIENT_SECRET_FILE = resource_path("client_secret.json")
def safe_email(email):
return email.replace("@", "_").replace(".", "_")
class GoogleAuth:
def __init__(self):
os.makedirs(TOKEN_FILE, exist_ok=True)
self.user_email = None
self.token_file = None
self.creds = None
def get_token_path(self, email):
return os.path.join(TOKEN_FILE, f"{safe_email(email)}_token.json")
def is_logged_in(self):
if not os.path.exists(TOKEN_FILE):
return False
tokens = [f for f in os.listdir(TOKEN_FILE) if f.endswith("_token.json")]
if not tokens:
return False
# Load first token automatically
token_path = os.path.join(TOKEN_FILE, tokens[0])
self.token_file = token_path
# extract email from filename
email = tokens[0].replace("_token.json", "")
self.user_email = email.replace("_", "@", 1).replace("_", ".")
return True
def authenticate(self):
creds = None
if self.token_file and os.path.exists(self.token_file):
with open(self.token_file, "rb") as f:
encrypted = f.read()
decrypted = decrypt(encrypted).decode()
data = json.loads(decrypted)
if data.get("device_id") != DEVICE_ID:
raise Exception("Token belongs to another device.")
creds = Credentials.from_authorized_user_info(data, SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
try:
creds.refresh(Request())
except Exception:
raise Exception("Session expired. Please sign in again.")
else:
flow = InstalledAppFlow.from_client_secrets_file(
CLIENT_SECRET_FILE, SCOPES
)
creds = flow.run_local_server(port=0)
response = http_requests.get(
"https://www.googleapis.com/oauth2/v2/userinfo",
headers={"Authorization": f"Bearer {creds.token}"}
)
info = response.json()
email = info.get("email")
if not email:
raise Exception("Could not retrieve Google Account Email.")
self.user_email = email
self.token_file = self.get_token_path(self.user_email)
data = json.loads(creds.to_json())
data["device_id"] = DEVICE_ID
encrypted = encrypt(json.dumps(data).encode())
with open(self.token_file, "wb") as token:
token.write(encrypted)
# Get Email if not set yet
if not self.user_email:
response = http_requests.get(
"https://www.googleapis.com/oauth2/v2/userinfo",
headers={"Authorization": f"Bearer {creds.token}"}
)
info = response.json()
email = info.get("email")
if not email:
raise Exception("Could not retrieve Google Account Email.")
self.user_email = email
self.token_file = self.get_token_path(self.user_email)
self.creds = creds
return gspread.authorize(creds)
def sign_out(self):
if self.token_file and os.path.exists(self.token_file):
os.remove(self.token_file)
self.user_email = None
self.creds = None
self.token_file = None