def test_oauth2_authorize(self): app = Flask(__name__) app.secret_key = '!' oauth = OAuth(app) client = oauth.register('dev', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize') with app.test_request_context(): resp = client.authorize_redirect('https://b.com/bar') self.assertEqual(resp.status_code, 302) url = resp.headers.get('Location') self.assertIn('state=', url) state = session['_dev_authlib_state_'] self.assertIsNotNone(state) with app.test_request_context(path='/?code=a&state={}'.format(state)): # session is cleared in tests session['_dev_authlib_state_'] = state with mock.patch('requests.sessions.Session.send') as send: send.return_value = mock_send_value(get_bearer_token()) token = client.authorize_access_token() self.assertEqual(token['access_token'], 'a') with app.test_request_context(): self.assertEqual(client.token, None)
def test_oauth2_authorize(self): request = self.factory.get('/login') request.session = self.factory.session oauth = OAuth() client = oauth.register( 'dev', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', ) rv = client.authorize_redirect(request, 'https://a.b/c') self.assertEqual(rv.status_code, 302) url = rv.get('Location') self.assertIn('state=', url) state = dict(url_decode(urlparse.urlparse(url).query))['state'] with mock.patch('requests.sessions.Session.send') as send: send.return_value = mock_send_value(get_bearer_token()) request2 = self.factory.get('/authorize?state={}'.format(state)) request2.session = request.session token = client.authorize_access_token(request2) self.assertEqual(token['access_token'], 'a')
def test_parse_id_token_nonce_supported(self): key = jwk.dumps('secret', 'oct', kid='f') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123', 'nonce_supported': False}, key, alg='HS256', iss='https://i.b', aud='dev', exp=3600, ) app = Flask(__name__) app.secret_key = '!' oauth = OAuth(app) client = oauth.register( 'dev', client_id='dev', client_secret='dev', fetch_token=get_bearer_token, jwks={'keys': [key]}, issuer='https://i.b', id_token_signing_alg_values_supported=['HS256', 'RS256'], ) with app.test_request_context(): session['_dev_authlib_nonce_'] = 'n' token['id_token'] = id_token user = client.parse_id_token(token) self.assertEqual(user.sub, '123')
def test_access_token_with_fetch_token(self): app = Flask(__name__) app.secret_key = '!' oauth = OAuth() token = get_bearer_token() oauth.init_app(app, fetch_token=lambda name: token) client = oauth.register('dev', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize') def fake_send(sess, req, **kwargs): auth = req.headers['Authorization'] self.assertEqual(auth, 'Bearer {}'.format(token['access_token'])) resp = mock.MagicMock() resp.text = 'hi' resp.status_code = 200 return resp with app.test_request_context(): with mock.patch('requests.sessions.Session.send', fake_send): resp = client.get('/api/user') self.assertEqual(resp.text, 'hi') # trigger ctx.authlib_client_oauth_token resp = client.get('/api/user') self.assertEqual(resp.text, 'hi')
async def test_runtime_error_fetch_jwks_uri(): key = jwk.dumps('secret', 'oct', kid='f') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123'}, key, alg='HS256', iss='https://i.b', aud='dev', exp=3600, nonce='n', ) oauth = OAuth() client = oauth.register( 'dev', client_id='dev', client_secret='dev', fetch_token=get_bearer_token, issuer='https://i.b', id_token_signing_alg_values_supported=['HS256'], ) req_scope = {'type': 'http', 'session': {'_dev_authlib_nonce_': 'n'}} req = Request(req_scope) token['id_token'] = id_token with pytest.raises(RuntimeError): await client.parse_id_token(req, token)
async def test_force_fetch_jwks_uri(): secret_keys = read_file_path('jwks_private.json') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123'}, secret_keys, alg='RS256', iss='https://i.b', aud='dev', exp=3600, nonce='n', ) app = AsyncPathMapDispatch({ '/jwks': {'body': read_file_path('jwks_public.json')} }) oauth = OAuth() client = oauth.register( 'dev', client_id='dev', client_secret='dev', fetch_token=get_bearer_token, jwks_uri='https://i.b/jwks', issuer='https://i.b', client_kwargs={ 'app': app, } ) req_scope = {'type': 'http', 'session': {'_dev_authlib_nonce_': 'n'}} req = Request(req_scope) token['id_token'] = id_token user = await client.parse_id_token(req, token) assert user.sub == '123'
def test_runtime_error_fetch_jwks_uri(self): key = jwk.dumps('secret', 'oct', kid='f') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123'}, key, alg='HS256', iss='https://i.b', aud='dev', exp=3600, nonce='n', ) app = Flask(__name__) app.secret_key = '!' oauth = OAuth(app) client = oauth.register( 'dev', client_id='dev', client_secret='dev', fetch_token=get_bearer_token, jwks={'keys': [jwk.dumps('secret', 'oct', kid='b')]}, issuer='https://i.b', id_token_signing_alg_values_supported=['HS256'], ) with app.test_request_context(): session['_dev_authlib_nonce_'] = 'n' token['id_token'] = id_token self.assertRaises(RuntimeError, client.parse_id_token, token)
def test_oauth2_authorize_code_verifier(self): request = self.factory.get('/login') request.session = self.factory.session oauth = OAuth() client = oauth.register( 'dev', client_id='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', client_kwargs={'code_challenge_method': 'S256'}, ) state = 'foo' code_verifier = 'bar' rv = client.authorize_redirect(request, 'https://a.b/c', state=state, code_verifier=code_verifier) self.assertEqual(rv.status_code, 302) url = rv.get('Location') self.assertIn('state=', url) self.assertIn('code_challenge=', url) with mock.patch('requests.sessions.Session.send') as send: send.return_value = mock_send_value(get_bearer_token()) request2 = self.factory.get('/authorize?state={}'.format(state)) request2.session = request.session token = client.authorize_access_token(request2) self.assertEqual(token['access_token'], 'a')
async def test_oauth2_authorize(): oauth = OAuth() app = AsyncPathMapDispatch({'/token': {'body': get_bearer_token()}}) client = oauth.register('dev', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', client_kwargs={ 'app': app, }) req_scope = {'type': 'http', 'session': {}} req = Request(req_scope) resp = await client.authorize_redirect(req, 'https://b.com/bar') assert resp.status_code == 302 url = resp.headers.get('Location') assert 'state=' in url state = req.session.get('_dev_authlib_state_') assert state is not None req_scope.update({ 'path': '/', 'query_string': f'code=a&state={state}', 'session': req.session, }) req = Request(req_scope) token = await client.authorize_access_token(req) assert token['access_token'] == 'a'
def test_oauth2_fetch_access_token(self): url = 'https://example.com/token' token = get_bearer_token() with mock.patch('requests.sessions.Session.send') as send: send.return_value = mock_send_value(token) client = OAuthClient(client_id='foo', access_token_url=url) self.assertEqual(client.fetch_access_token(), token) self.assertEqual(client.fetch_access_token(url), token)
def test_openid_authorize(self): app = Flask(__name__) app.secret_key = '!' oauth = OAuth(app) key = jwk.dumps('secret', 'oct', kid='f') client = oauth.register( 'dev', client_id='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', client_kwargs={'scope': 'openid profile'}, jwks={'keys': [key]}, ) with app.test_request_context(): resp = client.authorize_redirect('https://b.com/bar') self.assertEqual(resp.status_code, 302) url = resp.headers['Location'] query_data = dict(url_decode(urlparse.urlparse(url).query)) state = query_data['state'] self.assertIsNotNone(state) session_data = session[f'_state_dev_{state}'] nonce = session_data['data']['nonce'] self.assertIsNotNone(nonce) self.assertEqual(nonce, query_data['nonce']) token = get_bearer_token() token['id_token'] = generate_id_token( token, {'sub': '123'}, key, alg='HS256', iss='https://i.b', aud='dev', exp=3600, nonce=query_data['nonce'], ) path = '/?code=a&state={}'.format(state) with app.test_request_context(path=path): session[f'_state_dev_{state}'] = session_data with mock.patch('requests.sessions.Session.send') as send: send.return_value = mock_send_value(token) token = client.authorize_access_token() self.assertEqual(token['access_token'], 'a') self.assertIn('userinfo', token)
def test_oauth2_access_token_with_post(self): app = Flask(__name__) app.secret_key = '!' oauth = OAuth(app) client = oauth.register('dev', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize') payload = {'code': 'a', 'state': 'b'} with app.test_request_context(data=payload, method='POST'): session['_dev_authlib_state_'] = 'b' with mock.patch('requests.sessions.Session.send') as send: send.return_value = mock_send_value(get_bearer_token()) token = client.authorize_access_token() self.assertEqual(token['access_token'], 'a')
def test_openid_authorize(self): request = self.factory.get('/login') request.session = self.factory.session key = jwk.dumps('secret', 'oct', kid='f') oauth = OAuth() client = oauth.register( 'dev', client_id='dev', jwks={'keys': [key]}, api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', client_kwargs={'scope': 'openid profile'}, ) resp = client.authorize_redirect(request, 'https://b.com/bar') self.assertEqual(resp.status_code, 302) url = resp.get('Location') self.assertIn('nonce=', url) query_data = dict(url_decode(urlparse.urlparse(url).query)) token = get_bearer_token() token['id_token'] = generate_id_token( token, {'sub': '123'}, key, alg='HS256', iss='https://i.b', aud='dev', exp=3600, nonce=query_data['nonce'], ) state = query_data['state'] with mock.patch('requests.sessions.Session.send') as send: send.return_value = mock_send_value(token) request2 = self.factory.get( '/authorize?state={}&code=foo'.format(state)) request2.session = request.session token = client.authorize_access_token(request2) self.assertEqual(token['access_token'], 'a') self.assertIn('userinfo', token) self.assertEqual(token['userinfo']['sub'], '123')
async def test_oauth2_authorize_code_challenge(): dispatch = PathMapDispatch({ '/token': {'body': get_bearer_token()} }) oauth = OAuth() client = oauth.register( 'dev', client_id='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', client_kwargs={ 'code_challenge_method': 'S256', 'dispatch': dispatch, }, ) req_scope = {'type': 'http', 'session': {}} req = Request(req_scope) resp = await client.authorize_redirect(req, redirect_uri='https://b.com/bar') assert resp.status_code == 302 url = resp.headers.get('Location') assert 'code_challenge=' in url assert 'code_challenge_method=S256' in url state = req.session['_dev_authlib_state_'] assert state is not None verifier = req.session['_dev_authlib_code_verifier_'] assert verifier is not None req_scope.update( { 'path': '/', 'query_string': 'code=a&state={}'.format(state).encode(), 'session': req.session, } ) req = Request(req_scope) token = await client.authorize_access_token(req) assert token['access_token'] == 'a'
def test_oauth2_access_token_with_post(self): oauth = OAuth() client = oauth.register( 'dev', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', ) payload = {'code': 'a', 'state': 'b'} with mock.patch('requests.sessions.Session.send') as send: send.return_value = mock_send_value(get_bearer_token()) request = self.factory.post('/token', data=payload) request.session = self.factory.session request.session['_state_dev_b'] = {'data': {}} token = client.authorize_access_token(request) self.assertEqual(token['access_token'], 'a')
def test_parse_id_token(self): key = jwk.dumps('secret', 'oct', kid='f') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123'}, key, alg='HS256', iss='https://i.b', aud='dev', exp=3600, nonce='n', ) app = Flask(__name__) app.secret_key = '!' oauth = OAuth(app) client = oauth.register( 'dev', client_id='dev', client_secret='dev', fetch_token=get_bearer_token, jwks={'keys': [key]}, issuer='https://i.b', id_token_signing_alg_values_supported=['HS256', 'RS256'], ) with app.test_request_context(): self.assertIsNone(client.parse_id_token(token, nonce='n')) token['id_token'] = id_token user = client.parse_id_token(token, nonce='n') self.assertEqual(user.sub, '123') claims_options = {'iss': {'value': 'https://i.b'}} user = client.parse_id_token(token, nonce='n', claims_options=claims_options) self.assertEqual(user.sub, '123') claims_options = {'iss': {'value': 'https://i.c'}} self.assertRaises(InvalidClaimError, client.parse_id_token, token, 'n', claims_options)
async def test_parse_id_token(): key = jwk.dumps('secret', 'oct', kid='f') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123'}, key, alg='HS256', iss='https://i.b', aud='dev', exp=3600, nonce='n', ) oauth = OAuth() client = oauth.register( 'dev', client_id='dev', client_secret='dev', fetch_token=get_bearer_token, jwks={'keys': [key]}, issuer='https://i.b', id_token_signing_alg_values_supported=['HS256', 'RS256'], ) req_scope = {'type': 'http', 'session': {'_dev_authlib_nonce_': 'n'}} req = Request(req_scope) user = await client.parse_id_token(req, token) assert user is None token['id_token'] = id_token user = await client.parse_id_token(req, token) assert user.sub == '123' claims_options = {'iss': {'value': 'https://i.b'}} user = await client.parse_id_token(req, token, claims_options) assert user.sub == '123' with pytest.raises(InvalidClaimError): claims_options = {'iss': {'value': 'https://i.c'}} await client.parse_id_token(req, token, claims_options)
def test_force_fetch_jwks_uri(self): secret_keys = read_file_path('jwks_private.json') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123'}, secret_keys, alg='RS256', iss='https://i.b', aud='dev', exp=3600, nonce='n', ) app = Flask(__name__) app.secret_key = '!' oauth = OAuth(app) client = oauth.register( 'dev', client_id='dev', client_secret='dev', fetch_token=get_bearer_token, jwks={'keys': [jwk.dumps('secret', 'oct', kid='f')]}, jwks_uri='https://i.b/jwks', issuer='https://i.b', ) def fake_send(sess, req, **kwargs): resp = mock.MagicMock() resp.json = lambda: read_file_path('jwks_public.json') resp.status_code = 200 return resp with app.test_request_context(): session['_dev_authlib_nonce_'] = 'n' self.assertIsNone(client.parse_id_token(token)) with mock.patch('requests.sessions.Session.send', fake_send): token['id_token'] = id_token user = client.parse_id_token(token) self.assertEqual(user.sub, '123')
def test_request_with_token(self): with mock.patch('requests.sessions.Session.send') as send: send.return_value = mock_send_value({'name': 'a'}) client = OAuthClient(client_id='foo') token = get_bearer_token() resp = client.get('https://i.b/user', token=token) self.assertEqual(resp.json()['name'], 'a') resp = client.post('https://i.b/user', token=token) self.assertEqual(resp.json()['name'], 'a') resp = client.put('https://i.b/user', token=token) self.assertEqual(resp.json()['name'], 'a') resp = client.delete('https://i.b/user', token=token) self.assertEqual(resp.json()['name'], 'a') client.api_base_url = 'https://i.b' resp = client.get('user', token=token) self.assertEqual(resp.json()['name'], 'a')
async def test_oauth2_authorize_access_denied(): oauth = OAuth() app = AsyncPathMapDispatch({'/token': {'body': get_bearer_token()}}) client = oauth.register('dev', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', client_kwargs={ 'app': app, }) req = Request({ 'type': 'http', 'session': {}, 'path': '/', 'query_string': 'error=access_denied&error_description=Not+Allowed', }) with pytest.raises(OAuthError): await client.authorize_access_token(req)
def fake_send(sess, req, **kwargs): self.assertEqual(sess.token['access_token'], 'dev') return mock_send_value(get_bearer_token())
async def fetch_token(request): return get_bearer_token()
def fake_send(sess, req, **kwargs): self.assertIn(f'code_verifier={verifier}', req.body) return mock_send_value(get_bearer_token())
def fake_send(sess, req, **kwargs): self.assertIn('code_verifier={}'.format(verifier), req.body) return mock_send_value(get_bearer_token())