def handler(event, _context): """Handle the signout event.""" request = event['Records'][0]['cf']['request'] domain_name = request['headers']['host'][0]['value'] extracted = extract_and_parse_cookies( request['headers'], CONFIG.get('client_id') ) if not extracted.get('idToken'): return { 'body': "Bad Request", 'status': "400", 'statusDescription': "Bad Request", 'headers': CONFIG.get('cloud_front_headers') } tokens = { 'id_token': extracted.get('idToken'), 'access_token': extracted.get('accessToken'), 'refresh_token': extracted.get('refreshToken'), } query_string = { 'logout_uri': 'https://%s%s' % (domain_name, CONFIG.get('redirect_path_sign_out')), 'client_id': CONFIG.get('client_id') } headers = { # Redirect the user to logout 'location': [ { 'key': 'location', 'value': 'https://%s/logout?%s' % ( CONFIG.get('cognito_auth_domain'), urlencode(query_string) ) } ], 'set-cookie': get_cookie_headers( CONFIG.get("client_id"), CONFIG.get("oauth_scopes"), tokens, domain_name, CONFIG.get('cookie_settings'), # Make sure we expire all the tokens during retrieval expire_all_tokens=True ) } headers.update(CONFIG.get('cloud_front_headers')) return { 'status': '307', 'statusDescription': 'Temporary Redirect', 'headers': headers }
def handler(event, _context): """Handle the signout event.""" request = event["Records"][0]["cf"]["request"] domain_name = request["headers"]["host"][0]["value"] extracted = extract_and_parse_cookies(request["headers"], CONFIG.get("client_id")) if not extracted.get("idToken"): return { "body": "Bad Request", "status": "400", "statusDescription": "Bad Request", "headers": CONFIG.get("cloud_front_headers"), } tokens = { "id_token": extracted.get("idToken"), "access_token": extracted.get("accessToken"), "refresh_token": extracted.get("refreshToken"), } query_string = { "logout_uri": "https://%s%s" % (domain_name, CONFIG.get("redirect_path_sign_out")), "client_id": CONFIG.get("client_id"), } headers = { # Redirect the user to logout "location": [{ "key": "location", "value": "https://%s/logout?%s" % (CONFIG.get("cognito_auth_domain"), urlencode(query_string)), }], "set-cookie": get_cookie_headers( CONFIG.get("client_id"), CONFIG.get("oauth_scopes"), tokens, domain_name, CONFIG.get("cookie_settings"), # Make sure we expire all the tokens during retrieval expire_all_tokens=True, ), } headers.update(CONFIG.get("cloud_front_headers")) return { "status": "307", "statusDescription": "Temporary Redirect", "headers": headers, }
def handler(event, _context): """Handle the authorization refresh. Keyword Args: event: The Lambda Event """ request = event['Records'][0]['cf']['request'] domain_name = request['headers']['host'][0]['value'] redirected_from_uri = 'https://%s' % domain_name try: parsed_qs = parse_qs(request.get('querystring')) requested_uri = parsed_qs.get('requestedUri')[0] current_nonce = parsed_qs.get('nonce')[0] # Add the requested uri path to the main redirected_from_uri += requested_uri or "" cookies = extract_and_parse_cookies(request.get('headers'), CONFIG.get('client_id')) tokens = { 'id_token': cookies.get('idToken'), 'access_token': cookies.get('accessToken'), 'refresh_token': cookies.get('refreshToken') } validate_refresh_request(current_nonce, cookies.get('nonce'), tokens) try: # Request new tokens based on the refresh_token body = { 'grant_type': 'refresh_token', 'client_id': CONFIG.get('client_id'), 'refresh_token': tokens.get('refresh_token') } res = http_post_with_retry( ('https://%s/oauth2/token' % CONFIG.get('cognito_auth_domain')), body, {'Content-Type': 'application/x-www-form-urlencoded'}) tokens['id_token'] = res.get('id_token') tokens['access_token'] = res.get('access_token') except Exception as err: # pylint: disable=broad-except LOGGER.error(err) # Otherwise clear the refresh token tokens['refresh_token'] = "" headers = { 'location': [{ 'key': 'location', 'value': redirected_from_uri }], 'set-cookie': get_cookie_headers(CONFIG.get('client_id'), CONFIG.get('oauth_scopes'), tokens, domain_name, CONFIG.get('cookie_settings')) } headers.update(CONFIG.get('cloud_front_headers')) # Redirect the user back to their requested uri # with new tokens at hand return { 'status': '307', 'statusDescription': 'Temporary Redirect', 'headers': headers } # Send a basic html error response and inform the user # why refresh was unsuccessful except Exception as err: # pylint: disable=broad-except LOGGER.info(err) LOGGER.info(traceback.print_exc()) headers = { "content-type": [{ "key": "Content-Type", "value": "text/html; charset=UTF-8" }] } headers.update(CONFIG.get('cloud_front_headers')) return { 'body': create_error_html("Bad Request", err, redirected_from_uri), 'status': '400', 'headers': headers }
def handler(event, _context): """Handle the authorization parsing. Keyword Args: event (Any): The Lambda Event """ request = event['Records'][0]['cf']['request'] domain_name = request['headers']['host'][0]['value'] redirected_from_uri = 'https://%s' % domain_name # Attempt to parse the request and retrieve authorization # tokens to integrate with our header cookies try: qsp = parse_qs(request.get('querystring')) # Authorization code given by Cognito code = qsp.get('code') # The requested URI and current nonce information state = json.loads( base64.urlsafe_b64decode(qsp.get('state')[0]).decode()) # Missing required components if not code or not state: msg = 'Invalid query string. ' + \ 'Your query string should include parameters "state" and "code"' LOGGER.info(msg) raise Exception(msg) current_nonce = state.get('nonce') requested_uri = state.get('requestedUri') redirected_from_uri = requested_uri or "/" # Get all the cookies from the headers cookies = extract_and_parse_cookies(request.get('headers'), CONFIG.get('client_id')) # Retrieve the original nonce value as well as our PKCE code original_nonce = cookies.get('nonce') pkce = cookies.get('pkce') # If we're missing one of the nonces, or they don't match, cause an error if not current_nonce or not original_nonce or current_nonce != original_nonce: # No original nonce? CSRF violation if not original_nonce: msg = "Your browser didn't send the nonce cookie along, " + \ "but it is required for security (prevent CSRF)" LOGGER.error(msg) raise Exception(msg) # Nonce's don't match msg = "Nonce Mismatch" LOGGER.error(msg) raise Exception(msg) payload = { 'grant_type': 'authorization_code', 'client_id': CONFIG.get('client_id'), 'redirect_uri': 'https://%s%s' % (domain_name, CONFIG.get('redirect_path_sign_in')), 'code': code[0], 'code_verifier': pkce } # Request tokens from our Cognito Authorization Domain tokens = http_post_with_retry( ('https://%s/oauth2/token' % CONFIG.get('cognito_auth_domain')), payload, {"Content-Type": "application/x-www-form-urlencoded"}) if not tokens: raise Exception('Was not able to obtain tokens from Cognito') headers = { 'location': [{ 'key': 'location', 'value': redirected_from_uri }], 'set-cookie': get_cookie_headers(CONFIG.get("client_id"), CONFIG.get("oauth_scopes"), tokens, domain_name, CONFIG.get('cookie_settings')) } headers.update(CONFIG.get('cloud_front_headers')) # Redirect user to the originally requested uri with the # token header cookies response = { 'status': '307', 'statusDescription': 'Temporary Redirect', 'headers': headers } return response except Exception as err: # pylint: disable=broad-except LOGGER.error(err) LOGGER.error(traceback.print_exc()) headers = CONFIG.get('cloud_front_headers') headers['content-type'] = [{ 'key': 'Content-Type', 'value': 'text/html; charset=UTF-8' }] # Inform user of bad request and reason return { 'body': create_error_html("Bad Request", err, redirected_from_uri), 'status': '400', 'headers': headers }
def handler(event, _context): """Handle the authorization parsing. Args: event (Any): The Lambda Event. _context (Any): Lambda context object. """ request = event["Records"][0]["cf"]["request"] domain_name = request["headers"]["host"][0]["value"] redirected_from_uri = "https://%s" % domain_name # Attempt to parse the request and retrieve authorization # tokens to integrate with our header cookies try: qsp = parse_qs(request.get("querystring")) # Authorization code given by Cognito code = qsp.get("code") # The requested URI and current nonce information state = json.loads(base64.urlsafe_b64decode(qsp.get("state")[0]).decode()) # Missing required components if not code or not state: msg = ( "Invalid query string. " 'Your query string should include parameters "state" and "code"' ) LOGGER.info(msg) raise Exception(msg) current_nonce = state.get("nonce") requested_uri = state.get("requestedUri") redirected_from_uri = requested_uri or "/" # Get all the cookies from the headers cookies = extract_and_parse_cookies( request.get("headers"), CONFIG.get("client_id") ) # Retrieve the original nonce value as well as our PKCE code original_nonce = cookies.get("nonce") pkce = cookies.get("pkce") # If we're missing one of the nonces, or they don't match, cause an error if not current_nonce or not original_nonce or current_nonce != original_nonce: # No original nonce? CSRF violation if not original_nonce: msg = ( "Your browser didn't send the nonce cookie along, " "but it is required for security (prevent CSRF)" ) LOGGER.error(msg) raise Exception(msg) # Nonce's don't match msg = "Nonce Mismatch" LOGGER.error(msg) raise Exception(msg) payload = { "grant_type": "authorization_code", "client_id": CONFIG.get("client_id"), "redirect_uri": "https://%s%s" % (domain_name, CONFIG.get("redirect_path_sign_in")), "code": code[0], "code_verifier": pkce, } # Request tokens from our Cognito Authorization Domain tokens = http_post_with_retry( ("https://%s/oauth2/token" % CONFIG.get("cognito_auth_domain")), payload, {"Content-Type": "application/x-www-form-urlencoded"}, ) if not tokens: raise Exception("Was not able to obtain tokens from Cognito") headers = { "location": [{"key": "location", "value": redirected_from_uri}], "set-cookie": get_cookie_headers( CONFIG.get("client_id"), CONFIG.get("oauth_scopes"), tokens, domain_name, CONFIG.get("cookie_settings"), ), } headers.update(CONFIG.get("cloud_front_headers")) # Redirect user to the originally requested uri with the # token header cookies response = { "status": "307", "statusDescription": "Temporary Redirect", "headers": headers, } return response except Exception as err: # pylint: disable=broad-except LOGGER.error(err) LOGGER.error(traceback.print_exc()) headers = CONFIG.get("cloud_front_headers") headers["content-type"] = [ {"key": "Content-Type", "value": "text/html; charset=UTF-8"} ] # Inform user of bad request and reason return { "body": create_error_html("Bad Request", err, redirected_from_uri), "status": "400", "headers": headers, }
def handler(event, _context): """Handle the authorization refresh. Args: event: The Lambda Event. _context (Any): Lambda context object. """ request = event["Records"][0]["cf"]["request"] domain_name = request["headers"]["host"][0]["value"] redirected_from_uri = "https://%s" % domain_name try: parsed_qs = parse_qs(request.get("querystring")) requested_uri = parsed_qs.get("requestedUri")[0] current_nonce = parsed_qs.get("nonce")[0] # Add the requested uri path to the main redirected_from_uri += requested_uri or "" cookies = extract_and_parse_cookies( request.get("headers"), CONFIG.get("client_id") ) tokens = { "id_token": cookies.get("idToken"), "access_token": cookies.get("accessToken"), "refresh_token": cookies.get("refreshToken"), } validate_refresh_request(current_nonce, cookies.get("nonce"), tokens) try: # Request new tokens based on the refresh_token body = { "grant_type": "refresh_token", "client_id": CONFIG.get("client_id"), "refresh_token": tokens.get("refresh_token"), } res = http_post_with_retry( ("https://%s/oauth2/token" % CONFIG.get("cognito_auth_domain")), body, {"Content-Type": "application/x-www-form-urlencoded"}, ) tokens["id_token"] = res.get("id_token") tokens["access_token"] = res.get("access_token") except Exception as err: # pylint: disable=broad-except LOGGER.error(err) # Otherwise clear the refresh token tokens["refresh_token"] = "" headers = { "location": [{"key": "location", "value": redirected_from_uri}], "set-cookie": get_cookie_headers( CONFIG.get("client_id"), CONFIG.get("oauth_scopes"), tokens, domain_name, CONFIG.get("cookie_settings"), ), } headers.update(CONFIG.get("cloud_front_headers")) # Redirect the user back to their requested uri # with new tokens at hand return { "status": "307", "statusDescription": "Temporary Redirect", "headers": headers, } # Send a basic html error response and inform the user # why refresh was unsuccessful except Exception as err: # pylint: disable=broad-except LOGGER.info(err) LOGGER.info(traceback.print_exc()) headers = { "content-type": [ {"key": "Content-Type", "value": "text/html; charset=UTF-8"} ] } headers.update(CONFIG.get("cloud_front_headers")) return { "body": create_error_html("Bad Request", err, redirected_from_uri), "status": "400", "headers": headers, }