diff --git a/oidc_example/op2/server.py b/oidc_example/op2/server.py index 1544836..dd2d4aa 100755 --- a/oidc_example/op2/server.py +++ b/oidc_example/op2/server.py @@ -398,6 +398,12 @@ if __name__ == '__main__': PASSWORD_END_POINT_INDEX) URLS.append((r'^' + end_point, make_auth_verify(authn.verify))) + # Ensure javascript_login_authn to be defined + try: + javascript_login_authn + except NameError: + javascript_login_authn = None + if "JavascriptLogin" == authkey: if not javascript_login_authn: end_points = config.AUTHENTICATION[ diff --git a/oidc_example/rp3/rp.py b/oidc_example/rp3/rp.py index 58441d8..0cc8f8a 100755 --- a/oidc_example/rp3/rp.py +++ b/oidc_example/rp3/rp.py @@ -161,7 +161,12 @@ def application(environ, start_response): logout_url += "&" + urllib.urlencode({ "id_token_hint": id_token_as_signed_jwt(client, _idtoken, "HS256")}) + # Also append the ACR values + logout_url += "&" + urllib.urlencode({"acr_values": ACR_VALUES}, + True) + LOGGER.debug("Logout URL: %s" % str(logout_url)) + LOGGER.debug("Logging out from session: %s" % str(session)) clear_session(session) resp = Redirect(str(logout_url)) return resp(environ, start_response) diff --git a/src/oic/oic/__init__.py b/src/oic/oic/__init__.py index 185cad5..ba93e21 100644 --- a/src/oic/oic/__init__.py +++ b/src/oic/oic/__init__.py @@ -51,7 +51,8 @@ from oic.utils.webfinger import OIC_ISSUER from oic.utils.webfinger import WebFinger from jwkest import jws -from jwkest.jws import alg2keytype, JWS +from jwkest import jwe +from jwkest.jws import JWS logger = logging.getLogger(__name__) @@ -303,6 +304,32 @@ class Client(oauth2.Client): return None + def request_object_encryption(self, msg, **kwargs): + try: + encalg = self.behaviour["request_object_encryption_alg"] + except KeyError: + return msg + else: + encenc = self.behaviour["request_object_encryption_enc"] + _jwe = JWE(msg, alg=encalg, enc=encenc) + _kty = jwe.alg2keytype(encalg) + + try: + _kid = kwargs["enc_kid"] + except KeyError: + try: + _kid = self.kid["enc"][_kty] + except KeyError: + _kid = "" + + if _kid: + _jwe["keys"] = self.keyjar.get_encrypt_key(_kty, kid=_kid) + _jwe["kid"] = _kid + else: + _jwe["keys"] = self.keyjar.get_signing_key(_kty) + + return _jwe.encrypt(self.keyjar) + def construct_AuthorizationRequest(self, request=AuthorizationRequest, request_args=None, extra_args=None, request_param=None, **kwargs): @@ -324,7 +351,9 @@ class Client(oauth2.Client): if "request_method" in kwargs: if kwargs["request_method"] == "file": request_param = "request_uri" - del kwargs["request_method"] + else: + request_param = "request" + del kwargs["request_method"] areq = oauth2.Client.construct_AuthorizationRequest(self, request, request_args, @@ -332,12 +361,16 @@ class Client(oauth2.Client): **kwargs) if request_param: - alg = self.behaviour["request_object_signing_alg"] - if "algorithm" not in kwargs: - kwargs["algorithm"] = alg + try: + alg = self.behaviour["request_object_signing_alg"] + except KeyError: + alg = None + else: + if "algorithm" not in kwargs: + kwargs["algorithm"] = alg if "keys" not in kwargs and alg: - _kty = alg2keytype(alg) + _kty = jws.alg2keytype(alg) try: kwargs["keys"] = self.keyjar.get_signing_key( _kty, kid=self.kid["sig"][_kty]) @@ -346,6 +379,9 @@ class Client(oauth2.Client): _req = make_openid_request(areq, **kwargs) + # Should the request be encrypted + _req = self.request_object_encryption(_req, **kwargs) + if request_param == "request": areq["request"] = _req else: @@ -712,7 +748,7 @@ class Client(oauth2.Client): return _schema().from_json(txt=resp.text) else: algo = self.client_prefs["userinfo_signed_response_alg"] - _kty = alg2keytype(algo) + _kty = jws.alg2keytype(algo) # Keys of the OP ? try: keys = self.keyjar.get_signing_key(_kty, self.kid["sig"][_kty]) diff --git a/src/oic/oic/consumer.py b/src/oic/oic/consumer.py index 17b443a..f51a788 100644 --- a/src/oic/oic/consumer.py +++ b/src/oic/oic/consumer.py @@ -159,6 +159,7 @@ class Consumer(Client): self.seed = "" self.nonce = "" self.request_filename = "" + self.request_uri = "" self.user_info = None self.registration_expires_at = 0 self.secret_type = "Bearer" diff --git a/src/oic/oic/provider.py b/src/oic/oic/provider.py index de24528..b0c19b8 100644 --- a/src/oic/oic/provider.py +++ b/src/oic/oic/provider.py @@ -403,13 +403,20 @@ class Provider(AProvider): if not comparision_type: comparision_type = "exact" + if not isinstance(areq["acr_values"], list): + areq["acr_values"] = [areq["acr_values"]] + for acr in areq["acr_values"]: res = self.authn_broker.pick(acr, comparision_type) + logger.debug("Picked AuthN broker for ACR %s: %s" % ( + str(acr), str(res))) if res: #Return the best guess by pick. return res[0] - except KeyError: - pass + except KeyError as exc: + logger.debug( + "An error occured while picking the authN broker: %s" % str( + exc)) # return the best I have return None, None @@ -422,8 +429,10 @@ class Provider(AProvider): client_info = self.cdb[self.sdb.getClient_id(uid)] if redirect_uri in client_info["post_logout_redirect_uris"]: return redirect_uri - except: - pass + except Exception as exc: + logger.debug( + "An error occurred while verifying redir URI: %s" % str(exc)) + return None def is_session_revoked(self, request="", cookie=None): @@ -460,7 +469,8 @@ class Provider(AProvider): esr = EndSessionRequest().from_urlencoded(request) redirect_uri = self.verify_post_logout_redirect_uri(esr, cookie) if not redirect_uri: - return self._error_response("Not allowed!") + return self._error_response( + "Not allowed (Post logout redirect URI verification failed)!") authn, acr = self.pick_auth(esr) @@ -474,7 +484,8 @@ class Provider(AProvider): try: uid = identity["uid"] except KeyError: - return self._error_response("Not allowed!") + return self._error_response( + "Not allowed (UID could not be retrieved)!") #if self.sdb.get_verified_logout(uid): # return self.let_user_verify_logout(uid, esr, cookie, redirect_uri) @@ -1335,11 +1346,19 @@ class Provider(AProvider): :param pcr_class: :return: ProviderConfigurationResponse instance """ + _scopes = SCOPE2CLAIMS.keys() + _scopes.append("openid") + + _claims = [] + for _cl in SCOPE2CLAIMS.values(): + _claims.extend(_cl) + _claims = list(set(_claims)) + _provider_info = pcr_class( token_endpoint_auth_methods_supported=[ "client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt"], - scopes_supported=["openid"], + scopes_supported=_scopes, response_types_supported=["code", "token", "id_token", "code token", "code id_token", "token id_token", @@ -1350,7 +1369,7 @@ class Provider(AProvider): "authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer"], claim_types_supported=["normal", "aggregated", "distributed"], - claims_supported=SCOPE2CLAIMS.keys(), + claims_supported=_claims, claims_parameter_supported="true", request_parameter_supported="true", request_uri_parameter_supported="true", diff --git a/src/oic/utils/authn/ldapc.py b/src/oic/utils/authn/ldapc.py index 96e73a3..5c8e330 100644 --- a/src/oic/utils/authn/ldapc.py +++ b/src/oic/utils/authn/ldapc.py @@ -12,7 +12,8 @@ SCOPE_MAP = { class LDAPAuthn(UsernamePasswordMako): def __init__(self, srv, ldapsrv, return_to, pattern, mako_template, - template_lookup, ldap_user="", ldap_pwd=""): + template_lookup, ldap_user="", ldap_pwd="", + verification_endpoints=["verify"]): """ :param srv: The server instance :param ldapsrv: Which LDAP server to us @@ -27,8 +28,9 @@ class LDAPAuthn(UsernamePasswordMako): that as. "" is a anonymous user :param ldap_pwd: The password for the ldap_user """ - UsernamePasswordMako.__init__(self, srv, mako_template, template_lookup, - None, return_to) + UsernamePasswordMako.__init__( + self, srv, mako_template, template_lookup, None, return_to, + verification_endpoints=verification_endpoints) self.ldap = ldap.initialize(ldapsrv) self.ldap.protocol_version = 3 diff --git a/src/oic/utils/sdb.py b/src/oic/utils/sdb.py index 9a43806..83eaadd 100644 --- a/src/oic/utils/sdb.py +++ b/src/oic/utils/sdb.py @@ -149,7 +149,7 @@ class SessionDB(object): try: sid = self.token.get_key(item) except Exception: - raise KeyError + raise KeyError("item '%s' could not be found" % str(item)) return self._db[sid] def __setitem__(self, key, value): @@ -159,6 +159,13 @@ class SessionDB(object): self._db[key] = value + def __delitem__(self, key): + """ + Actually delete the pointed session from this SessionDB instance + :param key: session identifier + """ + del self._db[key] + def keys(self): return self._db.keys()