Esempio n. 1
0
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
    }
Esempio n. 2
0
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,
    }
Esempio n. 3
0
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
        }
Esempio n. 4
0
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
        }
Esempio n. 5
0
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,
        }
Esempio n. 6
0
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,
        }