@@ -76,10 +76,7 @@ def start_auth(self, context, request_info):
76
76
"""
77
77
oidc_nonce = rndstr ()
78
78
oidc_state = rndstr ()
79
- state_data = {
80
- NONCE_KEY : oidc_nonce ,
81
- STATE_KEY : oidc_state
82
- }
79
+ state_data = {NONCE_KEY : oidc_nonce , STATE_KEY : oidc_state }
83
80
context .state [self .name ] = state_data
84
81
85
82
args = {
@@ -88,7 +85,7 @@ def start_auth(self, context, request_info):
88
85
"client_id" : self .client .client_id ,
89
86
"redirect_uri" : self .client .registration_response ["redirect_uris" ][0 ],
90
87
"state" : oidc_state ,
91
- "nonce" : oidc_nonce
88
+ "nonce" : oidc_nonce ,
92
89
}
93
90
args .update (self .config ["client" ]["auth_req_params" ])
94
91
auth_req = self .client .construct_AuthorizationRequest (request_args = args )
@@ -104,7 +101,9 @@ def register_endpoints(self):
104
101
:return: A list that can be used to map the request to SATOSA to this endpoint.
105
102
"""
106
103
url_map = []
107
- redirect_path = urlparse (self .config ["client" ]["client_metadata" ]["redirect_uris" ][0 ]).path
104
+ redirect_path = urlparse (
105
+ self .config ["client" ]["client_metadata" ]["redirect_uris" ][0 ]
106
+ ).path
108
107
if not redirect_path :
109
108
raise SATOSAError ("Missing path in redirect uri" )
110
109
@@ -122,10 +121,16 @@ def _verify_nonce(self, nonce, context):
122
121
"""
123
122
backend_state = context .state [self .name ]
124
123
if nonce != backend_state [NONCE_KEY ]:
125
- msg = "Missing or invalid nonce in authn response for state: {}" .format (backend_state )
126
- logline = lu .LOG_FMT .format (id = lu .get_session_id (context .state ), message = msg )
124
+ msg = "Missing or invalid nonce in authn response for state: {}" .format (
125
+ backend_state
126
+ )
127
+ logline = lu .LOG_FMT .format (
128
+ id = lu .get_session_id (context .state ), message = msg
129
+ )
127
130
logger .debug (logline )
128
- raise SATOSAAuthenticationError (context .state , "Missing or invalid nonce in authn response" )
131
+ raise SATOSAAuthenticationError (
132
+ context .state , "Missing or invalid nonce in authn response"
133
+ )
129
134
130
135
def _get_tokens (self , authn_response , context ):
131
136
"""
@@ -142,22 +147,24 @@ def _get_tokens(self, authn_response, context):
142
147
"client_secret" : self .client .client_secret ,
143
148
"code" : authn_response ["code" ],
144
149
"grant_type" : "authorization_code" ,
145
- "redirect_uri" : self .client .registration_response [' redirect_uris' ][0 ],
150
+ "redirect_uri" : self .client .registration_response [" redirect_uris" ][0 ],
146
151
}
147
152
148
153
token_resp = requests .post (
149
154
"https://appleid.apple.com/auth/token" ,
150
155
data = args ,
151
- headers = {"Content-Type" : "application/x-www-form-urlencoded" }
152
- ).json ()
156
+ headers = {"Content-Type" : "application/x-www-form-urlencoded" },
157
+ ).json ()
153
158
154
159
logger .debug ("apple response received" )
155
160
logger .debug (token_resp )
156
161
157
162
self ._check_error_response (token_resp , context )
158
163
159
164
keyjar = self .client .keyjar
160
- id_token_claims = dict (Message ().from_jwt (token_resp ["id_token" ], keyjar = keyjar ))
165
+ id_token_claims = dict (
166
+ Message ().from_jwt (token_resp ["id_token" ], keyjar = keyjar )
167
+ )
161
168
162
169
return token_resp ["access_token" ], id_token_claims
163
170
@@ -176,7 +183,9 @@ def _check_error_response(self, response, context):
176
183
error = response ["error" ],
177
184
description = response .get ("error_description" , "" ),
178
185
)
179
- logline = lu .LOG_FMT .format (id = lu .get_session_id (context .state ), message = msg )
186
+ logline = lu .LOG_FMT .format (
187
+ id = lu .get_session_id (context .state ), message = msg
188
+ )
180
189
logger .debug (logline )
181
190
raise SATOSAAuthenticationError (context .state , "Access denied" )
182
191
@@ -192,24 +201,44 @@ def response_endpoint(self, context, *args):
192
201
:return:
193
202
"""
194
203
backend_state = context .state [self .name ]
195
- authn_resp = self .client .parse_response (AuthorizationResponse , info = context .request , sformat = "dict" )
204
+
205
+ # Apple has no userinfo endpoint
206
+ # but may send some user information via POST in the first request.
207
+ #
208
+ # References:
209
+ # - https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple
210
+ # - https://developer.apple.com/documentation/sign_in_with_apple/namei
211
+ try :
212
+ userdata = context .request .get ("user" , "{}" )
213
+ userinfo = json .load (userdata )
214
+ except Exception as e :
215
+ userinfo = {}
216
+
217
+ authn_resp = self .client .parse_response (
218
+ AuthorizationResponse , info = context .request , sformat = "dict"
219
+ )
196
220
if backend_state [STATE_KEY ] != authn_resp ["state" ]:
197
- msg = "Missing or invalid state in authn response for state: {}" .format (backend_state )
198
- logline = lu .LOG_FMT .format (id = lu .get_session_id (context .state ), message = msg )
221
+ msg = "Missing or invalid state in authn response for state: {}" .format (
222
+ backend_state
223
+ )
224
+ logline = lu .LOG_FMT .format (
225
+ id = lu .get_session_id (context .state ), message = msg
226
+ )
199
227
logger .debug (logline )
200
- raise SATOSAAuthenticationError (context .state , "Missing or invalid state in authn response" )
228
+ raise SATOSAAuthenticationError (
229
+ context .state , "Missing or invalid state in authn response"
230
+ )
201
231
202
232
self ._check_error_response (authn_resp , context )
203
233
access_token , id_token_claims = self ._get_tokens (authn_resp , context )
204
234
if not id_token_claims :
205
235
id_token_claims = {}
206
236
207
- # Apple has no userinfo endpoint
208
- userinfo = {}
209
-
210
237
if not id_token_claims and not userinfo :
211
238
msg = "No id_token or userinfo, nothing to do.."
212
- logline = lu .LOG_FMT .format (id = lu .get_session_id (context .state ), message = msg )
239
+ logline = lu .LOG_FMT .format (
240
+ id = lu .get_session_id (context .state ), message = msg
241
+ )
213
242
logger .error (logline )
214
243
raise SATOSAAuthenticationError (context .state , "No user info available." )
215
244
@@ -218,7 +247,9 @@ def response_endpoint(self, context, *args):
218
247
logline = lu .LOG_FMT .format (id = lu .get_session_id (context .state ), message = msg )
219
248
logger .debug (logline )
220
249
del context .state [self .name ]
221
- internal_resp = self ._translate_response (all_user_claims , self .client .authorization_endpoint )
250
+ internal_resp = self ._translate_response (
251
+ all_user_claims , self .client .authorization_endpoint
252
+ )
222
253
return self .auth_callback_func (context , internal_resp )
223
254
224
255
def _translate_response (self , response , issuer ):
@@ -245,7 +276,9 @@ def get_metadata_desc(self):
245
276
See satosa.backends.oauth.get_metadata_desc
246
277
:rtype: satosa.metadata_creation.description.MetadataDescription
247
278
"""
248
- return get_metadata_desc_for_oauth_backend (self .config ["provider_metadata" ]["issuer" ], self .config )
279
+ return get_metadata_desc_for_oauth_backend (
280
+ self .config ["provider_metadata" ]["issuer" ], self .config
281
+ )
249
282
250
283
251
284
def _create_client (provider_metadata , client_metadata , verify_ssl = True ):
@@ -258,15 +291,15 @@ def _create_client(provider_metadata, client_metadata, verify_ssl=True):
258
291
:return: client instance to use for communicating with the configured provider
259
292
:rtype: oic.oic.Client
260
293
"""
261
- client = oic .Client (
262
- client_authn_method = CLIENT_AUTHN_METHOD , verify_ssl = verify_ssl
263
- )
294
+ client = oic .Client (client_authn_method = CLIENT_AUTHN_METHOD , verify_ssl = verify_ssl )
264
295
265
296
# Provider configuration information
266
297
if "authorization_endpoint" in provider_metadata :
267
298
# no dynamic discovery necessary
268
- client .handle_provider_config (ProviderConfigurationResponse (** provider_metadata ),
269
- provider_metadata ["issuer" ])
299
+ client .handle_provider_config (
300
+ ProviderConfigurationResponse (** provider_metadata ),
301
+ provider_metadata ["issuer" ],
302
+ )
270
303
else :
271
304
# do dynamic discovery
272
305
client .provider_config (provider_metadata ["issuer" ])
@@ -277,9 +310,12 @@ def _create_client(provider_metadata, client_metadata, verify_ssl=True):
277
310
client .store_registration_info (RegistrationRequest (** client_metadata ))
278
311
else :
279
312
# do dynamic registration
280
- client .register (client .provider_info ['registration_endpoint' ],
281
- ** client_metadata )
313
+ client .register (
314
+ client .provider_info ["registration_endpoint" ], ** client_metadata
315
+ )
282
316
283
- client .subject_type = (client .registration_response .get ("subject_type" ) or
284
- client .provider_info ["subject_types_supported" ][0 ])
317
+ client .subject_type = (
318
+ client .registration_response .get ("subject_type" )
319
+ or client .provider_info ["subject_types_supported" ][0 ]
320
+ )
285
321
return client
0 commit comments