Category Archives: AWS

Cognito Hosted UI User Pool – Google not showing

Hi everyone,

I’m implementing Cognito User Pools for an app and currently adding social providers (Google, Facebook, etc).

The setup process seems pretty straight forward, however the social options did not appear on my hosted ui.

It turned out that I’d missed the last step in the documentation:
– Go to “App Client Settings” (left hand menu under App integration)
– Look for “Enabled Identity Providers” and check any that you want to show

I found this a little unintuitive as I’d expected it to show once it was enabled in the “Identity Providers” section. I probably just need to learn to read ALL of the docs!

hosted-ui.JPG

The full documentation is available here: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-social-idp.html

And the bulk of the configuration is performed here:
google-congito

Advertisements

Access to fetch from origin has been blocked by CORS policy – AWS SAM Local

Hi everyone,

I’ve been using AWS SAM local lately and ran into a bit of an issue with CORS. It took a looong time to find a solution that worked for all of my local scenarios so hopefully this will be able to help someone else out.

Access to fetch at 'http://127.0.0.1:3000' from origin 'http://localhost:3001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

The error above is fairly typical when working with APIs. The request will work from tools such as postman and fiddler but browsers will block it. These are a few good links that explain why CORS is necessary:
https://medium.com/@electra_chong/what-is-cors-what-is-it-used-for-308cafa4df1a
https://stackoverflow.com/a/29167709/522859

As for the solution, add the following to your template.yml:

  PreFlightFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: functions/generic/cors.getPreflightHandler
      Runtime: nodejs8.10
      Events:
        GetEvent:
          Type: Api
          Properties:
            Path: /pages
            Method: options
            RestApiId: !Ref XXXApi
            Auth:
              Authorizer: NONE

If you haven’t already defined your api in your template.yml file there is a default/omitted one created for you. There are a few examples on the AWS github: https://github.com/awslabs/serverless-application-model/blob/release/v1.8.0/examples/2016-10-31/api_cognito_auth/template.yaml

The next thing to do is to create a handler for the options request:


/* Handles retrieving a specific page */
exports.getPreflightHandler = async (event, context, callback) => {
    callback(null, { body: {} }, headers: { 'content-type': 'application/json', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token', 'Access-Control-Allow-Methods': 'OPTIONS,GET,POST,PUT,PATCH,DELETE', 'Access-Control-Allow-Credentials': true }, statusCode: 200 })
};

I’ve moved all mine out to some helper methods but the above should be enough to get it working for you. Hopefully the AWS team will have a simpler solution soon but if you run into any issues in the meantime please let me know!

AWS SAM Request Extremely Slow – Fix

Hi everyone,

I’m currently using AWS SAM CLI with NodeJS and was surprised to find that the requests were significantly slower when run locally. Luckily, I came across a post that suggested adding –skip-pull-image to your start-api command:

sam local start-api --skip-pull-image

This brought my requests down to under a second. Thanks to the following link for the info: https://github.com/awslabs/aws-sam-cli/issues/134#issuecomment-406786704

this.query is not a function – error when using promisify with mysql

Hi everyone,

I ran into the following error while attempting to promisify my node.js mysql transactions:

TypeError: this.query is not a function
    at rollback (/var/task/node_modules/mysql/lib/Connection.js:179:15)
    at rollback (internal/util.js:230:26)

This took a while to track down but it turns out that I needed to bind the connection after promisifying the function. Instead of:

util.promisify(connection.query);

Add bind to the end of it:

util.promisify(connection.query).bind(connection)

Thanks to the following stackoverflow post for the insight: https://stackoverflow.com/a/51690276/522859

Overriding Global Variables – AWS SAM Local

Hi everyone,

Today I’ve added local overrides to the global variables in my template.yml file. This was pretty finicky – a lot of conflicting suggestions out there unfortunately. These are the settings that ended up working for me.

local-env-var-overrides.json:

{
    "Parameters": {
        "RDS_HOSTNAME_TEST": "192.168.0.31"
    }
}

template.yaml

Globals:
  Function:
    Environment:
      Variables:        
        RDS_HOSTNAME_TEST:
          Type: String
          Default: 'DEFAULT'

aws sam local command:

sam local start-api --env-vars "local-env-var-overrides.json"

create.js

exports.createTrapHandler = async (event, context, callback) => {

	try {

        console.log("RDS_HOSTNAME_TEST: " + process.env.RDS_HOSTNAME_TEST)
        // Outputs: 192.168.0.31

This link ended up being pretty useful: https://www.npmjs.com/package/aws-sam-local

Get User Id in Lambda node.js

Hi everyone,

A quick post on where to find the user id (sub) in a lambda requested that has been authenticated with a congito authorizer.

You’ll be able to find everything you need in the event object under requestContext > authorizer > claims:


exports.viewContextHandler = async (event, context, callback) => {
    console.log(JSON.stringify(event.requestContext));
}

"requestContext": {
    "resourceId": "XXXXX",
    "authorizer": {
        "claims": {
            "at_hash": "XXXXX",
            "sub": "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
            "aud": "XXXXX12341234512345XXXXX",
            "email_verified": "true",
            "token_use": "id",
            "auth_time": "1547371205",
            "iss": "https://cognito-XXXXX.com/XXXXX",
            "cognito:username": "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
            "exp": "Sun Jan 13 10:20:05 UTC 2019",
            "iat": "Sun Jan 13 09:20:05 UTC 2019",
            "email": "XXXXX@XXXXX.XXXXX"
        }
    },
}

Upload an Image to S3 Using Post and a Presigned Url

Hi everyone,

Today I’ve been converting my “PUT” upload to S3 to a “POST”. The main motivator for this was to restrict the file size of uploads using a signed policy. Unfortunately this was a pretty tedious process and the error responses from S3 were very vague. Thankfully it’s working now and here’s what I needed to do!

Here’s an excerpt from my nodejs lambda function that generates the presigned url:

/* Creates a pre-signed upload url and then stores a reference in a table */
exports.createUploadUrl = async params => {

    var { databaseHandler, bucketHandler } = params;

    // Create id for upload
    var uploadId = uuidv4();

    // Retrieve pre-signed url
    var bucketDataPromise = createPresignedPostPromise({
        Bucket: process.env.BUCKET_UPLOADS,
        Expires: 60 * 60,
        Conditions: [            
            ["content-length-range", 0, 300000], // 300kb
            [ "eq", "$key", uploadId],
            [ "eq", "$Content-Type", 'image/jpeg' ],
        ]
    });

    // var ddbData = await ddbDataPromise;
    var bucketData = await bucketDataPromise;

    // Wait for database handler to complete operation and then return
    return Helpers.generateResponse({
        data: {
            uploadData: bucketData,
            additionalFields: {
                key: uploadId,
                "Content-Type": 'image/jpeg',
            },
        },
        statusCode: 200
    });
}

You can then retrieve the upload details using a request like the following (Python):

resp = requests.put("https://f86caxqe9f.execute-api.ap-southeast-2.amazonaws.com/Prod/images", data=open(path, 'rb'))

This will return a response similar to the following:

{
    "messages": [],
    "data": {
        "uploadUrl": {
            "url": "YOUR UPLOAD URL",
            "fields": {
                "bucket": "YOUR BUCKET",
                "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
                "X-Amz-Credential": "XXX",
                "X-Amz-Date": "20190107T125044Z",
                "X-Amz-Security-Token": "SECURITY_TOKEN",
                "Policy": "YOUR BASE64 ENCODED POLICY",
                "X-Amz-Signature": "SIGNATURE"
            }
        },
        "uploadId": "UPLOAD_ID"
    }
}

And once you have those details you can use them to upload the image:

# Attempt to retrieve upload url etc
json = resp.json()
data = json["data"]
uploadUrl = data["uploadData"]["url"]
uploadFields = data["uploadData"]["fields"]
uploadFields.update(data["additionalFields"])

try:
    print("Uploading image...",end='')
    headers = {'Content-Type': 'multipart/form-data' }
    files = { 'file': open(path, 'rb')}
    resp = requests.post(uploadUrl, data=uploadFields, files=files)

    # Only show content if there's an error
    if resp.status_code == 200:
        print("Uploaded")
    else:  
        print("ERROR")                          
        print(repr(resp))
        print(repr(resp.content))

except Exception as e:
    print("\r\nFailed to upload image.")
    print("Upload data:")
    print(repr(uploadFields))
    print("Exception:")
    print(repr(e))
    traceback.print_exc()

Hopefully that’s been able to help you out, but feel free to let me know in the comments below if you need more info!

Thanks to these links for the info:
https://stackoverflow.com/a/35923104/522859
https://stackoverflow.com/a/49311831/522859