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

Changes necessary to make OIDC certification a success. #49

Merged
merged 41 commits into from
Nov 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2f12e4c
First set of changes.
rohe Sep 24, 2021
dc03542
Make sure there is a '/' between path and name.
rohe Sep 24, 2021
f9711ad
Allow behaviour_args for do_provider_info and do_client_registration …
rohe Sep 24, 2021
befb1ed
Change default names for where to store signed requests. To not colli…
rohe Sep 24, 2021
76c3fdc
Allow alg == "none"
rohe Sep 24, 2021
c68bcc7
Might not be any behaviour args.
rohe Sep 24, 2021
4e6ba55
Might not be any behaviour args.
rohe Sep 24, 2021
208cdc0
Added argument.
rohe Sep 24, 2021
70a8e71
Fixed tests.
rohe Sep 25, 2021
bbdab14
Set requirement on oidcmsg.
rohe Sep 26, 2021
d334f73
Make repsonse_type actually matter.
rohe Sep 27, 2021
41a8e9c
Might not have an ID Token.
rohe Sep 28, 2021
d941bec
Collect and ID Token if it's not collected as part of the normal stream.
rohe Sep 28, 2021
9a5426e
Collect and ID Token if it's not collected as part of the normal stream.
rohe Sep 28, 2021
381fcf1
Log
rohe Sep 28, 2021
816498c
Log
rohe Sep 30, 2021
6029656
Refactoring
rohe Sep 30, 2021
b5926ef
Refactoring
rohe Sep 30, 2021
f8d0414
Bumped version.
rohe Sep 30, 2021
7d89874
Fixed pick_redirect_uri
rohe Sep 30, 2021
bdaffb6
Missed request args. Necessary !!
rohe Oct 1, 2021
7bce59c
Extended add_callbacks to deal with more callback uris.
rohe Oct 4, 2021
a84270b
Add callback uris handled by the registration service instead of the …
rohe Oct 5, 2021
b9c8570
log
rohe Oct 5, 2021
be2871c
log
rohe Oct 5, 2021
eb7fd14
post_logout_redirect_uri singleton.
rohe Oct 5, 2021
731b7d5
Callback uris are stored in context.callback .
rohe Oct 6, 2021
37d5a24
If no args then I should try to use them.
rohe Oct 6, 2021
6b59b2a
Check if nonce values are the same.
rohe Oct 7, 2021
9ec3b08
Refactored nonce checking.
rohe Oct 7, 2021
90c3a08
Added 2 new parameters to gather_verify_arguments. Allows for extende…
rohe Oct 7, 2021
58f0ec4
Typing
rohe Oct 8, 2021
fac61ff
This method is about getting all the tokens the token endpoint provid…
rohe Oct 10, 2021
28f8751
post_logout_redirect_uri not post_logout_redirect_uris
rohe Oct 13, 2021
57330a1
post_logout_redirect_uri not post_logout_redirect_uris
rohe Oct 13, 2021
59f8170
post_logout_redirect_uri not post_logout_redirect_uris
rohe Oct 14, 2021
813b9d9
post_logout_redirect_uri in logout request. post_logout_redirect_uris…
rohe Oct 14, 2021
3088dec
Spelling error.
rohe Oct 14, 2021
02cfc1e
If no ID Token then no sid.
rohe Oct 20, 2021
de794e2
response_type can be list or singelton.
rohe Oct 20, 2021
3ea017e
Merge pull request #48 from IdentityPython/certification_1
rohe Nov 10, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions example/flask_rp/conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,7 @@
"redirect_uris": [
"https://{domain}:{port}/authz_cb/local"
],
"post_logout_redirect_uris": [
"https://{domain}:{port}/session_logout/local"
],
"post_logout_redirect_uri": "https://{domain}:{port}/session_logout/local",
"frontchannel_logout_uri": "https://{domain}:{port}/fc_logout/local",
"frontchannel_logout_session_required": true,
"backchannel_logout_uri": "https://{domain}:{port}/bc_logout/local",
Expand Down Expand Up @@ -231,9 +229,7 @@
"redirect_uris": [
"https://{domain}:{port}/authz_cb/django"
],
"post_logout_redirect_uris": [
"https://{domain}:{port}/session_logout/django"
],
"post_logout_redirect_uris": "https://{domain}:{port}/session_logout/django",
"frontchannel_logout_uri": "https://{domain}:{port}/fc_logout/django",
"frontchannel_logout_session_required": true,
"backchannel_logout_uri": "https://{domain}:{port}/bc_logout/django",
Expand Down
4 changes: 4 additions & 0 deletions example/flask_rp/templates/opresult.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ <h3>Endpoints</h3>
<dd>{{ url }}</dd>
{% endfor %}
</dl>
<dl>
<dt>ID Token</dt>
<dd>{{ id_token }}</dd>
</dl>
<h3>User information</h3>
<dl>
{% for key, value in userinfo.items() %}
Expand Down
19 changes: 12 additions & 7 deletions example/flask_rp/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,14 @@ def rp():
uid = ''

if iss or uid:
args = {
'req_args': {
"claims": {"id_token": {"acr": {"value": "https://refeds.org/profile/mfa"}}}
}
}

if uid:
args = {'user_id': uid}
else:
args = {}
args['user_id'] = uid

session['op_identifier'] = iss
try:
Expand Down Expand Up @@ -145,14 +149,15 @@ def finalize(op_identifier, request_args):
return render_template('opresult.html', endpoints=endpoints,
userinfo=res['userinfo'],
access_token=res['token'],
id_token=res["id_token"],
**kwargs)
else:
return make_response(res['error'], 400)


def get_op_identifier_by_cb_uri(url: str):
uri = splitquery(url)[0]
for k,v in current_app.rph.issuer2rp.items():
for k, v in current_app.rph.issuer2rp.items():
_cntx = v.get_service_context()
for endpoint in ("redirect_uris",
"post_logout_redirect_uris",
Expand Down Expand Up @@ -180,10 +185,10 @@ def repost_fragment():
return finalize(op_identifier, args)


@oidc_rp_views.route('/ihf_cb')
def ihf_cb(self, op_identifier='', **kwargs):
@oidc_rp_views.route('/authz_im_cb')
def authz_im_cb(op_identifier='', **kwargs):
logger.debug('implicit_hybrid_flow kwargs: {}'.format(kwargs))
return render_template('repost_fragment.html')
return render_template('repost_fragment.html', op_identifier=op_identifier)


@oidc_rp_views.route('/session_iframe')
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def run_tests(self):
"Programming Language :: Python :: 3.9",
"Topic :: Software Development :: Libraries :: Python Modules"],
install_requires=[
'oidcmsg==1.3.3-1',
'oidcmsg==1.4.1',
'pyyaml>=5.1.2',
'responses'
],
Expand Down
2 changes: 1 addition & 1 deletion src/oidcrp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

__author__ = 'Roland Hedberg'
__version__ = '2.0.1'
__version__ = '2.1.0'

logger = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion src/oidcrp/client_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from oidcmsg.oauth2 import SINGLE_OPTIONAL_STRING
from oidcmsg.oidc import AuthnToken
from oidcmsg.time_util import utc_time_sans_frac
from oidcmsg.util import rndstr

from oidcrp.util import rndstr
from oidcrp.util import sanitize
from .defaults import DEF_SIGN_ALG
from .defaults import JWT_BEARER
Expand Down
2 changes: 1 addition & 1 deletion src/oidcrp/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
try:
from secrets import token_urlsafe as rnd_token
except ImportError:
from oidcendpoint import rndstr as rnd_token
from cryptojwt import rndstr as rnd_token

DEFAULT_FILE_ATTRIBUTE_NAMES = ['server_key', 'server_cert', 'filename', 'template_dir',
'private_path', 'public_path', 'db_file']
Expand Down
53 changes: 48 additions & 5 deletions src/oidcrp/oauth2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
from json import JSONDecodeError
import logging
from typing import Optional

from oidcmsg.exception import FormatError
from oidcmsg.message import Message
from oidcmsg.oauth2 import is_error_message

from oidcrp.entity import Entity
from oidcrp.exception import ConfigurationError
from oidcrp.exception import OidcServiceError
from oidcrp.exception import ParseError
from oidcrp.http import HTTPLib
from oidcrp.service import REQUEST_INFO
from oidcrp.service import SUCCESSFUL
from oidcrp.service import Service
from oidcrp.util import do_add_ons
from oidcrp.util import get_deserialization_method

Expand Down Expand Up @@ -72,7 +77,11 @@ def __init__(self, client_authn_factory=None, keyjar=None, verify_ssl=True, conf
# just ignore verify_ssl until it goes away
self.verify_ssl = self.httpc_params.get("verify", True)

def do_request(self, request_type, response_body_type="", request_args=None, **kwargs):
def do_request(self,
request_type: str,
response_body_type: Optional[str] = "",
request_args: Optional[dict] = None,
behaviour_args: Optional[dict] = None, **kwargs):
_srv = self._service[request_type]

_info = _srv.get_request_parameters(request_args=request_args, **kwargs)
Expand All @@ -93,8 +102,13 @@ def set_client_id(self, client_id):
self.client_id = client_id
self._service_context.set('client_id', client_id)

def get_response(self, service, url, method="GET", body=None, response_body_type="",
headers=None, **kwargs):
def get_response(self,
service: Service,
url: str,
method: Optional[str] = "GET",
body: Optional[dict] = None,
response_body_type: Optional[str] = "",
headers: Optional[dict] = None, **kwargs):
"""

:param url:
Expand Down Expand Up @@ -129,8 +143,13 @@ def get_response(self, service, url, method="GET", body=None, response_body_type
return self.parse_request_response(service, resp,
response_body_type, **kwargs)

def service_request(self, service, url, method="GET", body=None,
response_body_type="", headers=None, **kwargs):
def service_request(self,
service: Service,
url: str,
method: Optional[str] = "GET",
body: Optional[dict] = None,
response_body_type: Optional[str] = "",
headers: Optional[dict] = None, **kwargs) -> Message:
"""
The method that sends the request and handles the response returned.
This assumes that the response arrives in the HTTP response.
Expand Down Expand Up @@ -249,3 +268,27 @@ def parse_request_response(self, service, reqresp, response_body_type='',
reqresp.text))
raise OidcServiceError("HTTP ERROR: %s [%s] on %s" % (
reqresp.text, reqresp.status_code, reqresp.url))


def dynamic_provider_info_discovery(client: Client, behaviour_args: Optional[dict]=None):
"""
This is about performing dynamic Provider Info discovery

:param behaviour_args:
:param client: A :py:class:`oidcrp.oidc.Client` instance
"""
try:
client.get_service('provider_info')
except KeyError:
raise ConfigurationError(
'Can not do dynamic provider info discovery')
else:
_context = client.client_get("service_context")
try:
_context.set('issuer', _context.config['srv_discovery_url'])
except KeyError:
pass

response = client.do_request('provider_info', behaviour_args=behaviour_args)
if is_error_message(response):
raise OidcServiceError(response['error'])
4 changes: 2 additions & 2 deletions src/oidcrp/oauth2/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from oidcmsg.time_util import time_sans_frac

from oidcrp.oauth2.utils import get_state_parameter
from oidcrp.oauth2.utils import pick_redirect_uris
from oidcrp.oauth2.utils import pre_construct_pick_redirect_uri
from oidcrp.oauth2.utils import set_state_parameter
from oidcrp.service import Service

Expand All @@ -32,7 +32,7 @@ class Authorization(Service):
def __init__(self, client_get, client_authn_factory=None, conf=None):
Service.__init__(self, client_get,
client_authn_factory=client_authn_factory, conf=conf)
self.pre_construct.extend([pick_redirect_uris, set_state_parameter])
self.pre_construct.extend([pre_construct_pick_redirect_uri, set_state_parameter])
self.post_construct.append(self.store_auth_request)

def update_service_context(self, resp, key='', **kwargs):
Expand Down
65 changes: 44 additions & 21 deletions src/oidcrp/oauth2/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import logging
from typing import Optional
from typing import Union

from oidcmsg.exception import MissingParameter
from oidcmsg.exception import MissingRequiredAttribute
from oidcmsg.message import Message

from oidcrp.service import Service

logger = logging.getLogger(__name__)


def get_state_parameter(request_args, kwargs):
Expand All @@ -14,35 +24,48 @@ def get_state_parameter(request_args, kwargs):
return _state


def pick_redirect_uris(request_args=None, service=None, **kwargs):
"""Pick one redirect_uri base on response_mode out of a list of such."""
_context = service.client_get("service_context")
def pick_redirect_uri(context,
request_args: Optional[Union[Message, dict]] = None,
response_type: Optional[str] = ''):
if request_args is None:
request_args = {}

if 'redirect_uri' in request_args:
return request_args, {}
return request_args["redirect_uri"]

_callback = _context.callback
if _callback:
try:
_response_type = request_args['response_type']
except KeyError:
_response_type = _context.behaviour['response_types'][0]
request_args['response_type'] = _response_type
if context.redirect_uris:
redirect_uri = context.redirect_uris[0]
elif context.callback:
if not response_type:
_conf_resp_types = context.behaviour.get('response_types', [])
response_type = request_args.get('response_type')
if not response_type and _conf_resp_types:
response_type = _conf_resp_types[0]

try:
_response_mode = request_args['response_mode']
except KeyError:
_response_mode = ''
_response_mode = request_args.get('response_mode')

if _response_mode == 'form_post':
request_args['redirect_uri'] = _callback['form_post']
elif _response_type == 'code':
request_args['redirect_uri'] = _callback['code']
if _response_mode == 'form_post' or response_type == ["form_post"]:
redirect_uri = context.callback['form_post']
elif response_type == 'code' or response_type == ["code"]:
redirect_uri = context.callback['code']
else:
request_args['redirect_uri'] = _callback['implicit']
redirect_uri = context.callback['implicit']

logger.debug(
f"pick_redirect_uris: response_type={response_type}, response_mode={_response_mode}, "
f"redirect_uri={redirect_uri}")
else:
request_args['redirect_uri'] = _context.redirect_uris[0]
logger.error("No redirect_uri")
raise MissingRequiredAttribute('redirect_uri')

return redirect_uri


def pre_construct_pick_redirect_uri(request_args: Optional[Union[Message, dict]] = None,
service: Optional[Service] = None, **kwargs):
_context = service.client_get("service_context")
request_args["redirect_uri"] = pick_redirect_uri(_context,
request_args=request_args)
return request_args, {}


Expand Down
10 changes: 10 additions & 0 deletions src/oidcrp/oidc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging

from oidcrp.client_auth import BearerHeader
from oidcrp.oidc.registration import CALLBACK_URIS

try:
from json import JSONDecodeError
Expand Down Expand Up @@ -112,6 +113,15 @@ def __init__(self, client_authn_factory=None,
keyjar=keyjar, verify_ssl=verify_ssl, config=config,
httplib=httplib, services=_srvs, httpc_params=httpc_params)

_context = self.get_service_context()
if _context.callback is None:
_context.callback = {}

for _cb in CALLBACK_URIS:
_uri = config.get(_cb)
if _uri:
_context.callback[_cb] = _uri

def fetch_distributed_claims(self, userinfo, callback=None):
"""

Expand Down
6 changes: 5 additions & 1 deletion src/oidcrp/oidc/access_token.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import logging
from typing import Optional
from typing import Union

from oidcmsg import oidc
from oidcmsg.message import Message
from oidcmsg.oidc import verified_claim_name
from oidcmsg.time_util import time_sans_frac

Expand All @@ -26,7 +28,9 @@ def __init__(self,
access_token.AccessToken.__init__(self, client_get,
client_authn_factory=client_authn_factory, conf=conf)

def gather_verify_arguments(self):
def gather_verify_arguments(self,
response: Optional[Union[dict, Message]] = None,
behaviour_args: Optional[dict] = None):
"""
Need to add some information before running verify()

Expand Down
Loading