Amplify Gen 2 Users List

I'm read all the Amplify Gen 2 Documentation but I don't find how to list all registered users in application.

It's because need to create a admin page to list all users with his roles in Angular.

I think that probably can do this with lambda functions or something like that but I don't find nothing about that.

Thanks for all!

I'm read all the documentation: https://docs.amplify.aws/angular/build-a-backend/auth/connect-your-frontend/

Upvotes: 2

Views: 672

Answers (2)

You can implement a lambda function that uses the CognitoIdentityProviderClient from the aws-sdk to get the user list. And then define a custom query that calls the function:

data/resource/list-users-handler/handler.ts

import { env } from '$amplify/env/list-users-handler';
import type { Schema } from '../resource';
import {
  CognitoIdentityProviderClient,
  ListUsersCommand,
} from '@aws-sdk/client-cognito-identity-provider';

const client = new CognitoIdentityProviderClient();

export const handler: Schema['usersList']['functionHandler'] = async () => {
  const command = new ListUsersCommand({
    UserPoolId: env.AMPLIFY_AUTH_USERPOOL_ID,
  });

  const response = await client.send(command);

  // Ensure the return type of the function matches the one defined in the query
  const Users: Schema['UsersResponse']['type'] = {
    users:
      response.Users?.map((user) => {
        return {
          Username: user.Username || '',
          UserStatus: user.UserStatus || '',
          UserCreateDate: user.UserCreateDate?.toISOString() || '',
          UserLastModifiedDate: user.UserLastModifiedDate?.toISOString() || '',
          Enabled: user.Enabled || false,
          Attributes:
            user.Attributes?.map((attribute) => {
              return {
                Name: attribute.Name || '',
                Value: attribute.Value || '',
              };
            }) || [],
        };
      }) || [],
  };

  return Users;
};

data/resources.ts

import {
  type ClientSchema,
  a,
  defineData,
  defineFunction,
} from '@aws-amplify/backend';

export const usersListHandler = defineFunction({
  name: 'list-users-handler',
  entry: 'list-users-handler/handler.ts',
});

const schema = a.schema({
  UserAttribute: a.customType({
    Name: a.string(),
    Value: a.string(),
  }),

  User: a.customType({
    Attributes: a.ref('UserAttribute').array(),
    Enabled: a.boolean(),
    UserCreateDate: a.datetime(),
    UserLastModifiedDate: a.datetime(),
    UserStatus: a.string(),
    Username: a.string(),
  }),

  UsersResponse: a.customType({
    users: a.ref('User').array(),
  }),

  usersList: a
    .query()
    // return type of the query
    .returns(a.ref('UsersResponse'))
    // only allow users in the ADMIN group to call the API
    .authorization((allow) => [allow.group('ADMINS')])
    .handler(a.handler.function(usersListHandler)),
});

export type Schema = ClientSchema<typeof schema>;

// Set the authorization mode to user pool
export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'userPool',
  },
});

Then you need to authorize the lambda function so it can get the user list from the cognito user pool.

auth/resource.ts

import { defineAuth } from '@aws-amplify/backend';
import { usersListHandler } from '../data/resource';

export const auth = defineAuth({
  loginWith: {
    email: true,
  },
  userAttributes: {
    givenName: {
      required: true,
      mutable: true,
    },
    familyName: {
      required: true,
      mutable: true,
    },
  },
  groups: ['ADMINS', 'USERS'],
  access: (allow) => [
    allow.resource(usersListHandler).to(['listUsers']),
  ],
});

backend.ts

import { defineBackend } from '@aws-amplify/backend';

import { auth } from './auth/resource';
import { data } from './data/resource';

const backend = defineBackend({
  auth,
  data,
});

Finally you can acces the query API from the frontend like this:

import { generateClient } from 'aws-amplify/api';

const client = generateClient<Schema>();

const { data, errors } = await client.queries.usersList({
  authMode: 'userPool',
});

if (errors) {
  console.error(errors);
} else if (data) {
  console.log(data);
} else {
  console.warn('No data or errors found');
}

Upvotes: 0

The solution is create custom function that return all users by Cognito Pool and assign manually the permission to this function:

cognito-idp:ListUsers

backend.ts

import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';

import * as iam from "aws-cdk-lib/aws-iam";
import {listUsers} from "./data/list-users/resource";
import {PolicyStatement} from "aws-cdk-lib/aws-iam";

const backend =  defineBackend({
  auth,
  data,
  listUsers

});

const lambdaFunction = backend.listUsers.resources.lambda;
lambdaFunction.role?.attachInlinePolicy(
 new iam.Policy(backend.auth.resources.userPool, "AllowListUsers", {
  statements: [
   new iam.PolicyStatement({
    actions: ["cognito-idp:ListUsers"],
    resources: [backend.auth.resources.userPool.userPoolArn],
  }),
  ],
 })
);

auth/resource.ts

import { defineAuth } from '@aws-amplify/backend';
import {addUserToGroup} from "../data/add-user-to-group/resource";
import {listUsers} from "../data/list-users/resource";

export const auth = defineAuth({
  loginWith: {
   email: true,
},
groups: ["ADMINS"],
access: (allow) => [
  allow.resource(addUserToGroup).to(["addUserToGroup"]),
  allow.resource(listUsers).to(["manageUsers"]),
 ],
});

data/list-users/resource.ts

import { defineFunction } from "@aws-amplify/backend"

export const listUsers = defineFunction({
 name: "list-users",
})

data/list-users/handler.ts

import { env } from "$amplify/env/list-users"
import type { Schema } from "../resource"
import { CognitoIdentityProviderClient, ListUsersCommand } from "@aws-sdk/client-cognito-identity-provider";

type Handler = Schema["listUsers"]["functionHandler"]
const client = new CognitoIdentityProviderClient()

export const handler: Handler = async (event) => {
  const command = new ListUsersCommand({ UserPoolId: env.AMPLIFY_AUTH_USERPOOL_ID });


  const response = await client.send(command)
  return response;
}

Upvotes: 0

Related Questions