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))
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))
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")
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
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
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
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
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
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 )
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)
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")
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)
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
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")
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
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 )
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")