Skip to content
This repository was archived by the owner on Jun 12, 2021. It is now read-only.

Commit b706091

Browse files
committed
First version of support for pushed authorization as defined in
https://tools.ietf.org/html/draft-lodderstedt-oauth-par-00
1 parent e3509b5 commit b706091

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-0
lines changed

Diff for: src/oidcservice/oidc/add_on/pushed_authorization.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import logging
2+
3+
import requests
4+
from cryptojwt import JWT
5+
from oidcmsg.message import Message
6+
from oidcmsg.oauth2 import JWTSecuredAuthorizationRequest
7+
8+
logger = logging.getLogger(__name__)
9+
10+
11+
def push_authorization(request_args, service, **kwargs):
12+
"""
13+
:param request_args: All the request arguments as a AuthorizationRequest instance
14+
:param service: The service to which this post construct method is applied.
15+
:param kwargs: Extra keyword arguments.
16+
"""
17+
18+
method_args = service.service_context.add_on["pushed_authorization"]
19+
20+
# construct the message body
21+
if method_args["body_format"] == "urlencoded":
22+
_body = request_args.to_urlencoded()
23+
else:
24+
_jwt = JWT(key_jar=service.service_context.keyjar,
25+
iss=service.service_context.base_url)
26+
_jws = _jwt.pack(request_args.to_dict())
27+
28+
_msg = Message(request=_jws)
29+
if method_args["merge_rule"] == "lax":
30+
for param in request_args.required_parameters():
31+
_msg[param] = request_args.get(param)
32+
33+
_body = _msg.to_urlencoded()
34+
35+
# Send it to the Pushed Authorization Request Endpoint
36+
resp = method_args["http_client"].get(
37+
service.service_context.provider_info["pushed_authorization_request_endpoint"],
38+
data=_body
39+
)
40+
41+
if resp.status_code == 200:
42+
_resp = Message().from_json(resp.text)
43+
_req = JWTSecuredAuthorizationRequest(request_uri=_resp["request_uri"])
44+
if method_args["merge_rule"] == "lax":
45+
for param in request_args.required_parameters():
46+
_req[param] = request_args.get(param)
47+
request_args = _req
48+
49+
return request_args
50+
51+
52+
def add_pushed_authorization_support(services, body_format="jws", signing_algorthm="RS256",
53+
http_client=None, merge_rule="strict"):
54+
"""
55+
Add the necessary pieces to make pushed authorization happen.
56+
57+
:param services: A dictionary with all the services the client has access to.
58+
:param body_format: jws or urlencoded
59+
"""
60+
61+
if http_client is None:
62+
http_client = requests
63+
64+
_service = services["authorization"]
65+
_service.service_context.add_on['pushed_authorization'] = {
66+
"body_format": body_format,
67+
"signing_algorithm": signing_algorthm,
68+
"http_client": http_client,
69+
"merge_rule": merge_rule
70+
}
71+
72+
_service.post_construct.append(push_authorization)

Diff for: tests/test_21_pushed_auth.py

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import json
2+
import os
3+
4+
import pytest
5+
import responses
6+
from cryptojwt.key_jar import init_key_jar
7+
8+
from oidcservice.client_auth import factory as ca_factory
9+
from oidcservice.oauth2 import DEFAULT_SERVICES
10+
from oidcservice.oidc.add_on import do_add_ons
11+
from oidcservice.service import init_services
12+
from oidcservice.service_context import ServiceContext
13+
from oidcservice.state_interface import InMemoryStateDataBase
14+
15+
_dirname = os.path.dirname(os.path.abspath(__file__))
16+
17+
ISS = 'https://example.com'
18+
19+
KEYSPEC = [
20+
{"type": "RSA", "use": ["sig"]},
21+
{"type": "EC", "crv": "P-256", "use": ["sig"]},
22+
]
23+
24+
CLI_KEY = init_key_jar(public_path='{}/pub_client.jwks'.format(_dirname),
25+
private_path='{}/priv_client.jwks'.format(_dirname),
26+
key_defs=KEYSPEC, owner='')
27+
28+
29+
class TestPushedAuth:
30+
@pytest.fixture(autouse=True)
31+
def create_client(self):
32+
config = {
33+
'client_id': 'client_id', 'client_secret': 'a longesh password',
34+
'redirect_uris': ['https://example.com/cli/authz_cb'],
35+
'behaviour': {'response_types': ['code']},
36+
'add_ons': {
37+
"pushed_authorization": {
38+
"function":
39+
"oidcservice.oidc.add_on.pushed_authorization"
40+
".add_pushed_authorization_support",
41+
"kwargs": {
42+
"body_format": "jws",
43+
"signing_algorthm": "RS256",
44+
"http_client": None,
45+
"merge_rule": "lax"
46+
}
47+
}
48+
}
49+
}
50+
_cam = ca_factory
51+
_srvs = DEFAULT_SERVICES
52+
service_context = ServiceContext(CLI_KEY, client_id='client_id',
53+
issuer='https://www.example.org/as',
54+
config=config)
55+
56+
self.service = init_services(_srvs, service_context, InMemoryStateDataBase(), _cam)
57+
58+
if 'add_ons' in config:
59+
do_add_ons(config['add_ons'], self.service)
60+
61+
service_context.service = self.service
62+
service_context.provider_info = {
63+
"pushed_authorization_request_endpoint": "https://as.example.com/push"
64+
}
65+
66+
def test_authorization(self):
67+
auth_service = self.service["authorization"]
68+
req_args = {'foo': 'bar', "response_type": "code"}
69+
with responses.RequestsMock() as rsps:
70+
_resp = {
71+
"request_uri": "urn:example:bwc4JK-ESC0w8acc191e-Y1LTC2",
72+
"expires_in": 3600
73+
}
74+
rsps.add("GET",
75+
auth_service.service_context.provider_info[
76+
"pushed_authorization_request_endpoint"],
77+
body=json.dumps(_resp), status=200)
78+
79+
_req = auth_service.construct(request_args=req_args, state='state')
80+
81+
assert set(_req.keys()) == {"request_uri", "response_type", "client_id"}

0 commit comments

Comments
 (0)