Beispiel #1
0
    def token_endpoint(self, authn="", **kwargs):
        """
        This is where clients come to get their access tokens
        """

        _sdb = self.sdb

        logger.debug("- token -")
        body = kwargs["request"]
        logger.debug("body: %s" % body)

        areq = AccessTokenRequest().deserialize(body, "urlencoded")

        try:
            client_id = self.client_authn(self, areq, authn)
        except FailedAuthentication as err:
            err = TokenErrorResponse(error="unauthorized_client",
                                     error_description="%s" % err)
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        logger.debug("AccessTokenRequest: %s" % areq)

        _grant_type = areq["grant_type"]
        if _grant_type == "authorization_code":
            return self.code_grant_type(areq)
        elif _grant_type == 'client_credentials':
            return self.client_credentials_grant_type(areq)
        elif _grant_type == 'password':
            return self.password_grant_type(areq)
        elif _grant_type == 'refresh_token':
            return self.refresh_token_grant_type(areq)
        else:
            raise UnSupported('grant_type: {}'.format(_grant_type))
Beispiel #2
0
    def token_endpoint(self, authn="", **kwargs):
        """
        This is where clients come to get their access tokens
        """

        logger.debug("- token -")
        body = kwargs["request"]
        logger.debug("body: %s" % body)

        areq = AccessTokenRequest().deserialize(body, "urlencoded")

        try:
            self.client_authn(self, areq, authn)
        except FailedAuthentication as err:
            logger.error(err)
            err = TokenErrorResponse(error="unauthorized_client",
                                     error_description="%s" % err)
            return Response(err.to_json(),
                            content="application/json",
                            status_code=401)

        logger.debug("AccessTokenRequest: %s" % areq)

        _grant_type = areq["grant_type"]
        if _grant_type == "authorization_code":
            return self.code_grant_type(areq)
        elif _grant_type == 'client_credentials':
            return self.client_credentials_grant_type(areq)
        elif _grant_type == 'password':
            return self.password_grant_type(areq)
        elif _grant_type == 'refresh_token':
            return self.refresh_token_grant_type(areq)
        else:
            raise UnSupported('grant_type: {}'.format(_grant_type))
Beispiel #3
0
    def code_grant_type(self, areq):
        # assert that the code is valid
        try:
            _info = self.sdb[areq["code"]]
        except KeyError:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Unknown access grant")
            return Response(err.to_json(),
                            content="application/json",
                            status="401 Unauthorized")

        authzreq = json.loads(_info['authzreq'])
        if 'code_verifier' in areq:
            try:
                _method = authzreq['code_challenge_method']
            except KeyError:
                _method = 'S256'

            resp = self.verify_code_challenge(areq['code_verifier'],
                                              authzreq['code_challenge'],
                                              _method)
            if resp:
                return resp

        if 'state' in areq:
            if self.sdb[areq['code']]['state'] != areq['state']:
                err = TokenErrorResponse(error="unauthorized_client")
                return Unauthorized(err.to_json(), content="application/json")

        resp = self.token_scope_check(areq, _info)
        if resp:
            return resp

        # If redirect_uri was in the initial authorization request
        # verify that the one given here is the correct one.
        if "redirect_uri" in _info:
            assert areq["redirect_uri"] == _info["redirect_uri"]

        issue_refresh = False
        if 'scope' in authzreq and 'offline_access' in authzreq['scope']:
            if authzreq['response_type'] == 'code':
                issue_refresh = True

        try:
            _tinfo = self.sdb.upgrade_to_token(areq["code"],
                                               issue_refresh=issue_refresh)
        except AccessCodeUsed:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Access grant used")
            return Response(err.to_json(),
                            content="application/json",
                            status="401 Unauthorized")

        logger.debug("_tinfo: %s" % _tinfo)

        atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_tinfo))

        logger.debug("AccessTokenResponse: %s" % atr)

        return Response(atr.to_json(), content="application/json")
Beispiel #4
0
    def get_token_info(self, authn, req, endpoint):
        """

        :param authn:
        :param req:
        :return:
        """
        try:
            client_id = self.client_authn(self, req, authn)
        except FailedAuthentication as err:
            err = TokenErrorResponse(error="unauthorized_client",
                                     error_description="%s" % err)
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        logger.debug('{}: {} requesting {}'.format(endpoint, client_id,
                                                   req.to_dict()))

        try:
            token_type = req['token_type_hint']
        except KeyError:
            try:
                _info = self.sdb.token_factory['token'].info(req['token'])
            except KeyError:
                try:
                    _info = self.sdb.token_factory['refresh_token'].invalidate(
                        req['token'])
                except KeyError:
                    raise
                else:
                    token_type = 'refresh_token'
            else:
                token_type = 'token'
        else:
            try:
                _info = self.sdb.token_factory[token_type].invalidate(
                    req['token'])
            except KeyError:
                raise

        # simple rule: if client_id in azp or aud it's allow to introspect
        allow = False
        if client_id == _info['azr']:
            allow = True
        elif 'aud' in _info:
            if client_id in _info['aud']:
                allow = True

        if not allow:
            return BadRequest()

        return client_id, token_type, _info
Beispiel #5
0
    def get_token_info(self, authn, req, endpoint):
        """
        Parse token for information.

        :param authn:
        :param req:
        :return:
        """
        try:
            client_id = self.client_authn(self, req, authn)
        except FailedAuthentication as err:
            logger.error(err)
            error = TokenErrorResponse(error="unauthorized_client",
                                       error_description="%s" % err)
            return Response(error.to_json(),
                            content="application/json",
                            status="401 Unauthorized")

        logger.debug("{}: {} requesting {}".format(endpoint, client_id,
                                                   req.to_dict()))

        try:
            token_type = req["token_type_hint"]
        except KeyError:
            try:
                _info = self.sdb.token_factory["access_token"].get_info(
                    req["token"])
            except Exception:
                try:
                    _info = self.sdb.token_factory["refresh_token"].get_info(
                        req["token"])
                except Exception:
                    return self._return_inactive()
                else:
                    token_type = "refresh_token"
            else:
                token_type = "access_token"
        else:
            try:
                _info = self.sdb.token_factory[token_type].get_info(
                    req["token"])
            except Exception:
                return self._return_inactive()

        if not self.token_access(endpoint, client_id, _info):
            return BadRequest()

        return client_id, token_type, _info
Beispiel #6
0
    def get_token_info(self, authn, req, endpoint):
        """

        :param authn:
        :param req:
        :return:
        """
        try:
            client_id = self.client_authn(self, req, authn)
        except FailedAuthentication as err:
            logger.error(err)
            err = TokenErrorResponse(error="unauthorized_client",
                                     error_description="%s" % err)
            return Response(err.to_json(),
                            content="application/json",
                            status="401 Unauthorized")

        logger.debug('{}: {} requesting {}'.format(endpoint, client_id,
                                                   req.to_dict()))

        try:
            token_type = req['token_type_hint']
        except KeyError:
            try:
                _info = self.sdb.token_factory['access_token'].info(
                    req['token'])
            except KeyError:
                try:
                    _info = self.sdb.token_factory['refresh_token'].get_info(
                        req['token'])
                except KeyError:
                    raise
                else:
                    token_type = 'refresh_token'
            else:
                token_type = 'access_token'
        else:
            try:
                _info = self.sdb.token_factory[token_type].get_info(
                    req['token'])
            except KeyError:
                raise

        if not self.token_access(endpoint, client_id, _info):
            return BadRequest()

        return client_id, token_type, _info
Beispiel #7
0
    def get_token_info(self, authn, req, endpoint):
        """

        :param authn:
        :param req:
        :return:
        """
        try:
            client_id = self.client_authn(self, req, authn)
        except FailedAuthentication as err:
            logger.error(err)
            err = TokenErrorResponse(error="unauthorized_client",
                                     error_description="%s" % err)
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        logger.debug('{}: {} requesting {}'.format(endpoint, client_id,
                                                   req.to_dict()))

        try:
            token_type = req['token_type_hint']
        except KeyError:
            try:
                _info = self.sdb.token_factory['access_token'].info(
                    req['token'])
            except KeyError:
                try:
                    _info = self.sdb.token_factory['refresh_token'].get_info(
                        req['token'])
                except KeyError:
                    raise
                else:
                    token_type = 'refresh_token'
            else:
                token_type = 'access_token'
        else:
            try:
                _info = self.sdb.token_factory[token_type].get_info(
                    req['token'])
            except KeyError:
                raise

        if not self.token_access(endpoint, client_id, _info):
            return BadRequest()

        return client_id, token_type, _info
Beispiel #8
0
    def verify_code_challenge(
        code_verifier, code_challenge, code_challenge_method="S256"
    ):
        """
        Verify a PKCE (RFC7636) code challenge.

        :param code_verifier: The origin
        :param code_challenge: The transformed verifier used as challenge
        :return:
        """
        _h = CC_METHOD[code_challenge_method](code_verifier.encode("ascii")).digest()
        _cc = b64e(_h)
        if _cc.decode("ascii") != code_challenge:
            logger.error("PCKE Code Challenge check failed")
            err = TokenErrorResponse(
                error="invalid_request", error_description="PCKE check failed"
            )
            return Response(err.to_json(), content="application/json", status_code=401)
        return True
Beispiel #9
0
    def verify_code_challenge(code_verifier, code_challenge,
                              code_challenge_method='S256'):
        """
        Verify a PKCE (RFC7636) code challenge

        :param code_verifier: The origin
        :param code_challenge: The transformed verifier used as challenge
        :return:
        """
        _h = CC_METHOD[code_challenge_method](
            code_verifier.encode()).hexdigest()
        _cc = b64e(_h.encode())
        if _cc.decode() != code_challenge:
            logger.error('PCKE Code Challenge check failed')
            err = TokenErrorResponse(error="invalid_request",
                                     error_description="PCKE check failed")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")
        return True
Beispiel #10
0
    def password_grant_type(self, areq):
        """
        Token authorization using Resource owner password credentials.

        RFC6749 section 4.3
        """
        # `Any` comparison tries a first broker, so we either hit an IndexError or get a method
        try:
            authn, authn_class_ref = self.pick_auth(areq, "any")
        except IndexError:
            err = TokenErrorResponse(error="invalid_grant")
            return Unauthorized(err.to_json(), content="application/json")
        identity, _ts = authn.authenticated_as(
            username=areq["username"], password=areq["password"]
        )
        if identity is None:
            err = TokenErrorResponse(error="invalid_grant")
            return Unauthorized(err.to_json(), content="application/json")
        # We are returning a token
        areq["response_type"] = ["token"]
        authn_event = AuthnEvent(
            identity["uid"],
            identity.get("salt", ""),
            authn_info=authn_class_ref,
            time_stamp=_ts,
        )
        sid = self.setup_session(areq, authn_event, self.cdb[areq["client_id"]])
        _at = self.sdb.upgrade_to_token(self.sdb[sid]["code"], issue_refresh=True)
        atr_class = self.server.message_factory.get_response_type("token_endpoint")
        atr = atr_class(**by_schema(atr_class, **_at))
        return Response(
            atr.to_json(), content="application/json", headers=OAUTH2_NOCACHE_HEADERS
        )
Beispiel #11
0
    def rpt_token_endpoint(self, authn, request):
        areq = RPTRequest().deserialize(request, "json")

        try:
            client_id = self.srv["oauth"].client_authn(self.srv["oauth"], areq, authn)
        except FailedAuthentication as err:
            err = TokenErrorResponse(error="unauthorized_client", error_description="%s" % err)
            return Response(err.to_json(), content="application/json", status="401 Unauthorized")

        try:
            assert areq["grant_type"] == RQP_CLAIMS_GRANT_TYPE
        except AssertionError:
            err = TokenErrorResponse(error="invalid_request", error_description="Wrong grant type")
            return Response(err.to_json(), content="application/json", status="401 Unauthorized")

        requesting_party_uid = ""
        for rqp_claims in areq["claim_tokens"]:
            if rqp_claims["format"] == "json":
                claims = json.loads(base64.urlsafe_b64decode(rqp_claims["token"].encode("ascii")).decode("utf-8"))
                if "uid" in claims:
                    requesting_party_uid = claims["uid"]
                    break

        if not requesting_party_uid:
            err = TokenErrorResponse(error="invalid_request", error_description="No requesting party uid provided")
            return Response(err.to_json(), content="application/json", status="401 Unauthorized")

        return self.rpt_endpoint_(requesting_party_uid, client_id, request=request)
Beispiel #12
0
    def code_grant_type(self, areq):
        # assert that the code is valid
        try:
            _info = self.sdb[areq["code"]]
        except KeyError:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Unknown access grant")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        authzreq = json.loads(_info['authzreq'])
        if 'code_verifier' in areq:
            try:
                _method = authzreq['code_challenge_method']
            except KeyError:
                _method = 'S256'

            resp = self.verify_code_challenge(areq['code_verifier'],
                                              authzreq['code_challenge'],
                                              _method)
            if resp:
                return resp

        if 'state' in areq:
            if self.sdb[areq['code']]['state'] != areq['state']:
                logger.error('State value mismatch')
                err = TokenErrorResponse(error="unauthorized_client")
                return Unauthorized(err.to_json(), content="application/json")

        resp = self.token_scope_check(areq, _info)
        if resp:
            return resp

        # If redirect_uri was in the initial authorization request
        # verify that the one given here is the correct one.
        if "redirect_uri" in _info:
            assert areq["redirect_uri"] == _info["redirect_uri"]

        issue_refresh = False
        if 'scope' in authzreq and 'offline_access' in authzreq['scope']:
            if authzreq['response_type'] == 'code':
                issue_refresh = True

        try:
            _tinfo = self.sdb.upgrade_to_token(areq["code"],
                                               issue_refresh=issue_refresh)
        except AccessCodeUsed:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Access grant used")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        logger.debug("_tinfo: %s" % _tinfo)

        atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_tinfo))

        logger.debug("AccessTokenResponse: %s" % atr)

        return Response(atr.to_json(), content="application/json")
Beispiel #13
0
    def rpt_token_endpoint(self, authn, request):
        areq = RPTRequest().deserialize(request, "json")

        try:
            client_id = self.srv["oauth"].client_authn(self.srv["oauth"], areq,
                                                       authn)
        except FailedAuthentication as err:
            err = TokenErrorResponse(error="unauthorized_client",
                                     error_description="%s" % err)
            return Response(err.to_json(),
                            content="application/json",
                            status="401 Unauthorized")

        try:
            assert areq["grant_type"] == RQP_CLAIMS_GRANT_TYPE
        except AssertionError:
            err = TokenErrorResponse(error="invalid_request",
                                     error_description="Wrong grant type")
            return Response(err.to_json(),
                            content="application/json",
                            status="401 Unauthorized")

        requesting_party_uid = ""
        for rqp_claims in areq["claim_tokens"]:
            if rqp_claims["format"] == "json":
                claims = json.loads(
                    base64.urlsafe_b64decode(
                        rqp_claims["token"].encode("ascii")).decode("utf-8"))
                if "uid" in claims:
                    requesting_party_uid = claims["uid"]
                    break

        if not requesting_party_uid:
            err = TokenErrorResponse(
                error="invalid_request",
                error_description="No requesting party uid provided")
            return Response(err.to_json(),
                            content="application/json",
                            status="401 Unauthorized")

        return self.rpt_endpoint_(requesting_party_uid,
                                  client_id,
                                  request=request)
Beispiel #14
0
    def token_scope_check(areq, info):
        """
        verifies that the scope that is demanded for the access token is
        one that I'm comfortable with.

        :param areq: AccessTokenRequest
        :param info: What's in the session db
        :return: None if OK otherwise and error response.
        """
        try:
            assert areq["scope"] in info["scope"]
        except AssertionError:
            logger.error("Not the same scope as for the AuthzRequest")
            err = TokenErrorResponse(error="invalid_scope")
            return Response(err.to_json(), content="application/json")

        try:
            assert areq["scope"] in UMA_SCOPE.values()
        except AssertionError:
            logger.error("Asked for scope which I don't deal with")
            err = TokenErrorResponse(error="invalid_scope")
            return Response(err.to_json(), content="application/json")

        return None
Beispiel #15
0
    def token_endpoint(self, authn="", **kwargs):
        """
        This is where clients come to get their access tokens
        """

        _sdb = self.sdb

        logger.debug("- token -")
        body = kwargs["request"]
        logger.debug("body: %s" % body)

        areq = AccessTokenRequest().deserialize(body, "urlencoded")

        try:
            client = self.client_authn(self, areq, authn)
        except FailedAuthentication as err:
            err = TokenErrorResponse(error="unauthorized_client", error_description="%s" % err)
            return Response(err.to_json(), content="application/json", status="401 Unauthorized")

        logger.debug("AccessTokenRequest: %s" % areq)

        try:
            assert areq["grant_type"] == "authorization_code"
        except AssertionError:
            err = TokenErrorResponse(error="invalid_request", error_description="Wrong grant type")
            return Response(err.to_json(), content="application/json", status="401 Unauthorized")

        # assert that the code is valid
        _info = _sdb[areq["code"]]

        resp = self.token_scope_check(areq, _info)
        if resp:
            return resp

        # If redirect_uri was in the initial authorization request
        # verify that the one given here is the correct one.
        if "redirect_uri" in _info:
            assert areq["redirect_uri"] == _info["redirect_uri"]

        try:
            _tinfo = _sdb.upgrade_to_token(areq["code"], issue_refresh=True)
        except AccessCodeUsed:
            err = TokenErrorResponse(error="invalid_grant", error_description="Access grant used")
            return Response(err.to_json(), content="application/json", status="401 Unauthorized")

        logger.debug("_tinfo: %s" % _tinfo)

        atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_tinfo))

        logger.debug("AccessTokenResponse: %s" % atr)

        return Response(atr.to_json(), content="application/json")
Beispiel #16
0
    def token_scope_check(areq, info):
        """
        verifies that the scope that is demanded for the access token is
        one that I'm comfortable with.

        :param areq: AccessTokenRequest
        :param info: What's in the session db
        :return: None if OK otherwise and error response.
        """
        try:
            assert areq["scope"] in info["scope"]
        except AssertionError:
            logger.error("Not the same scope as for the AuthzRequest")
            err = TokenErrorResponse(error="invalid_scope")
            return Response(err.to_json(), content="application/json")

        try:
            assert areq["scope"] in list(UMA_SCOPE.values())
        except AssertionError:
            logger.error("Asked for scope which I don't deal with")
            err = TokenErrorResponse(error="invalid_scope")
            return Response(err.to_json(), content="application/json")

        return None
Beispiel #17
0
    def code_grant_type(self, areq):
        # assert that the code is valid
        try:
            _info = self.sdb[areq["code"]]
        except KeyError:
            err = TokenErrorResponse(
                error="invalid_grant", error_description="Unknown access grant"
            )
            return Response(
                err.to_json(), content="application/json", status="401 Unauthorized"
            )

        authzreq = json.loads(_info["authzreq"])
        if "code_verifier" in areq:
            try:
                _method = authzreq["code_challenge_method"]
            except KeyError:
                _method = "S256"

            resp = self.verify_code_challenge(
                areq["code_verifier"], authzreq["code_challenge"], _method
            )
            if resp:
                return resp

        if "state" in areq:
            if self.sdb[areq["code"]]["state"] != areq["state"]:
                logger.error("State value mismatch")
                err = TokenErrorResponse(error="unauthorized_client")
                return Unauthorized(err.to_json(), content="application/json")

        resp = self.token_scope_check(areq, _info)
        if resp:
            return resp

        # If redirect_uri was in the initial authorization request
        # verify that the one given here is the correct one.
        if "redirect_uri" in _info and areq["redirect_uri"] != _info["redirect_uri"]:
            logger.error("Redirect_uri mismatch")
            err = TokenErrorResponse(error="unauthorized_client")
            return Unauthorized(err.to_json(), content="application/json")

        issue_refresh = False
        if "scope" in authzreq and "offline_access" in authzreq["scope"]:
            if authzreq["response_type"] == "code":
                issue_refresh = True

        try:
            _tinfo = self.sdb.upgrade_to_token(
                areq["code"], issue_refresh=issue_refresh
            )
        except AccessCodeUsed:
            err = TokenErrorResponse(
                error="invalid_grant", error_description="Access grant used"
            )
            return Response(
                err.to_json(), content="application/json", status="401 Unauthorized"
            )

        logger.debug("_tinfo: %s" % _tinfo)

        atr_class = self.server.message_factory.get_response_type("token_endpoint")
        atr = atr_class(**by_schema(atr_class, **_tinfo))

        logger.debug("AccessTokenResponse: %s" % atr)

        return Response(
            atr.to_json(), content="application/json", headers=OAUTH2_NOCACHE_HEADERS
        )
Beispiel #18
0
    def token_endpoint(self, authn="", **kwargs):
        """
        This is where clients come to get their access tokens
        """

        _sdb = self.sdb

        logger.debug("- token -")
        body = kwargs["request"]
        logger.debug("body: %s" % body)

        areq = AccessTokenRequest().deserialize(body, "urlencoded")

        try:
            client_id = self.client_authn(self, areq, authn)
        except FailedAuthentication as err:
            err = TokenErrorResponse(error="unauthorized_client",
                                     error_description="%s" % err)
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        logger.debug("AccessTokenRequest: %s" % areq)

        # assert that the code is valid
        try:
            _info = _sdb[areq["code"]]
        except KeyError:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Unknown access grant")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        authzreq = json.loads(_info['authzreq'])
        if 'code_verifier' in areq:
            try:
                _method = authzreq['code_challenge_method']
            except KeyError:
                _method = 'S256'

            resp = self.verify_code_challenge(areq['code_verifier'],
                                              authzreq['code_challenge'],
                                              _method)
            if resp:
                return resp

        try:
            assert areq["grant_type"] == "authorization_code"
        except AssertionError:
            err = TokenErrorResponse(error="invalid_request",
                                     error_description="Wrong grant type")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        if 'state_hash' in areq:
            # have to get the token to get at the state
            code = areq['code']
            shash = base64.urlsafe_b64encode(
                hashlib.sha256(
                    self.sdb[code]['state'].encode('utf8')).digest())
            if shash.decode('ascii') != areq['state_hash']:
                err = TokenErrorResponse(error="unauthorized_client")
                return Unauthorized(err.to_json(), content="application/json")

        resp = self.token_scope_check(areq, _info)
        if resp:
            return resp

        # If redirect_uri was in the initial authorization request
        # verify that the one given here is the correct one.
        if "redirect_uri" in _info:
            assert areq["redirect_uri"] == _info["redirect_uri"]

        issue_refresh = False
        if 'scope' in authzreq and 'offline_access' in authzreq['scope']:
            if authzreq['response_type'] == 'code':
                issue_refresh = True

        try:
            _tinfo = _sdb.upgrade_to_token(areq["code"],
                                           issue_refresh=issue_refresh)
        except AccessCodeUsed:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Access grant used")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        logger.debug("_tinfo: %s" % _tinfo)

        atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_tinfo))

        logger.debug("AccessTokenResponse: %s" % atr)

        return Response(atr.to_json(), content="application/json")