25
25
import ssl
26
26
import atexit
27
27
28
+ import secrets
29
+ import hashlib
30
+ import base64
31
+
28
32
import tempfile as tf
29
33
from time import sleep
30
34
from threading import Thread
@@ -83,6 +87,7 @@ def __init__(self, session, **kwargs):
83
87
puser = cfg .get ('proxy_user' , '' )
84
88
ppw = cfg .get ('proxy_pw' , '' )
85
89
pauthkey = cfg .get ('proxy_authkey' , '' )
90
+ self .pkce = cfg .get ('pkce' , None )
86
91
87
92
try :
88
93
self .outopts = getattr (SAScfg , "SAS_output_options" )
@@ -271,6 +276,15 @@ def __init__(self, session, **kwargs):
271
276
else :
272
277
self .verify = bool (inver )
273
278
279
+ inpkce = kwargs .get ('pkce' , None )
280
+ if inpkce is not None :
281
+ if lock and self .pkce :
282
+ logger .warning ("Parameter 'pkce' passed to SAS_session was ignored due to configuration restriction." )
283
+ else :
284
+ self .pkce = inpkce
285
+ if self .pkce is None :
286
+ self .pkce = True
287
+
274
288
if len (self .url ) > 0 :
275
289
http = self .url .split ('://' )
276
290
hp = http [1 ].split (':' )
@@ -297,6 +311,7 @@ def __init__(self, session, **kwargs):
297
311
else :
298
312
self .port = 80
299
313
314
+ cv = None
300
315
if not self ._token and not authcode and not jwt and not self .serverid :
301
316
found = False
302
317
if self .authkey :
@@ -352,12 +367,23 @@ def __init__(self, session, **kwargs):
352
367
raise RuntimeError ("Neither authcode nor userid provided." )
353
368
354
369
if code_pw .lower () == 'authcode' :
355
- purl = "/SASLogon/oauth/authorize?client_id={}&response_type=code" .format (client_id )
370
+ if self .pkce :
371
+ cv = secrets .token_urlsafe (32 )
372
+ cvh = hashlib .sha256 (cv .encode ('ascii' )).digest ()
373
+ cvhe = base64 .urlsafe_b64encode (cvh )
374
+ cc = cvhe .decode ('ascii' )[:- 1 ]
375
+ purl = "/SASLogon/oauth/authorize?client_id={}&response_type=code&code_challenge_method=S256&code_challenge={}" .format (client_id , cc )
376
+ else :
377
+ purl = "/SASLogon/oauth/authorize?client_id={}&response_type=code" .format (client_id )
378
+
356
379
if len (self .url ) > 0 :
357
380
purl = self .url + purl
358
381
else :
359
382
purl = "http{}://{}:{}{}" .format ('s' if self .ssl else '' , self .ip , str (self .port ), purl )
360
- msg = "The default url to authenticate with would be {}\n " .format (purl )
383
+ if self .pkce :
384
+ msg = "The PKCE required url to authenticate with is {}\n " .format (purl )
385
+ else :
386
+ msg = "The default url to authenticate with would be {}\n " .format (purl )
361
387
msg += "Please enter authcode: "
362
388
authcode = self ._prompt (msg )
363
389
if authcode is None :
@@ -472,7 +498,7 @@ def __init__(self, session, **kwargs):
472
498
473
499
# get AuthToken
474
500
if not self ._token :
475
- js = self ._authenticate (user , pw , authcode , client_id , client_secret , jwt )
501
+ js = self ._authenticate (user , pw , authcode , client_id , client_secret , jwt , cv )
476
502
self ._token = js .get ('access_token' , None )
477
503
self ._refresh = js .get ('refresh_token' , None )
478
504
@@ -557,7 +583,7 @@ def __init__(self, session, **kwargs):
557
583
558
584
return
559
585
560
- def _authenticate (self , user , pw , authcode , client_id , client_secret , jwt ):
586
+ def _authenticate (self , user , pw , authcode , client_id , client_secret , jwt , cv ):
561
587
562
588
if self .serverid :
563
589
return {'access_token' :'tom' }
@@ -566,9 +592,13 @@ def _authenticate(self, user, pw, authcode, client_id, client_secret, jwt):
566
592
uauthcode = urllib .parse .quote (authcode )
567
593
uclient_id = urllib .parse .quote (client_id )
568
594
uclient_secret = urllib .parse .quote (client_secret )
569
- d1 = ("grant_type=authorization_code&code=" + uauthcode +
570
- "&client_id=" + uclient_id + "&client_secret=" + uclient_secret ).encode (self .encoding )
571
595
headers = {"Accept" :"application/vnd.sas.compute.session+json" ,"Content-Type" :"application/x-www-form-urlencoded" }
596
+ if self .pkce :
597
+ d1 = ("grant_type=authorization_code&code=" + uauthcode + "&code_verifier=" + cv +
598
+ "&client_id=" + uclient_id + "&client_secret=" + uclient_secret ).encode (self .encoding )
599
+ else :
600
+ d1 = ("grant_type=authorization_code&code=" + uauthcode +
601
+ "&client_id=" + uclient_id + "&client_secret=" + uclient_secret ).encode (self .encoding )
572
602
elif jwt :
573
603
ujwt = urllib .parse .quote (jwt )
574
604
d1 = "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=" + ujwt
0 commit comments