Skip to content

Commit

Permalink
enable microservice-based flow to replay AuthnRequest
Browse files Browse the repository at this point in the history
(requires wsgi_app in registered callback)
  • Loading branch information
rhoerbe committed Oct 17, 2019
1 parent d8ba645 commit da3f4e7
Show file tree
Hide file tree
Showing 27 changed files with 129 additions and 116 deletions.
2 changes: 1 addition & 1 deletion src/satosa/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __init__(self, auth_callback_func, internal_attributes, base_url, name):
self.base_url = base_url
self.name = name

def start_auth(self, context, internal_request):
def start_auth(self, context, internal_request, **kwargs):
"""
This is the start up function of the backend authorization.
Expand Down
4 changes: 2 additions & 2 deletions src/satosa/backends/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def __init__(self, outgoing, internal_attributes, config, base_url, name):
outgoing, internal_attributes, config, base_url, name, 'github',
'id')

def start_auth(self, context, internal_request, get_state=stateID):
def start_auth(self, context, internal_request, get_state=stateID, **kwargs):
"""
:param get_state: Generates a state to be used in authentication call
Expand Down Expand Up @@ -75,7 +75,7 @@ def auth_info(self, requrest):
UNSPECIFIED, None,
self.config['server_info']['authorization_endpoint'])

def _authn_response(self, context):
def _authn_response(self, context, **kwargs):
state_data = context.state[self.name]
aresp = self.consumer.parse_response(
AuthorizationResponse, info=json.dumps(context.request))
Expand Down
4 changes: 2 additions & 2 deletions src/satosa/backends/linkedin.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def __init__(self, outgoing, internal_attributes, config, base_url, name):
outgoing, internal_attributes, config, base_url, name, 'linkedin',
'id')

def start_auth(self, context, internal_request, get_state=stateID):
def start_auth(self, context, internal_request, get_state=stateID, **kwargs):
"""
:param get_state: Generates a state to be used in authentication call
Expand Down Expand Up @@ -76,7 +76,7 @@ def auth_info(self, requrest):
UNSPECIFIED, None,
self.config['server_info']['authorization_endpoint'])

def _authn_response(self, context):
def _authn_response(self, context, **kwargs):
state_data = context.state[self.name]
aresp = self.consumer.parse_response(
AuthorizationResponse, info=json.dumps(context.request))
Expand Down
4 changes: 2 additions & 2 deletions src/satosa/backends/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def __init__(self, outgoing, internal_attributes, config, base_url, name, extern
response_type=self.config["response_type"])
self.consumer.client_secret = self.config["client_secret"]

def start_auth(self, context, internal_request, get_state=stateID):
def start_auth(self, context, internal_request, get_state=stateID, **kwargs):
"""
See super class method satosa.backends.base#start_auth
:param get_state: Generates a state to be used in the authentication call.
Expand Down Expand Up @@ -118,7 +118,7 @@ def _verify_state(self, resp, state_data, state):
"Missing or invalid state [%s] in response!" %
received_state)

def _authn_response(self, context):
def _authn_response(self, context, **kwargs):
"""
Handles the authentication response from the AS.
Expand Down
4 changes: 2 additions & 2 deletions src/satosa/backends/openid_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def __init__(self, auth_callback_func, internal_attributes, config, base_url, na
if "response_type" not in config["client"]["auth_req_params"]:
config["auth_req_params"]["response_type"] = "code"

def start_auth(self, context, request_info):
def start_auth(self, context, request_info, **kwargs):
"""
See super class method satosa.backends.base#start_auth
:type context: satosa.context.Context
Expand Down Expand Up @@ -167,7 +167,7 @@ def _get_userinfo(self, state, context):
self._check_error_response(userinfo_resp, context)
return userinfo_resp.to_dict()

def response_endpoint(self, context, *args):
def response_endpoint(self, context, *args, **kwargs):
"""
Handles the authentication response from the OP.
:type context: satosa.context.Context
Expand Down
2 changes: 1 addition & 1 deletion src/satosa/backends/orcid.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def auth_info(self, requrest):
UNSPECIFIED, None,
self.config['server_info']['authorization_endpoint'])

def _authn_response(self, context):
def _authn_response(self, context, **kwargs):
state_data = context.state[self.name]
aresp = self.consumer.parse_response(
AuthorizationResponse, info=json.dumps(context.request))
Expand Down
25 changes: 13 additions & 12 deletions src/satosa/backends/saml2.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def __init__(self, outgoing, internal_attributes, config, base_url, name):
with open(p) as key_file:
self.encryption_keys.append(key_file.read())

def get_idp_entity_id(self, context):
def get_idp_entity_id(self, context, **kwargs):
"""
:type context: satosa.context.Context
:rtype: str | None
Expand Down Expand Up @@ -164,7 +164,7 @@ def get_idp_entity_id(self, context):
)
return entity_id

def start_auth(self, context, internal_req):
def start_auth(self, context, internal_req, **kwargs):
"""
See super class method satosa.backends.base.BackendModule#start_auth
Expand All @@ -184,7 +184,7 @@ def start_auth(self, context, internal_req):

return self.authn_request(context, entity_id)

def disco_query(self, context):
def disco_query(self, context, **kwargs):
"""
Makes a request to the discovery server
Expand Down Expand Up @@ -236,7 +236,7 @@ def construct_requested_authn_context(self, entity_id):

return authn_context

def authn_request(self, context, entity_id):
def authn_request(self, context, entity_id, **kwargs):
"""
Do an authorization request on idp with given entity id.
This is the start of the authorization.
Expand Down Expand Up @@ -294,7 +294,7 @@ def authn_request(self, context, entity_id):
context.state[self.name] = {"relay_state": relay_state}
return make_saml_response(binding, ht_args)

def authn_response(self, context, binding):
def authn_response(self, context, binding, **kwargs):
"""
Endpoint for the idp response
:type context: satosa.context,Context
Expand Down Expand Up @@ -326,11 +326,12 @@ def authn_response(self, context, binding):
raise SATOSAAuthenticationError(context.state, errmsg)
del self.outstanding_queries[req_id]

# check if the relay_state matches the cookie state
if context.state[self.name]["relay_state"] != context.request["RelayState"]:
satosa_logging(logger, logging.DEBUG,
"State did not match relay state for state", context.state)
raise SATOSAAuthenticationError(context.state, "State did not match relay state")
# if the response relay_state exists it must match that from the request
if self.name in context.state and "relay_state" in context.state[self.name]:
if context.state[self.name]["relay_state"] != context.request["RelayState"]:
logger.debug("State did not match relay state for state", extra={'state': context.state})
raise SATOSAAuthenticationError(context.state, "State did not match relay state")
del context.state[self.name]

context.decorate(Context.KEY_BACKEND_METADATA_STORE, self.sp.metadata)
if self.config.get(SAMLBackend.KEY_MEMORIZE_IDP):
Expand All @@ -340,7 +341,7 @@ def authn_response(self, context, binding):
context.state.pop(Context.KEY_FORCE_AUTHN, None)
return self.auth_callback_func(context, self._translate_response(authn_response, context.state))

def disco_response(self, context):
def disco_response(self, context, **kwargs):
"""
Endpoint for the discovery server response
Expand Down Expand Up @@ -406,7 +407,7 @@ def _translate_response(self, response, state):
json.dumps(response.ava, indent=4), state)
return internal_resp

def _metadata_endpoint(self, context):
def _metadata_endpoint(self, context, **kwargs):
"""
Endpoint for retrieving the backend metadata
:type context: satosa.context.Context
Expand Down
2 changes: 1 addition & 1 deletion src/satosa/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ def _run_bound_endpoint(self, context, spec):
:return: response
"""
try:
return spec(context)
return spec(context, wsgi_app=self)
except SATOSAAuthenticationError as error:
error.error_id = uuid.uuid4().urn
state = json.dumps(error.state.state_dict, indent=4)
Expand Down
2 changes: 1 addition & 1 deletion src/satosa/frontends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __init__(self, auth_req_callback_func, internal_attributes, base_url, name):
self.base_url = base_url
self.name = name

def handle_authn_response(self, context, internal_resp):
def handle_authn_response(self, context, internal_resp, **kwargs):
"""
If an authorization has been successful in a backend, this function is called and is
supposed to send an authorization response to the client.
Expand Down
16 changes: 8 additions & 8 deletions src/satosa/frontends/openid_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def _init_authorization_state(self):
return AuthorizationState(HashBasedSubjectIdentifierFactory(sub_hash_salt), authz_code_db, access_token_db,
refresh_token_db, sub_db, **token_lifetimes)

def handle_authn_response(self, context, internal_resp, extra_id_token_claims=None):
def handle_authn_response(self, context, internal_resp, extra_id_token_claims=None, **kwargs):
"""
See super class method satosa.frontends.base.FrontendModule#handle_authn_response
:type context: satosa.context.Context
Expand Down Expand Up @@ -232,7 +232,7 @@ def _get_authn_request_from_state(self, state):
"""
return AuthorizationRequest().deserialize(state[self.name]["oidc_request"])

def client_registration(self, context):
def client_registration(self, context, **kwargs):
"""
Handle the OIDC dynamic client registration.
:type context: satosa.context.Context
Expand All @@ -247,7 +247,7 @@ def client_registration(self, context):
except InvalidClientRegistrationRequest as e:
return BadRequest(e.to_json(), content="application/json")

def provider_config(self, context):
def provider_config(self, context, **kwargs):
"""
Construct the provider configuration information (served at /.well-known/openid-configuration).
:type context: satosa.context.Context
Expand All @@ -270,7 +270,7 @@ def _get_approved_attributes(self, provider_supported_claims, authn_req):
requested_claims.extend(authn_req["claims"][k].keys())
return set(provider_supported_claims).intersection(set(requested_claims))

def _handle_authn_request(self, context):
def _handle_authn_request(self, context, **kwargs):
"""
Parse and verify the authentication request into an internal request.
:type context: satosa.context.Context
Expand Down Expand Up @@ -315,7 +315,7 @@ def _handle_authn_request(self, context):
authn_req))
return internal_req

def handle_authn_request(self, context):
def handle_authn_request(self, context, **kwargs):
"""
Handle an authentication request and pass it on to the backend.
:type context: satosa.context.Context
Expand All @@ -329,7 +329,7 @@ def handle_authn_request(self, context):
return internal_req
return self.auth_req_callback_func(context, internal_req)

def jwks(self, context):
def jwks(self, context, **kwargs):
"""
Construct the JWKS document (served at /jwks).
:type context: satosa.context.Context
Expand All @@ -340,7 +340,7 @@ def jwks(self, context):
"""
return Response(json.dumps(self.provider.jwks), content="application/json")

def token_endpoint(self, context):
def token_endpoint(self, context, **kwargs):
"""
Handle token requests (served at /token).
:type context: satosa.context.Context
Expand All @@ -364,7 +364,7 @@ def token_endpoint(self, context):
error_resp = TokenErrorResponse(error=e.oauth_error, error_description=str(e))
return BadRequest(error_resp.to_json(), content="application/json")

def userinfo_endpoint(self, context):
def userinfo_endpoint(self, context, **kwargs):
headers = {"Authorization": context.request_authorization}

try:
Expand Down
4 changes: 2 additions & 2 deletions src/satosa/frontends/ping.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def __init__(self, auth_req_callback_func, internal_attributes, config, base_url

self.config = config

def handle_authn_response(self, context, internal_resp, extra_id_token_claims=None):
def handle_authn_response(self, context, internal_resp, extra_id_token_claims=None, **kwargs):
"""
See super class method satosa.frontends.base.FrontendModule#handle_authn_response
:type context: satosa.context.Context
Expand Down Expand Up @@ -47,7 +47,7 @@ def register_endpoints(self, backend_names):

return url_map

def ping_endpoint(self, context):
def ping_endpoint(self, context, **kwargs):
"""
"""
logprefix = PingFrontend.logprefix
Expand Down
24 changes: 12 additions & 12 deletions src/satosa/frontends/saml2.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def __init__(self, auth_req_callback_func, internal_attributes, config, base_url
self.KEY_CUSTOM_ATTR_RELEASE)
self.idp = None

def handle_authn_response(self, context, internal_response):
def handle_authn_response(self, context, internal_response, **kwargs):
"""
See super class method satosa.frontends.base.FrontendModule#handle_authn_response
:type context: satosa.context.Context
Expand All @@ -87,7 +87,7 @@ def handle_authn_response(self, context, internal_response):
"""
return self._handle_authn_response(context, internal_response, self.idp)

def handle_authn_request(self, context, binding_in):
def handle_authn_request(self, context, binding_in, **kwargs):
"""
This method is bound to the starting endpoint of the authentication.
Expand Down Expand Up @@ -174,7 +174,7 @@ def _validate_config(self, config):
except KeyError as e:
raise ValueError("Missing configuration key: %s" % key) from e

def _handle_authn_request(self, context, binding_in, idp):
def _handle_authn_request(self, context, binding_in, idp, **kwargs):
"""
See doc for handle_authn_request method.
Expand Down Expand Up @@ -286,7 +286,7 @@ def _filter_attributes(self, idp, internal_response, context,):

return attributes

def _handle_authn_response(self, context, internal_response, idp):
def _handle_authn_response(self, context, internal_response, idp, **kwargs):
"""
See super class satosa.frontends.base.FrontendModule
Expand Down Expand Up @@ -425,7 +425,7 @@ def _handle_backend_error(self, exception, idp):
satosa_logging(logger, logging.DEBUG, "HTTPargs: %s" % http_args, exception.state)
return make_saml_response(resp_args["binding"], http_args)

def _metadata_endpoint(self, context):
def _metadata_endpoint(self, context, **kwargs):
"""
Endpoint for retrieving the backend metadata
:type context: satosa.context.Context
Expand Down Expand Up @@ -589,7 +589,7 @@ def _load_endpoints_to_config(self, provider, target_entity_id, config=None):
idp_conf["service"]["idp"]["endpoints"][service] = idp_endpoints
return idp_conf

def _load_idp_dynamic_endpoints(self, context):
def _load_idp_dynamic_endpoints(self, context, **kwargs):
"""
Loads an idp server that accepts the target backend name in the endpoint url
ex: /<backend_name>/sso/redirect
Expand Down Expand Up @@ -621,7 +621,7 @@ def _load_idp_dynamic_entity_id(self, state):
idp_config = IdPConfig().load(idp_config_file, metadata_construction=False)
return Server(config=idp_config)

def handle_authn_request(self, context, binding_in):
def handle_authn_request(self, context, binding_in, **kwargs):
"""
Loads approved endpoints dynamically
See super class satosa.frontends.saml2.SAMLFrontend#handle_authn_request
Expand All @@ -637,7 +637,7 @@ def handle_authn_request(self, context, binding_in):
idp = self._load_idp_dynamic_endpoints(context)
return self._handle_authn_request(context, binding_in, idp)

def _create_state_data(self, context, resp_args, relay_state):
def _create_state_data(self, context, resp_args, relay_state, **kwargs):
"""
Adds the frontend idp entity id to state
See super class satosa.frontends.saml2.SAMLFrontend#save_state
Expand All @@ -661,7 +661,7 @@ def handle_backend_error(self, exception):
idp = self._load_idp_dynamic_entity_id(exception.state)
return self._handle_backend_error(exception, idp)

def handle_authn_response(self, context, internal_response):
def handle_authn_response(self, context, internal_response, **kwargs):
"""
See super class satosa.frontends.base.FrontendModule#handle_authn_response
:param context:
Expand Down Expand Up @@ -708,7 +708,7 @@ class SAMLVirtualCoFrontend(SAMLFrontend):
KEY_ORGANIZATION = 'organization'
KEY_ORGANIZATION_KEYS = ['display_name', 'name', 'url']

def handle_authn_request(self, context, binding_in):
def handle_authn_request(self, context, binding_in, **kwargs):
"""
See super class
satosa.frontends.saml2.SAMLFrontend#handle_authn_request
Expand All @@ -723,7 +723,7 @@ def handle_authn_request(self, context, binding_in):
idp = self._create_co_virtual_idp(context)
return self._handle_authn_request(context, binding_in, idp)

def handle_authn_response(self, context, internal_response):
def handle_authn_response(self, context, internal_response, **kwargs):
"""
See super class satosa.frontends.base.
FrontendModule#handle_authn_response
Expand All @@ -734,7 +734,7 @@ def handle_authn_response(self, context, internal_response):

return self._handle_authn_response(context, internal_response)

def _handle_authn_response(self, context, internal_response):
def _handle_authn_response(self, context, internal_response, **kwargs):
"""
"""
# Using the context of the current request and saved state from the
Expand Down
Loading

0 comments on commit da3f4e7

Please sign in to comment.