Allow user access to your API without authentication (Anonymous user access)
Tags: apigateway, cognito
Published: 2022-06-23 11:30:00


![Allow user access to your API without authentication (Anonymous user access)](https://www.codewithyou.com/static/images/aws-cognito-anonymous-user-access-api.jpg)

Photo by [Marek Okon](https://unsplash.com/@marekokon)

Amazon Cognito identity pools support both authenticated and unauthenticated users. Unauthenticated users receive access to your AWS resources even if they aren't logged in with any of your identity providers (IdPs). This degree of access is useful to display content to users before they log in. Each unauthenticated user has a unique identity in the identity pool, even though they haven't been individually logged in and authenticated.

This article describes the case where your user chooses to switch from logging in with an unauthenticated identity to using an authenticated identity.
We will demo the full solution for this case. You can get code for this solution from [GitHub](https://github.com/buithaibinh/aws-cognito-anonymous-user-access-api)

The demo has 2 parts:

1. Infrastructure: Setup the infrastructure for the demo.
2. /app This is a frontend application. write by Vue3

## Infrastructure: Setup the infrastructure for the demo.

```typescript
 // create a auth service, this will create a user pool and user pool client
    // it also creates 2 roles, one for unauthenticated and one for authenticated
    // the roles will be used to create the authorizer and the integration
    const auth = new AuthService(this, 'AuthService', {});

    // ==== HTTP API ====
    const api = new HttpApi(this, 'HttpApi', {
      description: 'This is a sample HTTP API',
      corsPreflight: {
        allowHeaders: [
          'Content-Type',
          'X-Amz-Date',
          'Authorization',
          'X-Api-Key',
          'X-Amz-Security-Token',
        ],
        allowMethods: [
          CorsHttpMethod.OPTIONS,
          CorsHttpMethod.GET,
          CorsHttpMethod.POST,
          CorsHttpMethod.PUT,
          CorsHttpMethod.PATCH,
          CorsHttpMethod.DELETE,
        ],
        allowCredentials: true,
        allowOrigins: ['http://localhost:8080'],
      },
    });
    // add http iam authorizer
    const authorizer = new apiGatewayAuthorizers.HttpIamAuthorizer();

    // lambda function
    const fn = new NodejsFunction(this, 'SampleFunction', {
      entry: './lambda/index.ts',
      runtime: lambda.Runtime.NODEJS_14_X,
      handler: 'main',
      architecture: lambda.Architecture.ARM_64,
      logRetention: logs.RetentionDays.ONE_WEEK,
      bundling: {
        externalModules: ['aws-sdk'],
        minify: true,
      },
    });

    // add route with lambda integration
    api.addRoutes({
      path: '/api/test',
      methods: [HttpMethod.GET],
      integration: new apiGatewayIntegrations.HttpLambdaIntegration(
        'fn-integration',
        fn,
        {}
      ),
      authorizer, // use IAM authorizer
    });

    // allow unauthenticated access to the API
    // This is very important for the API to work
    auth.unAuthRole.attachInlinePolicy(
      new iam.Policy(this, 'UnAuthPolicy', {
        statements: [
          new iam.PolicyStatement({
            actions: ['execute-api:*'],
            resources: [
              // allow unauthenticated access to the API /api
              // arn:aws:execute-api:region:account-id:api-id/stage/METHOD_HTTP_VERB/Resource-path
              `arn:aws:execute-api:${Stack.of(this).region}:*:*/*/*/api/*`,
            ],
          }),
        ],
      })
    );

    // output api url
    new CfnOutput(this, 'ApiUrl', {
      value: `${api.apiEndpoint}/api/test`,
    });
  }
```

1. First, we create a user pool and user pool client. I have written the code in [aws-cognito-anonymous-user-access-api](https://github.com/buithaibinh/aws-cognito-anonymous-user-access-api/blob/master/lib/auth-service.ts)

The use pool has 2 roles, one for unauthenticated and one for authenticated. The unauthenticated role will be used to create the authorizer and the integration. And a Cognito identity pool will be created. You must enable the feature allowUnauthenticatedIdentities in the identity pool.

```typescript
// cognito identity providers
const identityPool = new cognito.CfnIdentityPool(this, 'CognitoIdentityProvider', {
  allowUnauthenticatedIdentities: true,
  cognitoIdentityProviders: [
    {
      clientId: client.userPoolClientId,
      providerName: (userPool as cognito.UserPool).userPoolProviderName,
    },
  ],
})
```

2. Next, we create an HTTP API. You can see my article [How to create an HTTP API in AWS Lambda](https://www.codewithyou.com/blog/aws-lambda-proxy-integrations-for-http-apis-in-aws-cdk) to understand how to create an HTTP API.
3. Finally, we need to modify the unauthenticated role to allow unauthenticated access to the API. This step is very important for the API to work.

```typescript
auth.unAuthRole.attachInlinePolicy(
  new iam.Policy(this, 'UnAuthPolicy', {
    statements: [
      new iam.PolicyStatement({
        actions: ['execute-api:*'],
        resources: [
          // allow unauthenticated access to the API /api
          // arn:aws:execute-api:region:account-id:api-id/stage/METHOD_HTTP_VERB/Resource-path
          `arn:aws:execute-api:${Stack.of(this).region}:*:*/*/*/api/*`,
        ],
      }),
    ],
  })
)
```

## Frontend application.

We use AWS-amplify to create a frontend application. AWS Amplify is a JavaScript library for frontend and mobile developers building cloud-enabled applications. [You can see more here](https://github.com/aws-amplify/amplify-js)

Look into the file `App.vue` you can see my snippet code to make API call with the unauthenticated users (use AWS IAM authorizer).

```javaScript
// get current credentials. With aws-amplify, you can get current credentials from `Auth.currentUserCredentials`
const cre = await Auth.currentCredentials();
const signedRequest = sigV4Client
.newClient({
    accessKey: cre.accessKeyId,
    secretKey: cre.secretAccessKey,
    sessionToken: cre.sessionToken,
    region: awsExports.aws_cognito_region,
    endpoint: config.baseURL,
})
.signRequest({
    method: config.method,
    path: config.url,
    body: config.data,
});

```

`sigV4Client` is a helper class to sign requests. You can see more here [sigV4Client](https://github.com/AnomalyInnovations/sigV4Client). You need to sign the request before you send it to the API.

## Deploy to AWS

Deploy backend to AWS.

```
yarn deploy
```

After deploy, you can something export to file `cdk-outputs.json`.
Edit file `app/src/aws-exports.js` and replace the value of the key `apiUrl` with the value of the key `ApiUrl` in the file `cdk-outputs.json`...

## Testing

```
cd app
yarn install
yarn dev
```

The application will be running on localhost:8080. Open the browser and go to http://localhost:8080 and see the result.

```json
{
  "accessKey": "ASIA36FRBAMP6DINCSON",
  "accountId": "xxx",
  "callerId": "AROA36FRBAMPTRORUFVL7:CognitoIdentityCredentials",
  "cognitoIdentity": {
    "amr": ["unauthenticated"],
    "identityId": "ap-southeast-1:af38fa97-b29f-470b-8224-f58cc561e333",
    "identityPoolId": "ap-southeast-1:3cd835c2-19a8-40f5-8f14-2e30924a5fd3"
  },
  "principalOrgId": "aws:PrincipalOrgID",
  "userArn": "arn:aws:sts::xxxx:assumed-role/CdkStarterStackStack-AuthServiceIdentityPoolUnAuth-1GDNK5A20FXPC/CognitoIdentityCredentials",
  "userId": "AROA36FRBAMPTRORUFVL7:CognitoIdentityCredentials"
}
```

Wow, you can see the result of the API call without authentication.

## Clean up

Don't forget to clean up.

```
npx cdk destroy
```

Thanks for reading. I hope you find this article helpful. If you have any questions, please free leave a comment. I will try to answer your questions.