def providerinfo_endpoint(self, **kwargs): _log_info = logger.info _log_info("@providerinfo_endpoint") try: _response = self.create_providerinfo() _log_info("provider_info_response: %s" % (_response.to_dict(), )) headers = [("Cache-Control", "no-store"), ("x-ffo", "bar")] if 'handle' in kwargs: (key, timestamp) = kwargs['handle'] if key.startswith(STR) and key.endswith(STR): cookie = self.cookie_func(key, self.cookie_name, "pinfo", self.sso_ttl) headers.append(cookie) resp = Response(_response.to_json(), content="application/json", headers=headers) except Exception: message = traceback.format_exception(*sys.exc_info()) logger.error(message) resp = Response(message, content="html/text") return resp
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" % sanitize(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" % sanitize(areq)) if areq["grant_type"] != "authorization_code": 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" % sanitize(_tinfo)) atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_tinfo)) logger.debug("AccessTokenResponse: %s" % sanitize(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 """ 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 refresh_token_grant_type(self, areq): at = self.token_handler.refresh_access_token(self.baseurl, areq['access_token'], 'refresh_token') atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **at)) return Response(atr.to_json(), content="application/json")
def introspection_endpoint(self, authn='', request=None, **kwargs): """ Implements RFC7662 :param authn: Client Authentication information :param request: The introspection request :param kwargs: :return: """ tir = TokenIntrospectionRequest().deserialize(request, "urlencoded") resp = self.get_token_info(authn, tir, 'introspection_endpoint') if isinstance(resp, Response): return resp else: client_id, token_type, _info = resp logger.info('{} token introspection: {}'.format( client_id, tir.to_dict())) ir = TokenIntrospectionResponse( active=self.sdb.token_factory[token_type].is_valid(_info), **_info.to_dict()) ir.weed() return Response(ir.to_json(), content="application/json")
def token_endpoint(self, dtype='urlencoded', **kwargs): atr = AccessTokenRequest().deserialize(kwargs["request"], dtype) resp = super(PoPProvider, self).token_endpoint(**kwargs) if "token_type" not in atr or atr["token_type"] != "pop": return resp client_public_key = base64.urlsafe_b64decode( atr["key"].encode("utf-8")).decode("utf-8") pop_key = json.loads(client_public_key) atr = AccessTokenResponse().deserialize(resp.message, method="json") data = self.sdb.read(atr["access_token"]) jwt = { "iss": self.baseurl, "aud": self.baseurl, "exp": data["token_expires_at"], "nbf": int(time.time()), "cnf": { "jwk": pop_key } } _jws = JWS(jwt, alg="RS256").sign_compact( self.keyjar.get_signing_key(owner="")) self.access_tokens[_jws] = data["access_token"] atr["access_token"] = _jws atr["token_type"] = "pop" return Response(atr.to_json(), content="application/json")
def load_keys(self, request, client_id, client_secret): try: self.keyjar.load_keys(request, client_id) try: n_keys = len(self.keyjar[client_id]) msg = "Found {} keys for client_id={}" logger.debug(msg.format(n_keys, client_id)) except KeyError: pass except Exception as err: msg = "Failed to load client keys: {}" logger.error(msg.format(sanitize(request.to_dict()))) logger.error("%s", err) err = ClientRegistrationError( error="invalid_configuration_parameter", error_description="%s" % err) return Response(err.to_json(), content="application/json", status="400 Bad Request") # Add the client_secret as a symmetric key to the keyjar _kc = KeyBundle([{ "kty": "oct", "key": client_secret, "use": "ver" }, { "kty": "oct", "key": client_secret, "use": "sig" }]) try: self.keyjar[client_id].append(_kc) except KeyError: self.keyjar[client_id] = [_kc]
def __call__(self, cookie=None, end_point_index=0, **kwargs): """ Put up the login form """ # if cookie: # headers = [cookie] # else: # headers = [] resp = Response() 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) resp.message = mte.render(**argv).decode("utf-8") return resp
def revocation_endpoint(self, authn='', request=None, **kwargs): """ Implements RFC7009 allows a client to invalidate an access or refresh token. :param authn: Client Authentication information :param request: The revocation request :param kwargs: :return: """ trr = TokenRevocationRequest().deserialize(request, "urlencoded") resp = self.get_token_info(authn, trr, 'revocation_endpoint') if isinstance(resp, Response): return resp else: client_id, token_type, _info = resp logger.info('{} token revocation: {}'.format(client_id, trr.to_dict())) try: self.sdb.token_factory[token_type].invalidate(trr['token']) except KeyError: return BadRequest() else: return Response('OK')
def authz_error(error, descr=None, **kwargs): if 'status_code' in kwargs: warnings.warn('`status_code` kwarg is deprecated (and ignored) and will be removed in v0.14. ' 'If you need to set custom status_code, use `error_response` instead.', DeprecationWarning, stacklevel=2) response = AuthorizationErrorResponse(error=error) if descr: response["error_description"] = descr return Response(response.to_json(), content="application/json", status_code=400)
def error_response(error, descr=None, status=None, status_code=400): if status is not None: warnings.warn('`status` kwarg is deprecated, please use `status_code` instead.', DeprecationWarning, stacklevel=2) try: status_code = str(status[:3]) except ValueError: pass logger.error("%s" % sanitize(error)) response = ErrorResponse(error=error, error_description=descr) return Response(response.to_json(), content="application/json", status_code=status_code)
def claims_info_endpoint(self, request, authn): _log_info = logger.info _log_info("Claims_info_endpoint query: '%s'" % sanitize(request)) ucreq = self.srvmethod.parse_userinfo_claims_request(request) # _log_info("request: %s" % sanitize(ucreq)) # Bearer header or body access_token = bearer_auth(ucreq, authn) uiresp = OpenIDSchema(**self.info_store[access_token]) _log_info("returning: %s" % sanitize(uiresp.to_dict())) return Response(uiresp.to_json(), content="application/json")
def password_grant_type(self, areq): _at = self.token_handler.get_access_token(areq['client_id'], scope=areq['scope'], grant_type='password') _info = self.token_handler.token_factory.get_info(_at) try: _rt = self.token_handler.get_refresh_token(self.baseurl, _info['access_token'], 'password') except NotAllowed: atr = self.do_access_token_response(_at, _info, areq['state']) else: atr = self.do_access_token_response(_at, _info, areq['state'], _rt) return Response(atr.to_json(), content="application/json")
def client_info(self, client_id): _cinfo = self.cdb[client_id].copy() if not valid_client_info(_cinfo): err = ErrorResponse(error="invalid_client", error_description="Invalid client secret") return BadRequest(err.to_json(), content="application/json") try: _cinfo["redirect_uris"] = self._tuples_to_uris( _cinfo["redirect_uris"]) except KeyError: pass msg = ClientInfoResponse(**_cinfo) return Response(msg.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: 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 response(self, binding, http_args, query): cookie = self.create_cookie( '{"' + self.CONST_QUERY + '": "' + base64.b64encode(query.encode("ascii")).decode("ascii") + '" , "' + self.CONST_HASIDP + '": "True" }', self.CONST_SAML_COOKIE, self.CONST_SAML_COOKIE) if binding == BINDING_HTTP_ARTIFACT: resp = SeeOther() elif binding == BINDING_HTTP_REDIRECT: for param, value in http_args["headers"]: if param == "Location": resp = SeeOther(str(value), headers=[cookie]) break else: raise ServiceErrorException("Parameter error") else: http_args["headers"].append(cookie) resp = Response(http_args["data"], headers=http_args["headers"]) return resp
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 claims_endpoint(self, request, http_authz, *args): _log_info = logger.info ucreq = self.srvmethod.parse_user_claims_request(request) _log_info("request: %s" % sanitize(ucreq)) try: self.client_authn(self, ucreq, http_authz) except Exception as err: _log_info("Failed to verify client due to: %s" % err) if "claims_names" in ucreq: args = dict([(n, { "optional": True }) for n in ucreq["claims_names"]]) uic = Claims(**args) else: uic = None _log_info("User info claims: %s" % sanitize(uic)) # oicsrv, userdb, subject, client_id="", user_info_claims=None info = self.userinfo(ucreq["sub"], user_info_claims=uic, client_id=ucreq["client_id"]) _log_info("User info: %s" % sanitize(info)) # Convert to message format info = OpenIDSchema(**info) if self.do_aggregation(info, ucreq["sub"]): cresp = self._aggregation(info) else: cresp = self._distributed(info) _log_info("response: %s" % sanitize(cresp.to_dict())) return Response(cresp.to_json(), content="application/json")
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", headers=OAUTH2_NOCACHE_HEADERS)