Skip to content

Commit

Permalink
Fix Oauth flow for client with Client Credentials (#733)
Browse files Browse the repository at this point in the history
* Fix Oauth flow for client with Client Credentials

Close #727
  • Loading branch information
tpazderka committed Dec 23, 2019
1 parent 0c53a95 commit 9c05f34
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ The format is based on the [KeepAChangeLog] project.

## Unreleased

### Fixed
- [#727] OAuth client request using Client Credentials grant

### Added
- [#719] Add support for JWT registration tokens

[#719]: https://github.com/OpenIDC/pyoidc/pull/719
[#727]: https://github.com/OpenIDC/pyoidc/issues/727

## 1.1.2 [2019-11-23]

Expand Down
13 changes: 9 additions & 4 deletions src/oic/oauth2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from oic.oauth2.message import AuthorizationErrorResponse
from oic.oauth2.message import AuthorizationRequest
from oic.oauth2.message import AuthorizationResponse
from oic.oauth2.message import CCAccessTokenRequest
from oic.oauth2.message import ErrorResponse
from oic.oauth2.message import GrantExpired
from oic.oauth2.message import Message
Expand Down Expand Up @@ -67,8 +68,8 @@
REQUEST2ENDPOINT = {
"AuthorizationRequest": "authorization_endpoint",
"AccessTokenRequest": "token_endpoint",
# ROPCAccessTokenRequest: "authorization_endpoint",
# CCAccessTokenRequest: "authorization_endpoint",
"ROPCAccessTokenRequest": "token_endpoint",
"CCAccessTokenRequest": "token_endpoint",
"RefreshAccessTokenRequest": "token_endpoint",
"TokenRevocationRequest": "token_endpoint",
}
Expand Down Expand Up @@ -400,7 +401,11 @@ def construct_AuthorizationRequest(

def construct_AccessTokenRequest(
self,
request: Type[AccessTokenRequest] = None,
request: Union[
Type[AccessTokenRequest],
Type[ROPCAccessTokenRequest],
Type[CCAccessTokenRequest],
] = None,
request_args=None,
extra_args=None,
**kwargs
Expand All @@ -410,7 +415,7 @@ def construct_AccessTokenRequest(
request = self.message_factory.get_request_type("token_endpoint")
if request_args is None:
request_args = {}
if request is not ROPCAccessTokenRequest:
if not issubclass(request, (ROPCAccessTokenRequest, CCAccessTokenRequest)):
grant = self.get_grant(**kwargs)

if not grant.is_valid():
Expand Down
36 changes: 36 additions & 0 deletions tests/test_oauth2.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from urllib.parse import urlparse

import pytest
import responses

from oic.oauth2 import Client
from oic.oauth2 import Grant
Expand All @@ -18,11 +19,14 @@
from oic.oauth2.message import AuthorizationErrorResponse
from oic.oauth2.message import AuthorizationRequest
from oic.oauth2.message import AuthorizationResponse
from oic.oauth2.message import CCAccessTokenRequest
from oic.oauth2.message import DecodeError
from oic.oauth2.message import ErrorResponse
from oic.oauth2.message import FormatError
from oic.oauth2.message import GrantExpired
from oic.oauth2.message import MessageTuple
from oic.oauth2.message import MissingRequiredAttribute
from oic.oauth2.message import OauthMessageFactory
from oic.oauth2.message import RefreshAccessTokenRequest
from oic.utils import time_util
from oic.utils.keyio import KeyBundle
Expand Down Expand Up @@ -68,10 +72,12 @@ class TestClient(object):
def create_client(self):
self.redirect_uri = "https://example.com/redirect"
self.authorization_endpoint = "https://example.com/authz"
self.token_endpoint = "https://example.com/token"

self.client = Client("1", config={"issuer": "https://example.com/as"})
self.client.redirect_uris = [self.redirect_uri]
self.client.authorization_endpoint = self.authorization_endpoint
self.client.token_endpoint = self.token_endpoint

def test_construct_authz_req_no_optional_params(self):
areq = self.client.construct_AuthorizationRequest(
Expand Down Expand Up @@ -182,6 +188,16 @@ def test_construct_access_token_req(self):
assert atr["code"] == "AbCdEf"
assert atr["redirect_uri"] == self.redirect_uri

def test_construct_access_token_req_client_credentials(self):
# scope is default=""
request_args = {"grant_type": "client_credentials"}
atr = self.client.construct_AccessTokenRequest(
state="stat", request=CCAccessTokenRequest, request_args=request_args
)

assert atr["grant_type"] == "client_credentials"
assert atr["state"] == "stat"

def test_construct_access_token_request_fail(self):
with pytest.raises(GrantError):
self.client.construct_AccessTokenRequest(state="unknown")
Expand Down Expand Up @@ -521,6 +537,26 @@ def test_client_endpoint(self):
self.client._endpoint("token_endpoint")
self.client._endpoint("foo_endpoint")

def test_do_access_token_request_client_credentials(self):
class CCMessageFactory(OauthMessageFactory):
"""We are doing client credentials."""

token_endpoint = MessageTuple(CCAccessTokenRequest, AccessTokenResponse)

self.client.message_factory = CCMessageFactory
with responses.RequestsMock() as rsps:
rsps.add(
rsps.POST,
self.token_endpoint,
json={"access_token": "Token", "token_type": "bearer"},
)

resp = self.client.do_access_token_request()
assert rsps.calls[0].request.body == "grant_type=client_credentials"

assert isinstance(resp, AccessTokenResponse)
assert resp["access_token"] == "Token"


class TestServer(object):
@pytest.fixture(autouse=True)
Expand Down

0 comments on commit 9c05f34

Please sign in to comment.