Esempio n. 1
0
    def verify(self, request, **kwargs):
        _jwt = JWT(self.endpoint_context.keyjar)
        try:
            ca_jwt = _jwt.unpack(request["client_assertion"])
        except (Invalid, MissingKey, BadSignature) as err:
            logger.info("%s" % sanitize(err))
            raise AuthnFailure("Could not verify client_assertion.")

        try:
            logger.debug("authntoken: %s" % sanitize(ca_jwt.to_dict()))
        except AttributeError:
            logger.debug("authntoken: %s" % sanitize(ca_jwt))

        request[verified_claim_name("client_assertion")] = ca_jwt

        try:
            client_id = kwargs["client_id"]
        except KeyError:
            client_id = ca_jwt["iss"]

        # I should be among the audience
        # could be either my issuer id or the token endpoint
        if self.endpoint_context.issuer in ca_jwt["aud"]:
            pass
        elif self.endpoint_context.endpoint["token"].full_path in ca_jwt[
                "aud"]:
            pass
        else:
            raise NotForMe("Not for me!")

        return {"client_id": client_id, "jwt": ca_jwt}
Esempio n. 2
0
    def verify(self, request, **kwargs):
        """
        Verifies that the given username and password was correct
        :param request: Either the query part of a URL a urlencoded
        body of a HTTP message or a parse such.
        :param kwargs: Catch whatever else is sent.
        :return: redirect back to where ever the base applications
        wants the user after authentication.
        """

        logger.debug("verify(%s)" % sanitize(request))
        _dict = request

        logger.debug("dict: %s" % sanitize(_dict))
        # verify username and password
        try:
            self._verify(_dict["password"], _dict["login"])  # dict origin
        except TypeError:
            try:
                self._verify(_dict["password"][0], _dict["login"][0])
            except (AssertionError, KeyError) as err:
                logger.debug("Password verification failed: {}".format(err))
                return {'response', b"Unknown user or wrong password"}
            else:
                try:
                    _qp = _dict["query"]
                except KeyError:
                    _qp = self.get_multi_auth_cookie(kwargs['cookie'])
        except (AssertionError, KeyError) as err:
            logger.debug("Password verification failed: {}".format(err))
            return {'response': b"Unknown user or wrong password"}
        else:
            try:
                _qp = _dict["query"]
            except KeyError:
                _qp = self.get_multi_auth_cookie(kwargs['cookie'])

        logger.debug("Password verification succeeded.")
        # if "cookie" not in kwargs or self.endpoint_context.cookie_name not in
        # kwargs["cookie"]:
        headers = [self.cookie_dealer.create_cookie(_dict["login"], "upm")]
        try:
            return_to = self.generate_return_url(kwargs["return_to"], _qp)
        except KeyError:
            try:
                return_to = self.generate_return_url(self.return_to, _qp,
                                                     kwargs["path"])
            except KeyError:
                return_to = self.generate_return_url(self.return_to, _qp)

        return {'redirect': return_to, 'headers': headers}
Esempio n. 3
0
    def permissions(self, cookie=None, **kwargs):
        if cookie is None:
            return None
        else:
            logger.debug("kwargs: %s" % sanitize(kwargs))

            val = self.cookie_dealer.get_cookie_value(cookie)
            if val is None:
                return None
            else:
                uid, _ts, typ = val

            if typ == "uam":  # shortlived
                _now = int(time.time())
                if _now > (int(_ts) + int(self.cookie_dealer.cookie_ttl * 60)):
                    logger.debug("Authentication timed out")
                    raise ToOld("%d > (%d + %d)" %
                                (_now, int(_ts),
                                 int(self.cookie_dealer.cookie_ttl * 60)))
            else:
                if "max_age" in kwargs and kwargs["max_age"]:
                    _now = int(time.time())
                    if _now > (int(_ts) + int(kwargs["max_age"])):
                        logger.debug("Authentication too old")
                        raise ToOld("%d > (%d + %d)" %
                                    (_now, int(_ts), int(kwargs["max_age"])))

            return self.permdb[uid]
Esempio n. 4
0
    def _post_parse_request(self, request, client_id="", **kwargs):
        """
        This is where clients come to refresh their access tokens

        :param request: The request
        :param authn: Authentication info, comes from HTTP header
        :returns:
        """

        request = RefreshAccessTokenRequest(**request.to_dict())

        try:
            keyjar = self.endpoint_context.keyjar
        except AttributeError:
            keyjar = ""

        request.verify(keyjar=keyjar, opponent_id=client_id)

        if "client_id" not in request:  # Optional for refresh access token request
            request["client_id"] = client_id

        logger.debug("%s: %s" %
                     (request.__class__.__name__, sanitize(request)))

        return request
Esempio n. 5
0
    def _verify_sector_identifier(self, request):
        """
        Verify `sector_identifier_uri` is reachable and that it contains
        `redirect_uri`s.

        :param request: Provider registration request
        :return: si_redirects, sector_id
        :raises: InvalidSectorIdentifier
        """
        si_url = request["sector_identifier_uri"]
        try:
            res = self.endpoint_context.httpc.get(
                si_url, **self.endpoint_context.httpc_params)
            logger.debug("sector_identifier_uri => %s", sanitize(res.text))
        except Exception as err:
            logger.error(err)
            # res = None
            raise InvalidSectorIdentifier(
                "Couldn't read from sector_identifier_uri")

        try:
            si_redirects = json.loads(res.text)
        except ValueError:
            raise InvalidSectorIdentifier(
                "Error deserializing sector_identifier_uri content")

        if "redirect_uris" in request:
            logger.debug("redirect_uris: %s", request["redirect_uris"])
            for uri in request["redirect_uris"]:
                if uri not in si_redirects:
                    raise InvalidSectorIdentifier(
                        "redirect_uri missing from sector_identifiers")

        return si_redirects, si_url
Esempio n. 6
0
    def authenticated_as(self, cookie=None, **kwargs):
        if cookie is None:
            return None, 0
        else:
            logger.debug("kwargs: %s" % sanitize(kwargs))

            try:
                val = self.cookie_dealer.get_cookie_value(cookie)
            except (InvalidCookieSign, AssertionError) as err:
                logger.warning(err)
                val = None

            if val is None:
                return None, 0
            else:
                uid, _ts, typ = val

            if typ == "uam":  # short lived
                _ttl = self.cookie_dealer.ttl
                _now = int(time.time())
                if _now > (int(_ts) + int(_ttl * 60)):
                    logger.debug("Authentication timed out")
                    raise ToOld("%d > (%d + %d)" % (_now, int(_ts),
                                                    int(_ttl * 60)))
            else:
                if "max_age" in kwargs and kwargs["max_age"]:
                    _now = int(time.time())
                    if _now > (int(_ts) + int(kwargs["max_age"])):
                        logger.debug("Authentication too old")
                        raise ToOld("%d > (%d + %d)" % (
                            _now, int(_ts), int(kwargs["max_age"])))

            return {"uid": uid}, _ts
Esempio n. 7
0
    def _access_token_post_parse_request(self,
                                         request,
                                         client_id="",
                                         **kwargs):
        """
        This is where clients come to get their access tokens

        :param request: The request
        :param authn: Authentication info, comes from HTTP header
        :returns:
        """

        request = AccessTokenRequest(**request.to_dict())

        if "state" in request:
            try:
                sinfo = self.endpoint_context.sdb[request["code"]]
            except KeyError:
                logger.error("Code not present in SessionDB")
                return self.error_cls(error="unauthorized_client")
            else:
                state = sinfo["authn_req"]["state"]

            if state != request["state"]:
                logger.error("State value mismatch")
                return self.error_cls(error="unauthorized_client")

        if "client_id" not in request:  # Optional for access token request
            request["client_id"] = client_id

        logger.debug("%s: %s" %
                     (request.__class__.__name__, sanitize(request)))

        return request
Esempio n. 8
0
    def __call__(self, cookie=None, end_point_index=0, **kwargs):
        """
        Put up the login form
        """

        argv = self.templ_arg_func(end_point_index, **kwargs)
        logger.info("do_authentication argv: %s" % sanitize(argv))
        mte = self.template_lookup.get_template(self.mako_template)
        return {'response', mte.render(**argv).decode("utf-8")}
Esempio n. 9
0
def create_authn_response(endpoint, request, sid):
    """

    :param endpoint:
    :param request:
    :param sid:
    :return:
    """
    # create the response
    aresp = AuthorizationResponse()
    if request.get("state"):
        aresp["state"] = request["state"]

    if "response_type" in request and request["response_type"] == ["none"]:
        fragment_enc = False
    else:
        _context = endpoint.endpoint_context
        _sinfo = _context.sdb[sid]

        if request.get("scope"):
            aresp["scope"] = request["scope"]

        rtype = set(request["response_type"][:])
        handled_response_type = []

        fragment_enc = True
        if len(rtype) == 1 and "code" in rtype:
            fragment_enc = False

        if "code" in request["response_type"]:
            _code = aresp["code"] = _context.sdb[sid]["code"]
            handled_response_type.append("code")
        else:
            _context.sdb.update(sid, code=None)
            _code = None

        if "token" in rtype:
            _dic = _context.sdb.upgrade_to_token(issue_refresh=False, key=sid)

            logger.debug("_dic: %s" % sanitize(_dic))
            for key, val in _dic.items():
                if key in aresp.parameters() and val is not None:
                    aresp[key] = val

            handled_response_type.append("token")

        _access_token = aresp.get("access_token", None)

        not_handled = rtype.difference(handled_response_type)
        if not_handled:
            resp = AuthorizationErrorResponse(
                error="invalid_request",
                error_description="unsupported_response_type")
            return {"response_args": resp, "fragment_enc": fragment_enc}

    return {"response_args": aresp, "fragment_enc": fragment_enc}
Esempio n. 10
0
    def verify(self, request, **kwargs):
        _jwt = JWT(self.endpoint_context.keyjar)
        try:
            ca_jwt = _jwt.unpack(request["client_assertion"])
        except (Invalid, MissingKey, BadSignature) as err:
            logger.info("%s" % sanitize(err))
            raise AuthnFailure("Could not verify client_assertion.")

        authtoken = sanitize(ca_jwt)
        if hasattr(ca_jwt, "to_dict") and callable(ca_jwt, "to_dict"):
            authtoken = sanitize(ca_jwt.to_dict())
        logger.debug("authntoken: {}".format(authtoken))

        _endpoint = kwargs.get("endpoint")
        if _endpoint is None or not _endpoint:
            if self.endpoint_context.issuer in ca_jwt["aud"]:
                pass
            else:
                raise NotForMe("Not for me!")
        else:
            if set(ca_jwt["aud"]).intersection(
                    self.endpoint_context.endpoint[_endpoint].allowed_target_uris()):
                pass
            else:
                raise NotForMe("Not for me!")

        # If there is a jti use it to make sure one-time usage is true
        _jti = ca_jwt.get('jti')
        if _jti:
            _key = "{}:{}".format(ca_jwt['iss'], _jti)
            if _key in self.endpoint_context.jti_db:
                raise MultipleUsage("Have seen this token once before")
            else:
                self.endpoint_context.jti_db.set(_key, utc_time_sans_frac())

        request[verified_claim_name("client_assertion")] = ca_jwt
        client_id = kwargs.get("client_id") or ca_jwt["iss"]

        return {"client_id": client_id, "jwt": ca_jwt}
Esempio n. 11
0
    def permissions(self, cookie=None, **kwargs):
        if cookie is None:
            return None
        else:
            logger.debug("kwargs: %s" % sanitize(kwargs))

            val = self.cookie_dealer.get_cookie_value(cookie)
            if val is None:
                return None
            else:
                b64, _ts, typ = val

            info = cookie_value(b64)
            return self.get(info["sub"], info["client_id"])
Esempio n. 12
0
def create_return_url(base, query, **kwargs):
    """
    Add a query string plus extra parameters to a base URL which may contain
    a query part already.

    :param base: redirect_uri may contain a query part, no fragment allowed.
    :param query: Old query part as a string
    :param kwargs: extra query parameters
    :return: Constructed URL
    """
    part = urlsplit(base)
    if part.fragment:
        raise ValueError("Base URL contained parts it shouldn't")

    for key, values in parse_qs(query).items():
        if key in kwargs:
            if isinstance(kwargs[key], str):
                kwargs[key] = [kwargs[key]]
            kwargs[key].extend(values)
        else:
            kwargs[key] = values

    if part.query:
        for key, values in parse_qs(part.query).items():
            if key in kwargs:
                if isinstance(kwargs[key], str):
                    kwargs[key] = [kwargs[key]]
                kwargs[key].extend(values)
            else:
                kwargs[key] = values

        _url_base = base.split("?")[0]
    else:
        _url_base = base

    logger.debug("kwargs: %s" % sanitize(kwargs))
    if kwargs:
        return "%s?%s" % (_url_base, url_encode_params(kwargs))
    else:
        return _url_base
Esempio n. 13
0
    def _post_parse_request(self, request, client_id='', **kwargs):
        """
        This is where clients come to get their access tokens

        :param request: The request
        :param authn: Authentication info, comes from HTTP header
        :returns:
        """

        if 'state' in request:
            try:
                sinfo = self.endpoint_context.sdb[request['code']]
            except KeyError:
                logger.error('Code not present in SessionDB')
                return self.error_cls(error="unauthorized_client")
            else:
                state = sinfo['authn_req']['state']

            if state != request['state']:
                logger.error('State value mismatch')
                return self.error_cls(error="unauthorized_client")

        if "refresh_token" in request:
            request = RefreshAccessTokenRequest(**request.to_dict())

            try:
                keyjar = self.endpoint_context.keyjar
            except AttributeError:
                keyjar = ""

            request.verify(keyjar=keyjar, opponent_id=client_id)

        if "client_id" not in request:  # Optional for access token request
            request["client_id"] = client_id

        logger.debug("%s: %s" %
                     (request.__class__.__name__, sanitize(request)))

        return request
Esempio n. 14
0
    def _refresh_token_post_parse_request(self,
                                          request,
                                          client_id="",
                                          **kwargs):
        """
        This is where clients come to refresh their access tokens

        :param request: The request
        :param authn: Authentication info, comes from HTTP header
        :returns:
        """

        request = RefreshAccessTokenRequest(**request.to_dict())

        # verify that the request message is correct
        try:
            request.verify(keyjar=self.endpoint_context.keyjar)
        except (MissingRequiredAttribute, ValueError,
                MissingRequiredValue) as err:
            return self.error_cls(error="invalid_request",
                                  error_description="%s" % err)

        try:
            keyjar = self.endpoint_context.keyjar
        except AttributeError:
            keyjar = ""

        request.verify(keyjar=keyjar, opponent_id=client_id)

        if "client_id" not in request:  # Optional for refresh access token request
            request["client_id"] = client_id

        logger.debug("%s: %s" %
                     (request.__class__.__name__, sanitize(request)))

        return request
Esempio n. 15
0
    def client_registration_setup(self, request, new_id=True, set_secret=True):
        try:
            request.verify()
        except (MessageException, ValueError) as err:
            return ResponseMessage(error="invalid_configuration_request",
                                   error_description="%s" % err)

        request.rm_blanks()
        try:
            self.match_client_request(request)
        except CapabilitiesMisMatch as err:
            return ResponseMessage(
                error="invalid_request",
                error_description="Don't support proposed %s" % err,
            )

        _context = self.endpoint_context
        if new_id:
            # create new id och secret
            client_id = rndstr(12)
            # cdb client_id MUST be unique!
            while client_id in _context.cdb:
                client_id = rndstr(12)
            if "client_id" in request:
                del request["client_id"]
        else:
            client_id = request.get("client_id")
            if not client_id:
                raise ValueError("Missing client_id")

        _cinfo = {"client_id": client_id, "client_salt": rndstr(8)}

        if "registration_read" in self.endpoint_context.endpoint:
            self.add_registration_api(_cinfo, client_id, _context)

        if new_id:
            _cinfo["client_id_issued_at"] = utc_time_sans_frac()

        client_secret = ""
        if set_secret:
            client_secret = self.add_client_secret(_cinfo, client_id, _context)

        _context.cdb[client_id] = _cinfo
        _cinfo = self.do_client_registration(
            request,
            client_id,
            ignore=["redirect_uris", "policy_uri", "logo_uri", "tos_uri"],
        )
        if isinstance(_cinfo, ResponseMessage):
            return _cinfo

        args = dict([(k, v) for k, v in _cinfo.items()
                     if k in RegistrationResponse.c_param])

        comb_uri(args)
        response = RegistrationResponse(**args)

        # Add the client_secret as a symmetric key to the key jar
        if client_secret:
            _context.keyjar.add_symmetric(client_id, str(client_secret))

        _context.cdb[client_id] = _cinfo

        # Not all databases can be sync'ed
        if hasattr(_context.cdb, "sync") and callable(_context.cdb.sync):
            _context.cdb.sync()

        msg = "registration_response: {}"
        logger.info(msg.format(sanitize(response.to_dict())))

        return response
Esempio n. 16
0
def create_authn_response(endpoint_context, request, sid):
    # create the response
    aresp = AuthorizationResponse()
    try:
        aresp["state"] = request["state"]
    except KeyError:
        pass

    if "response_type" in request and request["response_type"] == ["none"]:
        fragment_enc = False
    else:
        _sinfo = endpoint_context.sdb[sid]

        try:
            aresp["scope"] = request["scope"]
        except KeyError:
            pass

        rtype = set(request["response_type"][:])
        handled_response_type = []
        if len(rtype) == 1 and "code" in rtype:
            fragment_enc = False
        else:
            fragment_enc = True

        if "code" in request["response_type"]:
            _code = aresp["code"] = endpoint_context.sdb[sid]["code"]
            handled_response_type.append("code")
        else:
            endpoint_context.sdb.update(sid, code=None)
            _code = None

        if "token" in rtype:
            _dic = endpoint_context.sdb.upgrade_to_token(issue_refresh=False,
                                                         key=sid)

            logger.debug("_dic: %s" % sanitize(_dic))
            for key, val in _dic.items():
                if key in aresp.parameters() and val is not None:
                    aresp[key] = val

            handled_response_type.append("token")

        try:
            _access_token = aresp["access_token"]
        except KeyError:
            _access_token = None

        if "id_token" in request["response_type"]:
            user_info = userinfo_in_id_token_claims(endpoint_context, _sinfo)
            if request["response_type"] == ["id_token"]:
                #  scopes should be returned here
                info = collect_user_info(endpoint_context, _sinfo)
                if user_info is None:
                    user_info = info
                else:
                    user_info.update(info)

            # client_info = endpoint_context.cdb[str(request["client_id"])]

            hargs = {}
            if {'code', 'id_token', 'token'}.issubset(rtype):
                hargs = {"code": _code, "access_token": _access_token}
            elif {'code', 'id_token'}.issubset(rtype):
                hargs = {"code": _code}
            elif {'id_token', 'token'}.issubset(rtype):
                hargs = {"access_token": _access_token}

            # or 'code id_token'
            try:
                id_token = sign_encrypt_id_token(endpoint_context, _sinfo,
                                                 str(request["client_id"]),
                                                 user_info=user_info,
                                                 sign=True, **hargs)
            except (JWEException, NoSuitableSigningKeys) as err:
                logger.warning(str(err))
                return AuthorizationErrorResponse(
                    error="invalid_request",
                    error_description="Could not sign/encrypt id_token")

            aresp["id_token"] = id_token
            _sinfo["id_token"] = id_token
            handled_response_type.append("id_token")

        not_handled = rtype.difference(handled_response_type)
        if not_handled:
            raise UnSupported("unsupported_response_type", list(not_handled))

    return {'response_args': aresp, 'fragment_enc': fragment_enc}
Esempio n. 17
0
def verify_redirect_uri(endpoint_context, request):
    """
    MUST NOT contain a fragment
    MAY contain query component

    :return: An error response if the redirect URI is faulty otherwise
        None
    """
    try:
        _redirect_uri = unquote(request["redirect_uri"])

        part = urlparse(_redirect_uri)
        if part.fragment:
            raise URIError("Contains fragment")

        (_base, _query) = splitquery(_redirect_uri)
        if _query:
            _query = parse_qs(_query)

        match = False
        for regbase, rquery in endpoint_context.cdb[str(request["client_id"])][
            "redirect_uris"]:

            # The URI MUST exactly match one of the Redirection URI
            if _base == regbase:
                # every registered query component must exist in the
                # redirect_uri
                if rquery:
                    for key, vals in rquery.items():
                        assert key in _query
                        for val in vals:
                            assert val in _query[key]
                # and vice versa, every query component in the redirect_uri
                # must be registered
                if _query:
                    if rquery is None:
                        raise ValueError
                    for key, vals in _query.items():
                        assert key in rquery
                        for val in vals:
                            assert val in rquery[key]
                match = True
                break
        if not match:
            raise RedirectURIError("Doesn't match any registered uris")
        # ignore query components that are not registered
        return None
    except Exception:
        logger.error("Faulty redirect_uri: %s" % request["redirect_uri"])
        try:
            _cinfo = endpoint_context.cdb[str(request["client_id"])]
        except KeyError:
            try:
                cid = request["client_id"]
            except KeyError:
                logger.error('No client id found')
                raise UnknownClient('No client_id provided')
            else:
                logger.info("Unknown client: %s" % cid)
                raise UnknownClient(request["client_id"])
        else:
            logger.info("Registered redirect_uris: %s" % sanitize(_cinfo))
            raise RedirectURIError(
                "Faulty redirect_uri: %s" % request["redirect_uri"])
Esempio n. 18
0
    def parse_request(self, request, auth=None, **kwargs):
        """

        :param request:
        :param endpoint_context:
        :param auth:
        :param kwargs:
        :return:
        """
        logger.debug("- {} -".format(self.endpoint_name))
        logger.info("Request: %s" % sanitize(request))

        if request:
            if isinstance(request, dict):
                req = self.request_cls(**request)
            else:
                if self.request_format == 'jwt':
                    req = self.request_cls().deserialize(
                        request,
                        "jwt",
                        keyjar=self.endpoint_context.keyjar,
                        verify=self.endpoint_context.verify_ssl,
                        **kwargs)
                elif self.request_format == 'url':
                    parts = urlparse(request)
                    scheme, netloc, path, params, query, fragment = parts[:6]
                    req = self.request_cls().deserialize(query, 'urlencoded')
                else:
                    req = self.request_cls().deserialize(
                        request, self.request_format)
        else:
            req = self.request_cls()

        # Verify that the client is allowed to do this
        _client_id = ''
        try:
            auth_info = self.client_authentication(req, auth, **kwargs)
        except UnknownOrNoAuthnMethod:
            if not self.client_auth_method:
                try:
                    _client_id = req['client_id']
                except KeyError:
                    _client_id = ''
            else:
                raise UnAuthorizedClient()
        else:
            if 'client_id' in auth_info:
                req['client_id'] = auth_info['client_id']
                _client_id = auth_info['client_id']
            else:
                try:
                    _client_id = req['client_id']
                except KeyError:
                    pass

        try:
            keyjar = self.endpoint_context.keyjar
        except AttributeError:
            keyjar = ""

        # verify that the request message is correct
        try:
            req.verify(keyjar=keyjar, opponent_id=_client_id)
        except (MissingRequiredAttribute, ValueError,
                MissingRequiredValue) as err:
            return self.error_cls(error="invalid_request",
                                  error_description="%s" % err)

        logger.info("Parsed and verified request: %s" % sanitize(req))

        # Do any endpoint specific parsing
        return self.do_post_parse_request(req, _client_id, **kwargs)
Esempio n. 19
0
    def do_client_registration(self, request, client_id, ignore=None):
        if ignore is None:
            ignore = []
        _context = self.endpoint_context
        _cinfo = _context.cdb[client_id].copy()
        logger.debug("_cinfo: %s" % sanitize(_cinfo))

        for key, val in request.items():
            if key not in ignore:
                _cinfo[key] = val

        if "post_logout_redirect_uris" in request:
            plruri = []
            for uri in request["post_logout_redirect_uris"]:
                if urlparse(uri).fragment:
                    err = ClientRegistrationErrorResponse(
                        error="invalid_configuration_parameter",
                        error_description="post_logout_redirect_uris "
                        "contains "
                        "fragment",
                    )
                    return err
                base, query = splitquery(uri)
                if query:
                    plruri.append((base, parse_qs(query)))
                else:
                    plruri.append((base, query))
            _cinfo["post_logout_redirect_uris"] = plruri

        if "redirect_uris" in request:
            try:
                ruri = self.verify_redirect_uris(request)
                _cinfo["redirect_uris"] = ruri
            except InvalidRedirectURIError as e:
                return ClientRegistrationErrorResponse(
                    error="invalid_redirect_uri", error_description=str(e))

        if "sector_identifier_uri" in request:
            try:
                _cinfo["si_redirects"], _cinfo[
                    "sector_id"] = self._verify_sector_identifier(request)
            except InvalidSectorIdentifier as err:
                return ResponseMessage(error="invalid_configuration_parameter",
                                       error_description=str(err))

        for item in ["policy_uri", "logo_uri", "tos_uri"]:
            if item in request:
                if verify_url(request[item], _cinfo["redirect_uris"]):
                    _cinfo[item] = request[item]
                else:
                    return ResponseMessage(
                        error="invalid_configuration_parameter",
                        error_description="%s pointed to illegal URL" % item,
                    )

        # Do I have the necessary keys
        for item in [
                "id_token_signed_response_alg", "userinfo_signed_response_alg"
        ]:
            if item in request:
                if request[item] in _context.provider_info[
                        PREFERENCE2PROVIDER[item]]:
                    ktyp = alg2keytype(request[item])
                    # do I have this ktyp and for EC type keys the curve
                    if ktyp not in ["none", "oct"]:
                        _k = []
                        for iss in ["", _context.issuer]:
                            _k.extend(
                                _context.keyjar.get_signing_key(
                                    ktyp, alg=request[item], owner=iss))
                        if not _k:
                            logger.warning('Lacking support for "{}"'.format(
                                request[item]))
                            del _cinfo[item]

        t = {"jwks_uri": "", "jwks": None}

        for item in ["jwks_uri", "jwks"]:
            if item in request:
                t[item] = request[item]

        # if it can't load keys because the URL is false it will
        # just silently fail. Waiting for better times.
        _context.keyjar.load_keys(client_id,
                                  jwks_uri=t["jwks_uri"],
                                  jwks=t["jwks"])
        n_keys = 0
        for kb in _context.keyjar.get(client_id, []):
            n_keys += len(kb.keys())
        msg = "found {} keys for client_id={}"
        logger.debug(msg.format(n_keys, client_id))

        return _cinfo
Esempio n. 20
0
def create_authn_response(endpoint, request, sid):
    """

    :param endpoint:
    :param request:
    :param sid:
    :return:
    """
    # create the response
    aresp = AuthorizationResponse()
    if request.get("state"):
        aresp["state"] = request["state"]

    if "response_type" in request and request["response_type"] == ["none"]:
        fragment_enc = False
    else:
        _context = endpoint.endpoint_context
        _sinfo = _context.sdb[sid]

        if request.get("scope"):
            aresp["scope"] = request["scope"]

        rtype = set(request["response_type"][:])
        handled_response_type = []

        fragment_enc = True
        if len(rtype) == 1 and "code" in rtype:
            fragment_enc = False

        if "code" in request["response_type"]:
            _code = aresp["code"] = _context.sdb[sid]["code"]
            handled_response_type.append("code")
        else:
            _context.sdb.update(sid, code=None)
            _code = None

        if "token" in rtype:
            _dic = _context.sdb.upgrade_to_token(issue_refresh=False, key=sid)

            logger.debug("_dic: %s" % sanitize(_dic))
            for key, val in _dic.items():
                if key in aresp.parameters() and val is not None:
                    aresp[key] = val

            handled_response_type.append("token")

        _access_token = aresp.get("access_token", None)

        if "id_token" in request["response_type"]:
            kwargs = {}
            if {"code", "id_token", "token"}.issubset(rtype):
                kwargs = {"code": _code, "access_token": _access_token}
            elif {"code", "id_token"}.issubset(rtype):
                kwargs = {"code": _code}
            elif {"id_token", "token"}.issubset(rtype):
                kwargs = {"access_token": _access_token}

            if request["response_type"] == ["id_token"]:
                kwargs["user_claims"] = True

            try:
                id_token = _context.idtoken.make(request, _sinfo, **kwargs)
            except (JWEException, NoSuitableSigningKeys) as err:
                logger.warning(str(err))
                resp = AuthorizationErrorResponse(
                    error="invalid_request",
                    error_description="Could not sign/encrypt id_token",
                )
                return {"response_args": resp, "fragment_enc": fragment_enc}

            aresp["id_token"] = id_token
            _sinfo["id_token"] = id_token
            handled_response_type.append("id_token")

        not_handled = rtype.difference(handled_response_type)
        if not_handled:
            resp = AuthorizationErrorResponse(
                error="invalid_request",
                error_description="unsupported_response_type")
            return {"response_args": resp, "fragment_enc": fragment_enc}

    return {"response_args": aresp, "fragment_enc": fragment_enc}
Esempio n. 21
0
    def parse_request(self, request, auth=None, **kwargs):
        """

        :param request: The request the server got
        :param auth: Client authentication information
        :param kwargs: extra keyword arguments
        :return:
        """
        LOGGER.debug("- {} -".format(self.endpoint_name))
        LOGGER.info("Request: %s" % sanitize(request))

        if request:
            if isinstance(request, (dict, Message)):
                req = self.request_cls(**request)
            else:
                _cls_inst = self.request_cls()
                if self.request_format == "jwt":
                    req = _cls_inst.deserialize(
                        request,
                        "jwt",
                        keyjar=self.endpoint_context.keyjar,
                        verify=self.endpoint_context.httpc_params["verify"],
                        **kwargs)
                elif self.request_format == "url":
                    parts = urlparse(request)
                    scheme, netloc, path, params, query, fragment = parts[:6]
                    req = _cls_inst.deserialize(query, "urlencoded")
                else:
                    req = _cls_inst.deserialize(request, self.request_format)
        else:
            req = self.request_cls()

        # Verify that the client is allowed to do this
        _client_id = ""
        try:
            auth_info = self.client_authentication(req,
                                                   auth,
                                                   endpoint=self.name,
                                                   **kwargs)
        except UnknownOrNoAuthnMethod:
            # If there is no required client authentication method
            if not self.client_authn_method:
                try:
                    _client_id = req["client_id"]
                except KeyError:
                    _client_id = ""
            else:
                raise
        else:
            if "client_id" in auth_info:
                req["client_id"] = auth_info["client_id"]
                _client_id = auth_info["client_id"]
            else:
                _client_id = req.get("client_id")

        keyjar = self.endpoint_context.keyjar

        # verify that the request message is correct
        try:
            req.verify(keyjar=keyjar, opponent_id=_client_id)
        except (MissingRequiredAttribute, ValueError,
                MissingRequiredValue) as err:
            return self.error_cls(error="invalid_request",
                                  error_description="%s" % err)

        LOGGER.info("Parsed and verified request: %s" % sanitize(req))

        # Do any endpoint specific parsing
        return self.do_post_parse_request(req, _client_id, **kwargs)
Esempio n. 22
0
def collect_user_info(endpoint_context,
                      session,
                      userinfo_claims=None,
                      scope_to_claims=None):
    """
    Collect information about a user.
    This can happen in two cases, either when constructing an IdToken or
    when returning user info through the UserInfo endpoint

    :param session: Session information
    :param userinfo_claims: user info claims
    :return: User info
    """
    authn_req = session["authn_req"]
    if scope_to_claims is None:
        scope_to_claims = endpoint_context.scope2claims

    supported_scopes = [
        s for s in authn_req["scope"]
        if s in endpoint_context.provider_info["scopes_supported"]
    ]

    if userinfo_claims is None:
        uic = scope2claims(supported_scopes, map=scope_to_claims)

        # Get only keys allowed by user and update the dict if such info
        # is stored in session
        perm_set = session.get("permission")
        if perm_set:
            uic = {key: uic[key] for key in uic if key in perm_set}

        uic = update_claims(session,
                            "userinfo",
                            provider_info=endpoint_context.provider_info,
                            old_claims=uic)

        if uic:
            userinfo_claims = Claims(**uic)
        else:
            userinfo_claims = None

        logger.debug("userinfo_claim: %s" %
                     sanitize(userinfo_claims.to_dict()))

    logger.debug("Session info: %s" % sanitize(session))

    authn_event = session["authn_event"]
    if authn_event:
        uid = authn_event["uid"]
    else:
        uid = session["uid"]

    info = endpoint_context.userinfo(uid, authn_req["client_id"],
                                     userinfo_claims)

    if "sub" in userinfo_claims:
        if not claims_match(session["sub"], userinfo_claims["sub"]):
            raise FailedAuthentication("Unmatched sub claim")

    info["sub"] = session["sub"]
    try:
        logger.debug("user_info_response: {}".format(info))
    except UnicodeEncodeError:
        logger.debug("user_info_response: {}".format(info.encode("utf-8")))

    return info