3
3
from django .http import JsonResponse
4
4
5
5
from todo .utils .jwt_utils import verify_jwt_token
6
- from todo .utils .google_jwt_utils import validate_google_access_token
6
+ from todo .utils .google_jwt_utils import (
7
+ validate_google_access_token ,
8
+ validate_google_refresh_token ,
9
+ generate_google_access_token ,
10
+ )
7
11
from todo .exceptions .auth_exceptions import TokenMissingError , TokenExpiredError , TokenInvalidError
8
- from todo .exceptions .google_auth_exceptions import GoogleTokenExpiredError , GoogleTokenInvalidError
12
+ from todo .exceptions .google_auth_exceptions import (
13
+ GoogleTokenExpiredError ,
14
+ GoogleTokenInvalidError ,
15
+ GoogleRefreshTokenExpiredError ,
16
+ )
9
17
from todo .constants .messages import AuthErrorMessages , ApiErrors
10
18
from todo .dto .responses .error_response import ApiErrorResponse , ApiErrorDetail
11
19
@@ -25,7 +33,8 @@ def __call__(self, request):
25
33
auth_success = self ._try_authentication (request )
26
34
27
35
if auth_success :
28
- return self .get_response (request )
36
+ response = self .get_response (request )
37
+ return self ._process_response (request , response )
29
38
else :
30
39
error_response = ApiErrorResponse (
31
40
statusCode = status .HTTP_401_UNAUTHORIZED ,
@@ -38,7 +47,8 @@ def __call__(self, request):
38
47
],
39
48
)
40
49
return JsonResponse (
41
- data = error_response .model_dump (mode = "json" , exclude_none = True ), status = status .HTTP_401_UNAUTHORIZED
50
+ data = error_response .model_dump (mode = "json" , exclude_none = True ),
51
+ status = status .HTTP_401_UNAUTHORIZED
42
52
)
43
53
44
54
except (TokenMissingError , TokenExpiredError , TokenInvalidError ) as e :
@@ -57,7 +67,8 @@ def __call__(self, request):
57
67
],
58
68
)
59
69
return JsonResponse (
60
- data = error_response .model_dump (mode = "json" , exclude_none = True ), status = status .HTTP_401_UNAUTHORIZED
70
+ data = error_response .model_dump (mode = "json" , exclude_none = True ),
71
+ status = status .HTTP_401_UNAUTHORIZED
61
72
)
62
73
63
74
def _try_authentication (self , request ) -> bool :
@@ -73,25 +84,61 @@ def _try_google_auth(self, request) -> bool:
73
84
try :
74
85
google_token = request .COOKIES .get ("ext-access" )
75
86
76
- if not google_token :
77
- return False
78
-
79
- payload = validate_google_access_token (google_token )
87
+ if google_token :
88
+ try :
89
+ payload = validate_google_access_token (google_token )
90
+ self ._set_google_user_data (request , payload )
91
+ return True
92
+ except (GoogleTokenExpiredError , GoogleTokenInvalidError ):
93
+ pass
80
94
81
- request .auth_type = "google"
82
- request .user_id = payload ["user_id" ]
83
- request .google_id = payload ["google_id" ]
84
- request .user_email = payload ["email" ]
85
- request .user_name = payload ["name" ]
86
- request .user_role = "external_user"
87
-
88
- return True
95
+ return self ._try_google_refresh (request )
89
96
90
97
except (GoogleTokenExpiredError , GoogleTokenInvalidError ) as e :
91
98
raise e
92
99
except Exception :
93
100
return False
94
101
102
+ def _try_google_refresh (self , request ) -> bool :
103
+ """Try to refresh Google access token"""
104
+ try :
105
+ refresh_token = request .COOKIES .get ("ext-refresh" )
106
+
107
+ if not refresh_token :
108
+ return False
109
+
110
+ payload = validate_google_refresh_token (refresh_token )
111
+
112
+ user_data = {
113
+ "user_id" : payload ["user_id" ],
114
+ "google_id" : payload ["google_id" ],
115
+ "email" : payload ["email" ],
116
+ "name" : payload .get ("name" , "" ),
117
+ }
118
+
119
+ new_access_token = generate_google_access_token (user_data )
120
+
121
+ self ._set_google_user_data (request , payload )
122
+
123
+ request ._new_access_token = new_access_token
124
+ request ._access_token_expires = settings .GOOGLE_JWT ["ACCESS_TOKEN_LIFETIME" ]
125
+
126
+ return True
127
+
128
+ except (GoogleRefreshTokenExpiredError , GoogleTokenInvalidError ):
129
+ return False
130
+ except Exception :
131
+ return False
132
+
133
+ def _set_google_user_data (self , request , payload ):
134
+ """Set Google user data on request"""
135
+ request .auth_type = "google"
136
+ request .user_id = payload ["user_id" ]
137
+ request .google_id = payload ["google_id" ]
138
+ request .user_email = payload ["email" ]
139
+ request .user_name = payload .get ("name" , "" )
140
+ request .user_role = "external_user"
141
+
95
142
def _try_rds_auth (self , request ) -> bool :
96
143
try :
97
144
rds_token = request .COOKIES .get (self .rds_cookie_name )
@@ -112,6 +159,28 @@ def _try_rds_auth(self, request) -> bool:
112
159
except Exception :
113
160
return False
114
161
162
+ def _process_response (self , request , response ):
163
+ """Process response and set new cookies if Google token was refreshed"""
164
+ if hasattr (request , '_new_access_token' ):
165
+ config = self ._get_cookie_config ()
166
+ response .set_cookie (
167
+ "ext-access" ,
168
+ request ._new_access_token ,
169
+ max_age = request ._access_token_expires ,
170
+ ** config
171
+ )
172
+ return response
173
+
174
+ def _get_cookie_config (self ):
175
+ """Get Google cookie configuration"""
176
+ return {
177
+ "path" : "/" ,
178
+ "domain" : settings .GOOGLE_COOKIE_SETTINGS .get ("COOKIE_DOMAIN" ),
179
+ "secure" : settings .GOOGLE_COOKIE_SETTINGS .get ("COOKIE_SECURE" , False ),
180
+ "httponly" : True ,
181
+ "samesite" : settings .GOOGLE_COOKIE_SETTINGS .get ("COOKIE_SAMESITE" , "Lax" ),
182
+ }
183
+
115
184
def _is_public_path (self , path : str ) -> bool :
116
185
return any (path .startswith (public_path ) for public_path in settings .PUBLIC_PATHS )
117
186
@@ -122,7 +191,8 @@ def _handle_rds_auth_error(self, exception):
122
191
errors = [ApiErrorDetail (title = ApiErrors .AUTHENTICATION_FAILED , detail = str (exception ))],
123
192
)
124
193
return JsonResponse (
125
- data = error_response .model_dump (mode = "json" , exclude_none = True ), status = status .HTTP_401_UNAUTHORIZED
194
+ data = error_response .model_dump (mode = "json" , exclude_none = True ),
195
+ status = status .HTTP_401_UNAUTHORIZED
126
196
)
127
197
128
198
def _handle_google_auth_error (self , exception ):
@@ -132,7 +202,8 @@ def _handle_google_auth_error(self, exception):
132
202
errors = [ApiErrorDetail (title = ApiErrors .AUTHENTICATION_FAILED , detail = str (exception ))],
133
203
)
134
204
return JsonResponse (
135
- data = error_response .model_dump (mode = "json" , exclude_none = True ), status = status .HTTP_401_UNAUTHORIZED
205
+ data = error_response .model_dump (mode = "json" , exclude_none = True ),
206
+ status = status .HTTP_401_UNAUTHORIZED
136
207
)
137
208
138
209
@@ -169,4 +240,4 @@ def get_current_user_info(request) -> dict:
169
240
}
170
241
)
171
242
172
- return user_info
243
+ return user_info
0 commit comments