5
5
from django .views .decorators .csrf import csrf_exempt
6
6
from django .views .generic import View
7
7
8
- import jwt
9
- from cryptography .hazmat .backends import default_backend
10
- from cryptography .x509 import load_pem_x509_certificate
11
-
12
8
from allauth .socialaccount .adapter import get_adapter
13
9
from allauth .socialaccount .helpers import (
14
10
complete_social_login ,
15
11
render_authentication_error ,
16
12
)
13
+ from allauth .socialaccount .internal import jwtkit
17
14
from allauth .socialaccount .providers .oauth2 .client import OAuth2Error
18
15
from allauth .socialaccount .providers .oauth2 .views import (
19
16
OAuth2Adapter ,
61
58
)
62
59
63
60
61
+ def _verify_and_decode (app , credential , verify_signature = True ):
62
+ return jwtkit .verify_and_decode (
63
+ credential = credential ,
64
+ keys_url = CERTS_URL ,
65
+ issuer = ID_TOKEN_ISSUER ,
66
+ audience = app .client_id ,
67
+ lookup_kid = jwtkit .lookup_kid_pem_x509_certificate ,
68
+ verify_signature = verify_signature ,
69
+ )
70
+
71
+
64
72
class GoogleOAuth2Adapter (OAuth2Adapter ):
65
73
provider_id = GoogleProvider .id
66
74
access_token_url = ACCESS_TOKEN_URL
@@ -85,27 +93,14 @@ def complete_login(self, request, app, token, response, **kwargs):
85
93
return login
86
94
87
95
def _decode_id_token (self , app , id_token ):
88
- try :
89
- data = jwt .decode (
90
- id_token ,
91
- # Since the token was received by direct communication
92
- # protected by TLS between this library and Google, we
93
- # are allowed to skip checking the token signature
94
- # according to the OpenID Connect Core 1.0
95
- # specification.
96
- # https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
97
- options = {
98
- "verify_signature" : False ,
99
- "verify_iss" : True ,
100
- "verify_aud" : True ,
101
- "verify_exp" : True ,
102
- },
103
- issuer = self .id_token_issuer ,
104
- audience = app .client_id ,
105
- )
106
- except jwt .PyJWTError as e :
107
- raise OAuth2Error ("Invalid id_token" ) from e
108
- return data
96
+ """
97
+ Since the token was received by direct communication protected by
98
+ TLS between this library and Google, we are allowed to skip checking the
99
+ token signature according to the OpenID Connect Core 1.0 specification.
100
+
101
+ https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
102
+ """
103
+ return _verify_and_decode (app , id_token , verify_signature = False )
109
104
110
105
def _fetch_user_info (self , access_token ):
111
106
resp = (
@@ -132,9 +127,9 @@ def dispatch(self, request):
132
127
try :
133
128
return super ().dispatch (request )
134
129
except (
130
+ OAuth2Error ,
135
131
requests .RequestException ,
136
132
PermissionDenied ,
137
- jwt .PyJWTError ,
138
133
) as exc :
139
134
return render_authentication_error (request , self .provider , exception = exc )
140
135
@@ -147,20 +142,7 @@ def post(self, request, *args, **kwargs):
147
142
self .check_csrf (request )
148
143
149
144
credential = request .POST .get ("credential" )
150
- alg , key = self .get_key (credential )
151
- identity_data = jwt .decode (
152
- credential ,
153
- key ,
154
- options = {
155
- "verify_signature" : True ,
156
- "verify_iss" : True ,
157
- "verify_aud" : True ,
158
- "verify_exp" : True ,
159
- },
160
- issuer = ID_TOKEN_ISSUER ,
161
- audience = self .provider .app .client_id ,
162
- algorithms = [alg ],
163
- )
145
+ identity_data = _verify_and_decode (app = self .provider .app , credential = credential )
164
146
login = self .provider .sociallogin_from_response (request , identity_data )
165
147
return complete_social_login (request , login )
166
148
@@ -174,21 +156,5 @@ def check_csrf(self, request):
174
156
if csrf_token_cookie != csrf_token_body :
175
157
raise PermissionDenied ("Failed to verify double submit cookie." )
176
158
177
- def get_key (self , credential ):
178
- header = jwt .get_unverified_header (credential )
179
- # {'alg': 'RS256', 'kid': '0ad1fec78504f447bae65bcf5afaedb65eec9e81', 'typ': 'JWT'}
180
- kid = header ["kid" ]
181
- alg = header ["alg" ]
182
- response = get_adapter ().get_requests_session ().get (CERTS_URL )
183
- response .raise_for_status ()
184
- jwks = response .json ()
185
- key = jwks .get (kid )
186
- if not key :
187
- raise PermissionDenied ("invalid 'kid'" )
188
- key = load_pem_x509_certificate (
189
- key .encode ("utf8" ), default_backend ()
190
- ).public_key ()
191
- return alg , key
192
-
193
159
194
160
login_by_token = csrf_exempt (LoginByTokenView .as_view ())
0 commit comments