Page updated Nov 14, 2023

Set up admin actions

Admin Actions allow you to execute queries and operations against users and groups in your Cognito user pool.

For example, the ability to list all users in a Cognito User Pool may provide useful for the administrative panel of an app if the logged-in user is a member of a specific Group called "Admins".

This is an advanced feature that is not recommended without an understanding of the underlying architecture. The associated infrastructure which is created is a base designed for you to customize for your specific business needs. We recommend removing any functionality which your app does not require.

The Amplify CLI can setup a REST endpoint with secure access to a Lambda function running with limited permissions to the User Pool if you wish to have these capabilities in your application, and you can choose to expose the actions to all users with a valid account or restrict to a specific User Pool Group.

Enable Admin Queries

amplify add auth
1amplify add auth

Select the option to go through Manual configuration.

Do you want to use the default authentication and security configuration? (Use arrow keys) Default configuration Default configuration with Social Provider (Federation) ❯ Manual configuration I want to learn more.
1Do you want to use the default authentication and security configuration? (Use arrow keys)
2 Default configuration
3 Default configuration with Social Provider (Federation)
4❯ Manual configuration
5 I want to learn more.

Go through the rest of the configuration steps until you reach the following prompts:

? Do you want to add User Pool Groups? Yes ? Provide a name for your user pool group: Admins ? Do you want to add another User Pool Group No ✔ Sort the user pool groups in order of preference · Admins ? Do you want to add an admin queries API? Yes ? Do you want to restrict access to the admin queries API to a specific Group? Yes ? Select the group to restrict access with: (Use arrow keys) ❯ Admins Enter a custom group
1? Do you want to add User Pool Groups? Yes
2? Provide a name for your user pool group: Admins
3? Do you want to add another User Pool Group No
4✔ Sort the user pool groups in order of preference · Admins
5? Do you want to add an admin queries API? Yes
6? Do you want to restrict access to the admin queries API to a specific Group? Yes
7? Select the group to restrict access with: (Use arrow keys)
8❯ Admins
9 Enter a custom group

Continue with the rest of the prompts to finish the configuration.

If you don't have any User Pool Groups, you will need to select Enter a custom group.

When ready, run amplify push to deploy the changes.

This will configure an API Gateway endpoint with a Cognito Authorizer that accepts an Access Token, which is used by a Lambda function to perform actions against the User Pool. The function is example code which you can use to remove, add, or alter functionality based on your business case by editing it in the amplify/backend/function/AdminQueriesXXX/src directory and running an amplify push to deploy your changes. If you choose to restrict actions to a specific Group, custom middleware in the function will prevent any actions unless the user is a member of that Group.

Admin Queries API

The default routes and their functions, HTTP methods, and expected parameters are below

  • addUserToGroup: Adds a user to a specific Group. Expects username and groupname in the POST body.
  • removeUserFromGroup: Removes a user from a specific Group. Expects username and groupname in the POST body.
  • confirmUserSignUp: Confirms a users signup. Expects username in the POST body.
  • disableUser: Disables a user. Expects username in the POST body.
  • enableUser: Enables a user. Expects username in the POST body.
  • getUser: Gets specific user details. Expects username as a GET query string.
  • listUsers: Lists all users in the current Cognito User Pool. You can provide an OPTIONAL limit (between 0 and 60) as a GET query string, which returns a NextToken that can be provided as a token query string for pagination.
  • listGroups: Lists all groups in the current Cognito User Pool. You can provide an OPTIONAL limit (between 0 and 60) as a GET query string, which returns a NextToken that can be provided as a token query string for pagination.
  • listGroupsForUser: Lists groups to which current user belongs to. Expects username as a GET query string. You can provide an OPTIONAL limit (between 0 and 60) as a GET query string, which returns a NextToken that can be provided as a token query string for pagination.
  • listUsersInGroup: Lists users that belong to a specific group. Expects groupname as a GET query string. You can provide an OPTIONAL limit (between 0 and 60) as a GET query string, which returns a NextToken that can be provided as a token query string for pagination.
  • signUserOut: Signs a user out from User Pools, but only if the call is originating from that user. Expects username in the POST body.

Example

To leverage this functionality in your app you would call the appropriate route from Amplify.API after signing in. The following example adds the user "richard" to the Editors Group and then list all members of the Editors Group with a pagination limit of 10:

import React from 'react' import { Amplify } from 'aws-amplify'; import { fetchAuthSession } from 'aws-amplify/auth'; import { post } from 'aws-amplify/api' import { withAuthenticator } from '@aws-amplify/ui-react'; import '@aws-amplify/ui-react/styles.css'; import amplifyconfig from './amplifyconfiguration.json'; Amplify.configure(amplifyconfig); const client = generateClient() async function addToGroup() { let apiName = 'AdminQueries'; let path = '/addUserToGroup'; let options = { body: { "username" : "richard", "groupname": "Editors" }, headers: { 'Content-Type' : 'application/json', Authorization: `${(await fetchAuthSession()).tokens.accessToken.payload}` } } return post({apiName, path, options}); } async function listEditors(limit){ let apiName = 'AdminQueries'; let path = '/listUsersInGroup'; let options = { queryStringParameters: { "groupname": "Editors", "limit": limit, }, headers: { 'Content-Type' : 'application/json', Authorization: `${(await fetchAuthSession()).tokens.accessToken.payload}` } } const response = await get({apiName, path, options}); return response; } function App() { return ( <div className="App"> <button onClick={addToGroup}>Add to Group</button> <button onClick={() => listEditors(10)}>List Editors</button> </div> ); } export default withAuthenticator(App);
1import React from 'react'
2import { Amplify } from 'aws-amplify';
3import { fetchAuthSession } from 'aws-amplify/auth';
4import { post } from 'aws-amplify/api'
5import { withAuthenticator } from '@aws-amplify/ui-react';
6import '@aws-amplify/ui-react/styles.css';
7
8import amplifyconfig from './amplifyconfiguration.json';
9Amplify.configure(amplifyconfig);
10
11const client = generateClient()
12
13async function addToGroup() {
14 let apiName = 'AdminQueries';
15 let path = '/addUserToGroup';
16 let options = {
17 body: {
18 "username" : "richard",
19 "groupname": "Editors"
20 },
21 headers: {
22 'Content-Type' : 'application/json',
23 Authorization: `${(await fetchAuthSession()).tokens.accessToken.payload}`
24 }
25 }
26 return post({apiName, path, options});
27}
28
29async function listEditors(limit){
30 let apiName = 'AdminQueries';
31 let path = '/listUsersInGroup';
32 let options = {
33 queryStringParameters: {
34 "groupname": "Editors",
35 "limit": limit,
36 },
37 headers: {
38 'Content-Type' : 'application/json',
39 Authorization: `${(await fetchAuthSession()).tokens.accessToken.payload}`
40 }
41 }
42 const response = await get({apiName, path, options});
43 return response;
44}
45
46function App() {
47 return (
48 <div className="App">
49 <button onClick={addToGroup}>Add to Group</button>
50 <button onClick={() => listEditors(10)}>List Editors</button>
51 </div>
52 );
53}
54
55export default withAuthenticator(App);

Adding Admin Actions

To add additional admin actions that are not included by default but are enabled by Amazon Cognito, you will need to update the Lambda function code that is generated for you. The change will include adding a route handler for the action and creating a route for it. You will then associate the route handler to the route within the Express app.

Below is an example of how to add an admin action that will allow you to update a user's attributes.

async function updateUserAttributes(username, attributes) { const params = { Username: username, UserAttributes: attributes, UserPoolId: 'STRING_VALUE', }; console.log(`Attempting to update ${username} attributes`); try { await cognitoIdentityServiceProvider.adminUpdateUserAttributes(params).promise(); console.log(`Success updating ${username} attributes`); return { message: `Success updating ${username} attributes`, }; } catch (err) { console.log(err); throw err; } }
1async function updateUserAttributes(username, attributes) {
2
3 const params = {
4 Username: username,
5 UserAttributes: attributes,
6 UserPoolId: 'STRING_VALUE',
7 };
8
9 console.log(`Attempting to update ${username} attributes`);
10
11 try {
12 await cognitoIdentityServiceProvider.adminUpdateUserAttributes(params).promise();
13 console.log(`Success updating ${username} attributes`);
14 return {
15 message: `Success updating ${username} attributes`,
16 };
17 } catch (err) {
18 console.log(err);
19 throw err;
20 }
21}

Once the route handler is defined, you will then add a route with the correct HTTP method to the Express app and associate the route handler to the route. Be sure to make the route unique.

Below is an example of how you can add a POST route named /updateUserAttributes and associate the above route handler to it.

app.post('/updateUserAttributes', async (req, res, next) => { if (!req.body.username || !req.body.attributes) { const err = new Error('username and attributes are required'); err.statusCode = 400; return next(err); } try { const response = await updateUserAttributes(req.body.username, req.body.attributes); res.status(200).json(response); } catch (err) { next(err); } });
1app.post('/updateUserAttributes', async (req, res, next) => {
2 if (!req.body.username || !req.body.attributes) {
3 const err = new Error('username and attributes are required');
4 err.statusCode = 400;
5 return next(err);
6 }
7
8 try {
9 const response = await updateUserAttributes(req.body.username, req.body.attributes);
10 res.status(200).json(response);
11 } catch (err) {
12 next(err);
13 }
14});