API - check reponse type when authorizing an app + add missing tests
This commit is contained in:
parent
94f2f2fec7
commit
922ff63d99
@ -523,13 +523,14 @@ def authorize(auth_user: User) -> Union[HttpResponse, Dict]:
|
|||||||
:form string response_type: client response type (only 'code' is supported
|
:form string response_type: client response type (only 'code' is supported
|
||||||
by FitTrackee)
|
by FitTrackee)
|
||||||
:form string scopes: OAuth2 client scopes
|
:form string scopes: OAuth2 client scopes
|
||||||
:form boolean confirm: confirmation
|
:form boolean confirm: confirmation (must be 'true')
|
||||||
:form string state: unique value to prevent cross-site request forgery
|
:form string state: unique value to prevent cross-site request forgery
|
||||||
(not mandatory)
|
(not mandatory but recommended)
|
||||||
:form string code_challenge: string generated from a code verifier
|
:form string code_challenge: string generated from a code verifier
|
||||||
(for PKCE, not mandatory)
|
(for PKCE, not mandatory but recommended)
|
||||||
:form string code_challenge_method: method used to create challenge,
|
:form string code_challenge_method: method used to create challenge,
|
||||||
for instance "S256" (for PKCE, not mandatory)
|
for instance "S256" (mandatory if `code_challenge`
|
||||||
|
provided)
|
||||||
|
|
||||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||||
|
|
||||||
@ -543,7 +544,12 @@ def authorize(auth_user: User) -> Union[HttpResponse, Dict]:
|
|||||||
- invalid token, please log in again
|
- invalid token, please log in again
|
||||||
"""
|
"""
|
||||||
data = request.form
|
data = request.form
|
||||||
if not data or 'client_id' not in data or 'response_type' not in data:
|
if (
|
||||||
|
not data
|
||||||
|
or 'client_id' not in data
|
||||||
|
or 'response_type' not in data
|
||||||
|
or data.get('response_type') != 'code'
|
||||||
|
):
|
||||||
return InvalidPayloadErrorResponse()
|
return InvalidPayloadErrorResponse()
|
||||||
|
|
||||||
confirm = data.get('confirm', 'false')
|
confirm = data.get('confirm', 'false')
|
||||||
@ -594,7 +600,7 @@ def issue_token() -> Response:
|
|||||||
:form string code: code generated after authorizing the client
|
:form string code: code generated after authorizing the client
|
||||||
(for token issue)
|
(for token issue)
|
||||||
:form string code_verifier: code verifier
|
:form string code_verifier: code verifier
|
||||||
(for PKCE and token issue, not mandatory)
|
(for token issue with PKCE, not mandatory)
|
||||||
:form string refresh_token: refresh token (for token refresh)
|
:form string refresh_token: refresh token (for token refresh)
|
||||||
|
|
||||||
:statuscode 200: success
|
:statuscode 200: success
|
||||||
|
@ -108,7 +108,7 @@ class TestOAuthClientCreation(ApiTestCaseMixin):
|
|||||||
|
|
||||||
self.assert_400(
|
self.assert_400(
|
||||||
response,
|
response,
|
||||||
error_message=('OAuth2 client invalid scopes'),
|
error_message='OAuth2 client invalid scopes',
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_it_creates_oauth_client(self, app: Flask, user_1: User) -> None:
|
def test_it_creates_oauth_client(self, app: Flask, user_1: User) -> None:
|
||||||
@ -254,6 +254,29 @@ class TestOAuthClientAuthorization(ApiTestCaseMixin):
|
|||||||
|
|
||||||
self.assert_400(response, error_message='invalid payload')
|
self.assert_400(response, error_message='invalid payload')
|
||||||
|
|
||||||
|
def test_it_returns_error_when_response_type_is_not_code(
|
||||||
|
self, app: Flask, user_1: User
|
||||||
|
) -> None:
|
||||||
|
client, auth_token = self.get_test_client_and_auth_token(
|
||||||
|
app, user_1.email
|
||||||
|
)
|
||||||
|
oauth_client = self.create_oauth2_client(user_1)
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
self.route,
|
||||||
|
data={
|
||||||
|
'client_id': oauth_client.client_id,
|
||||||
|
'response_type': self.random_string(),
|
||||||
|
'confirm': True,
|
||||||
|
},
|
||||||
|
headers=dict(
|
||||||
|
Authorization=f'Bearer {auth_token}',
|
||||||
|
content_type='multipart/form-data',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_400(response, error_message='invalid payload')
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'input_confirmation', [{'confirm': True}, {'confirm': 'true'}]
|
'input_confirmation', [{'confirm': True}, {'confirm': 'true'}]
|
||||||
)
|
)
|
||||||
@ -341,7 +364,9 @@ class TestOAuthClientAuthorization(ApiTestCaseMixin):
|
|||||||
f'{oauth_client.get_default_redirect_uri()}?code={code.code}'
|
f'{oauth_client.get_default_redirect_uri()}?code={code.code}'
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.parametrize('input_confirmation', [{}, {'confirm': False}])
|
@pytest.mark.parametrize(
|
||||||
|
'input_confirmation', [{}, {'confirm': False}, {'confirm': 'false'}]
|
||||||
|
)
|
||||||
def test_it_returns_error_when_no_confirmation(
|
def test_it_returns_error_when_no_confirmation(
|
||||||
self, app: Flask, user_1: User, input_confirmation: Dict
|
self, app: Flask, user_1: User, input_confirmation: Dict
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -531,12 +556,31 @@ class TestOAuthIssueAccessToken(OAuthIssueTokenTestCase):
|
|||||||
|
|
||||||
self.assert_invalid_request(response)
|
self.assert_invalid_request(response)
|
||||||
|
|
||||||
def test_it_returns_error_when_code_is_invalid(
|
def test_it_returns_error_when_grant_type_is_not_authorization_code(
|
||||||
self, app: Flask, user_1: User
|
self, app: Flask, user_1: User
|
||||||
) -> None:
|
) -> None:
|
||||||
oauth_client, code = self.create_authorized_oauth_client(app, user_1)
|
oauth_client, code = self.create_authorized_oauth_client(app, user_1)
|
||||||
client = app.test_client()
|
client = app.test_client()
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
self.route,
|
||||||
|
data={
|
||||||
|
'client_id': oauth_client.client_id,
|
||||||
|
'client_secret': oauth_client.client_secret,
|
||||||
|
'grant_type': self.random_string(),
|
||||||
|
'code': code,
|
||||||
|
},
|
||||||
|
headers=dict(content_type='multipart/form-data'),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_unsupported_grant_type(response)
|
||||||
|
|
||||||
|
def test_it_returns_error_when_code_is_invalid(
|
||||||
|
self, app: Flask, user_1: User
|
||||||
|
) -> None:
|
||||||
|
oauth_client, _ = self.create_authorized_oauth_client(app, user_1)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
self.route,
|
self.route,
|
||||||
data={
|
data={
|
||||||
@ -571,6 +615,34 @@ class TestOAuthIssueAccessToken(OAuthIssueTokenTestCase):
|
|||||||
class TestOAuthIssueAccessTokenWithCodeChallenge(OAuthIssueTokenTestCase):
|
class TestOAuthIssueAccessTokenWithCodeChallenge(OAuthIssueTokenTestCase):
|
||||||
route = '/api/oauth/token'
|
route = '/api/oauth/token'
|
||||||
|
|
||||||
|
def test_it_returns_error_when_grant_type_is_not_authorization_code(
|
||||||
|
self, app: Flask, user_1: User
|
||||||
|
) -> None:
|
||||||
|
code_verifier = generate_token(48)
|
||||||
|
oauth_client, code = self.create_authorized_oauth_client(
|
||||||
|
app,
|
||||||
|
user_1,
|
||||||
|
code_challenge={
|
||||||
|
'code_challenge': create_s256_code_challenge(code_verifier),
|
||||||
|
'code_challenge_method': 'S256',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
self.route,
|
||||||
|
data={
|
||||||
|
'client_id': oauth_client.client_id,
|
||||||
|
'client_secret': oauth_client.client_secret,
|
||||||
|
'grant_type': self.random_string(),
|
||||||
|
'code': code,
|
||||||
|
'code_verifier': code_verifier,
|
||||||
|
},
|
||||||
|
headers=dict(content_type='multipart/form-data'),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_unsupported_grant_type(response)
|
||||||
|
|
||||||
def test_it_raises_error_when_code_verifier_is_invalid(
|
def test_it_raises_error_when_code_verifier_is_invalid(
|
||||||
self, app: Flask, user_1: User
|
self, app: Flask, user_1: User
|
||||||
) -> None:
|
) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user