1

So I've setup Cognito and with boto3 I'm able to get AccessToken, IdToken and RefreshToken with the following code:

client = boto3.client('cognito-idp', region_name="us-east-1")
resp = client.initiate_auth(
    AuthFlow='USER_PASSWORD_AUTH',
    AuthParameters={
        'USERNAME': username,
        'PASSWORD': pwd,
        'SECRET_HASH': secret_hash
    },
    ClientId=app_client_id,
)

So far so good. But now I'm trying to access my resources like this:

# A dns entry pointing to a LoadBalancer using Cognito on port 443
aws_lb_endpoint = "https://my_endpoint.com/"
auth_endpoint = "https://myendpoint.auth.us-east-1.amazoncognito.com"

id_token = resp['AuthenticationResult']['IdToken']

r = requests.get(aws_lb_endpoint, headers={'Authorization': id_token})

But instead of getting my webpage behind Cognito, I get the Cognito login page in return:

<!DOCTYPE html>
<html lang="en">
<head><head>
    <link href="https://d3oia8etllorh5.cloudfront.net/20201215211355/css/bootstrap.min.css" rel="stylesheet"
        media="screen" />
    <link href="https://d3oia8etllorh5.cloudfront.net/20201215211355/css/cognito-login.css" rel="stylesheet"
        media="screen" />
    
    <title>Signin</title>

    <script src="https://d3oia8etllorh5.cloudfront.net/20201215211355/js/amazon-cognito-advanced-security-data.min.js" ></script>
    <script>
    function getAdvancedSecurityData(formReference) {
        if (typeof AmazonCognitoAdvancedSecurityData === "undefined") {
            return true;
        }

        // UserpoolId is not available on frontend for springboard. We do not use userPoolId
        // anyway other than put in context data. 
        var userPoolId = "";
        var clientId = getUrlParameter("client_id");
...

Here is the terraform code on how I connect to Cognito to the ALB:

resource "aws_lb_listener" "front_end_ssl" {
  load_balancer_arn = aws_lb.my_alb.arn
  port = "443"
  protocol = "HTTPS"
  ssl_policy = "ELBSecurityPolicy-2016-08"
  certificate_arn = var.certificate_arn

  default_action {
    type = "authenticate-cognito"

    authenticate_cognito {
      user_pool_arn       = aws_cognito_user_pool.my_pool.arn
      user_pool_client_id = aws_cognito_user_pool_client.webfront_pool_client.id
      user_pool_domain    = aws_cognito_user_pool_domain.my_pool_domain.domain
    }
  }

  default_action {
    type = "forward"
    target_group_arn = aws_lb_target_group.my_group_of_ec2_instances.arn
  }
}

resource "aws_cognito_user_pool_client" "webfront_pool_client" {
  name = "webfront_pool_client"

  user_pool_id = aws_cognito_user_pool.my_pool.id

  # Secret is mandatory here for the loadbalancer
  generate_secret = true
  prevent_user_existence_errors = "ENABLED"
  callback_urls = [
    "https://${aws_route53_record.server[0].fqdn}/oauth2/idpresponse"
  ]
  allowed_oauth_flows_user_pool_client = true

  # client_credentials would be ideal here for machine-to-machine communication. But we can't use client_credentials with Aws ALB
  allowed_oauth_flows = [
    "code"
  ]
  allowed_oauth_scopes = [
    "email",
    "openid"
  ]
  explicit_auth_flows = [
    "USER_PASSWORD_AUTH"
  ]
  supported_identity_providers = [
    "COGNITO"
  ]
}


Does anyone know how I can access my resources behind the Cognito login page with a REST request and using that IdToken? Thanks!

E-Kami
  • 2,129
  • 4
  • 25
  • 41

1 Answers1

1

Your code looks fine. If your application and Cognito are correctly configured, perhaps the problem could be that you are missing the Bearer prefix in your Authorization header when providing the id token.

Please, try:

r = requests.get(aws_lb_endpoint, headers={'Authorization': 'Bearer ' + id_token})

Please, also consider read this related SO question, I think it could be helpful.

jccampanero
  • 21,211
  • 2
  • 2
  • 26
  • I tried this already, I even tried with `r = requests.get(aws_lb_endpoint, headers={'Authorization': id_token})` but instead of getting what's behind the LB I get back the Cognito login page – E-Kami Jan 27 '21 at 11:11
  • I see @E-Kami. Please, be aware that, in any case, you probably need to provide the authorization header with the Bearer prefix as indicated. The problem will be then very likely in the Cognito or application setup. Please, can you describe in more detail your architecture? I mean, do you deployed the application in Beanstalk, for instance? Are you using API Gateway? – jccampanero Jan 27 '21 at 11:27
  • I just updated the question with all the details with terraform code – E-Kami Jan 27 '21 at 11:52
  • Sorry, I misunderstood your setup. It seems you are trying to perform Cognito authentication on ALB. In this kind of authorization the flow is different: please, see [this link](https://www.exampleloadbalancer.com/auth_detail.html), this [blog post](https://aws.amazon.com/es/blogs/aws/built-in-authentication-in-alb/#AuthenticationWalkthrough), and the [official documentation](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html). See next comment – jccampanero Jan 27 '21 at 12:22
  • As you can see, the authentication flow is a bit different, and many steps are carried out by the ALB itself. ALB will look for the `AWSELBAuthSessionCookie` session cookie created after successful user authentication, not for the JWT tokens returned by the IDP. This is probably the reason why it is redirecting you to the login page. I honestly didn't try the setup before, but I think the idea will include to look for the `AWSELBAuthSessionCookie` session cookie and include it in the following requests. Please, try to contact the AB, and analyze the response, and see if the cookie is defined – jccampanero Jan 27 '21 at 12:30
  • I see, thank you very much for this – E-Kami Jan 27 '21 at 12:40
  • You are welcome @E-Kami, thank you. Please, do not hesitate to contact me if you think that I can be of any help. – jccampanero Jan 27 '21 at 12:51
  • Were you able to figure this out? How did you get the AWSELBAuthSessionCookie cookie values? – Sukanya Dasgupta Feb 26 '21 at 13:05