CodeWithYou

Allow user access to your API without authentication (Anonymous user access)

Published on
Authors
Allow user access to your API without authentication

Photo by Marek Okon

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

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.

// 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`,
})
Advertisement
  1. First, we create a user pool and user pool client. I have written the code in aws-cognito-anonymous-user-access-api

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.

// cognito identity providers
const identityPool = new cognito.CfnIdentityPool(this, 'CognitoIdentityProvider', {
  allowUnauthenticatedIdentities: true,
  cognitoIdentityProviders: [
    {
      clientId: client.userPoolClientId,
      providerName: (userPool as cognito.UserPool).userPoolProviderName,
    },
  ],
})
  1. Next, we create an HTTP API. You can see my article How to create an HTTP API in AWS Lambda to understand how to create an HTTP API.
  2. 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.
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

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

// 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. 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.

{
  "accessKey": "xxx",
  "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.

Advertisement