2
2
import datetime
3
3
import hashlib
4
4
import json
5
+ import re
5
6
from urllib .parse import parse_qs , urlencode , urlparse
6
7
7
8
from django .contrib .auth import get_user_model
27
28
RefreshToken = get_refresh_token_model ()
28
29
UserModel = get_user_model ()
29
30
31
+ URI_OOB = "urn:ietf:wg:oauth:2.0:oob"
32
+ URI_OOB_AUTO = "urn:ietf:wg:oauth:2.0:oob:auto"
30
33
31
34
# mocking a protected resource view
32
35
class ResourceView (ProtectedResourceView ):
@@ -46,6 +49,7 @@ def setUp(self):
46
49
name = "Test Application" ,
47
50
redirect_uris = (
48
51
"http://localhost http://example.com http://example.org custom-scheme://example.com"
52
+ " " + URI_OOB + " " + URI_OOB_AUTO
49
53
),
50
54
user = self .dev_user ,
51
55
client_type = Application .CLIENT_CONFIDENTIAL ,
@@ -1456,6 +1460,94 @@ def test_code_exchange_succeed_when_redirect_uri_match_with_multiple_query_param
1456
1460
self .assertEqual (content ["scope" ], "read write" )
1457
1461
self .assertEqual (content ["expires_in" ], oauth2_settings .ACCESS_TOKEN_EXPIRE_SECONDS )
1458
1462
1463
+ def test_oob_as_html (self ):
1464
+ """
1465
+ Test out-of-band authentication.
1466
+ """
1467
+ self .client .login (username = "test_user" , password = "123456" )
1468
+
1469
+ authcode_data = {
1470
+ "client_id" : self .application .client_id ,
1471
+ "state" : "random_state_string" ,
1472
+ "scope" : "read write" ,
1473
+ "redirect_uri" : URI_OOB ,
1474
+ "response_type" : "code" ,
1475
+ "allow" : True ,
1476
+ }
1477
+
1478
+ response = self .client .post (reverse ("oauth2_provider:authorize" ), data = authcode_data )
1479
+ self .assertEqual (response .status_code , 200 )
1480
+ self .assertRegex (response ['Content-Type' ], r'^text/html' )
1481
+
1482
+ content = response .content .decode ("utf-8" )
1483
+
1484
+ # "A lot of applications, for legacy reasons, use this and regex
1485
+ # to extract the token, risking summoning zalgo in the process."
1486
+ # -- https://github.com/jazzband/django-oauth-toolkit/issues/235
1487
+
1488
+ matches = re .search (r'.*<code>([^<>]*)</code>' ,
1489
+ content )
1490
+ self .assertIsNotNone (matches ,
1491
+ msg = "OOB response contains code inside <code> tag" )
1492
+ self .assertEqual (len (matches .groups ()), 1 ,
1493
+ msg = "OOB response contains multiple <code> tags" )
1494
+ authorization_code = matches .groups ()[0 ]
1495
+
1496
+ token_request_data = {
1497
+ "grant_type" : "authorization_code" ,
1498
+ "code" : authorization_code ,
1499
+ "redirect_uri" : URI_OOB ,
1500
+ "client_id" : self .application .client_id ,
1501
+ "client_secret" : self .application .client_secret ,
1502
+ }
1503
+
1504
+ response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data )
1505
+ self .assertEqual (response .status_code , 200 )
1506
+
1507
+ content = json .loads (response .content .decode ("utf-8" ))
1508
+ self .assertEqual (content ["token_type" ], "Bearer" )
1509
+ self .assertEqual (content ["scope" ], "read write" )
1510
+ self .assertEqual (content ["expires_in" ], oauth2_settings .ACCESS_TOKEN_EXPIRE_SECONDS )
1511
+
1512
+ def test_oob_as_json (self ):
1513
+ """
1514
+ Test out-of-band authentication, with a JSON response.
1515
+ """
1516
+ self .client .login (username = "test_user" , password = "123456" )
1517
+
1518
+ authcode_data = {
1519
+ "client_id" : self .application .client_id ,
1520
+ "state" : "random_state_string" ,
1521
+ "scope" : "read write" ,
1522
+ "redirect_uri" : URI_OOB_AUTO ,
1523
+ "response_type" : "code" ,
1524
+ "allow" : True ,
1525
+ }
1526
+
1527
+ response = self .client .post (reverse ("oauth2_provider:authorize" ), data = authcode_data )
1528
+ self .assertEqual (response .status_code , 200 )
1529
+ self .assertRegex (response ['Content-Type' ], '^application/json' )
1530
+
1531
+ parsed_response = json .loads (response .content .decode ("utf-8" ))
1532
+
1533
+ self .assertIn ('access_token' , parsed_response )
1534
+ authorization_code = parsed_response ['access_token' ]
1535
+
1536
+ token_request_data = {
1537
+ "grant_type" : "authorization_code" ,
1538
+ "code" : authorization_code ,
1539
+ "redirect_uri" : URI_OOB_AUTO ,
1540
+ "client_id" : self .application .client_id ,
1541
+ "client_secret" : self .application .client_secret ,
1542
+ }
1543
+
1544
+ response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data )
1545
+ self .assertEqual (response .status_code , 200 )
1546
+
1547
+ content = json .loads (response .content .decode ("utf-8" ))
1548
+ self .assertEqual (content ["token_type" ], "Bearer" )
1549
+ self .assertEqual (content ["scope" ], "read write" )
1550
+ self .assertEqual (content ["expires_in" ], oauth2_settings .ACCESS_TOKEN_EXPIRE_SECONDS )
1459
1551
1460
1552
class TestAuthorizationCodeProtectedResource (BaseTest ):
1461
1553
def test_resource_access_allowed (self ):
0 commit comments