AWS Cognito - MFA setup - amazon-web-services

I use the JavaScript AWS SDK for MFA setup and have 2 issues:
First, I update phone number (phone_number attribute with updateUserAttributes method).
It updates but returns empty object instead of (according to docs):
{
"CodeDeliveryDetailsList": [
{
"AttributeName": "string",
"DeliveryMedium": "string",
"Destination": "string"
}
]
}
Second, I am trying to send user a verification code with getAttributeVerificationCode with the following payload:
const params = {
AccessToken: auth.accessToken,
AttributeName: 'phone_number'
}
and I am getting
CustomMessage failed with error
Cannot read property identity of undefined
as error. Any ideas?

For the ones who come across same problem, we could solve it by using cognitoUser.updateAttributes instead of cognitoidentityserviceprovider.updateUserAttributes contrary to the official docs. AWS, especially cognito is premature and lack of docs is another issue for the ones who are considering to use.

AWS is expecting strings, maybe the token is getting passed incorrectly here...
const params = {
"AccessToken": auth.accessToken.toString(),
"UserAttributes": [
{
"Name": "phone number",
"Value": "(555)555-5555"
}
]
}
And here...
const params = {
"AccessToken": auth.accessToken.toString(),
"AttributeName": "phone number"
}

Related

How to update a single user's groups with WSO2 SCIM REST API without using patch/ Groups as it results in timeout when the user count is high?

We are using WSO2 SCIM apis to define roles to user and update it.
For role update operation , we are currently adding the new user role(add user to new role group using SCIM api) , and then delete the existing user role (call users SCIM GET request under a GROUP, delete the existing user from the list and use the newly created list as body arguments to call SCIM PATCH request for the GROUP). With this approach , we were able to update roles. But as the user base increased , the above approach of PATCH operation is getting timeout error .(The new role gets updated to user, but the existing role persists as the 2nd api is getting failed).
Below is one solution which i tried out :
Add new role, delete the newly created role inside user details and call PATCH api with the updated roles of user. But then realized on further investigation that roles inside user is readonly and can't be updated using patch/put operations. So i failed in getting a proper solution .
Is there a way to update a single user's role inside the GROUP without using PATCH /Groups endpoint ?
As I have mentioned in the answer https://stackoverflow.com/a/64225419/10055162, the SCIM specification doesn't allow to update the user's group attribute using PATCH /Users/{userId}.
Also, PATCH /Groups/{groupId} may cause performance issues when the group's member count is too high.
WSO2 IS has improved the performance of PATCH /Groups/{groupId} to some extent.
https://github.com/wso2/product-is/issues/6918 - available 5.10.0 onwards
https://github.com/wso2/product-is/issues/9120 - available 5.11.0 onwards
So, if you are using an older version of IS, can you please try with the latest GA release(5.11.0). It may improve the performance.
UPDATED:
You can use SCIM POST /Bulk endpoint to update user's groups by single REST call, instead of having multiple PATCH /Groups/{group-id} calls.
Refer to https://anuradha-15.medium.com/scim-2-0-bulk-operation-support-in-wso2-identity-server-5-10-0-8041577a4fe3 for more details on Bulk endpoint.
example:
To assign two groups (Group1 and Group2) to a user, execute POST https://<host>:<port>/scim2/Bulk with payload similar to the following.
{
"Operations": [
{
"data": {
"Operations": [
{
"op": "add",
"value": {
"members": [
{
"display": "anuradha",
"value": "db15b161-a205-454d-9da1-4a2a0df0585e"
}
]
}
}
]
},
"method": "PATCH",
"path": "/Groups/f707b6cc-91f8-4b8a-97fb-a01c2a79515c"
},
{
"data": {
"Operations": [
{
"op": "add",
"value": {
"members": [
{
"display": "anuradha",
"value": "db15b161-a205-454d-9da1-4a2a0df0585e"
}
]
}
}
]
},
"method": "PATCH",
"path": "/Groups/8c91215f-1b7a-4cdb-87d9-ae29c60d70de"
}
],
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:BulkRequest"
]
}

Cannot access resource_access.client-test.roles with Flask-OIDC and Keycloak

I configured Keycloak to authenticate the users of my client and return it's role to my application. The following JSON is the data Keycloak returns with OIDC for my user. In the data, we can clearly see that the field resource_access.test-client.roles is present.
{
....some data..
"allowed-origins": [
"http://localhost:5000"
],
"realm_access": {
"roles": [
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"test-client": {
"roles": [
"DemoRole"
]
},
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
....some data..
}
In my application side I used Flask-OIDC to make the authentication and get my user role.
The app configuration was done this way
app.config.update({
'SECRET_KEY': 'u\x91\xcf\xfa\x0c\xb9\x95\xe3t\xba2K\x7f\xfd\xca\xa3\x9f\x90\x88\xb8\xee\xa4\xd6\xe4',
'TESTING': True,
'DEBUG': True,
'OIDC_CLIENT_SECRETS': 'client_secrets.json',
'OIDC_ID_TOKEN_COOKIE_SECURE': False,
'OIDC_REQUIRE_VERIFIED_EMAIL': False,
'OIDC_USER_INFO_ENABLED': True,
'OIDC_SCOPES': ['openid', 'email', 'profile', 'roles'],
'OIDC_VALID_ISSUERS': ['http://localhost:8080/auth/realms/MyDemo'],
'OIDC_OPENID_REALM': 'http://localhost:5000/oidc_callback'
})
The client_secrets.json is
{
"web": {
"auth_uri": "http://localhost:8080/auth/realms/MyDemo/protocol/openid-connect/auth",
"client_id": "test-client",
"client_secret": "40074399-b1b6-442c-9862-68b655ef8dad",
"redirect_uris": [
"http://localhost:5000/oidc_callback"
],
"userinfo_uri": "http://localhost:8080/auth/realms/MyDemo/protocol/openid-connect/userinfo",
"token_uri": "http://localhost:8080/auth/realms/MyDemo/protocol/openid-connect/token",
"token_introspection_uri": "http://localhost:8080/auth/realms/MyDemo/protocol/openid-connect/token/introspect"
}
}
This done, I'm trying to retrieve the role from my user in the following endpoint
#app.route('/private')
#oidc.require_login
def hello_me():
info = oidc.user_getinfo(['resource_access'])
client = info['test-client']
role = client['roles']
The problem is that 'info' is being returned empty. I can access the fields 'email', 'surname' and others, but I can't access 'resource_access', even it being present in the returned JSON. I expected that the sequence of code above could return me the user roles.
What am I doing wrong?
Claim resource_access can be exposed in the access token and/or id token and/or userinfo response. It is not clear from your problem description, where it is exposed exactly. Apparently it is in the wrong place and not where your application code is expecting that.
So fix location of the claim and you should be fine:
lazy option: expose it everywhere (access token and id token and userinfo response)
proper option: read documentation of used library and expose claim only to the right location (access token or id token or userinfo response)

Trying to use model within post-confirmation function

When the user is registered with the method Auth.signup and this one confirmed the code received by email. I want to execute the post-confirmation trigger and update a User table made through the method #model on the schema.graphql file.
I updated the Auth like this:
andres#DESKTOP-CPTOQVN:~/TeVi$ amplify update auth
Please note that certain attributes may not be overwritten if you choose to use defaults settings.
You have configured resources that might depend on this Cognito resource. Updating this Cognito resource could have unintended side effects.
Using service: Cognito, provided by: awscloudformation
What do you want to do? Walkthrough all the auth configurations
Select the authentication/authorization services that you want to use: User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Storage features for images or other content, Analytics, and more)
Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM) Yes
Do you want to enable 3rd party authentication providers in your identity pool? No
Do you want to add User Pool Groups? No
Do you want to add an admin queries API? No
Multifactor authentication (MFA) user login options: OFF
Email based user registration/forgot password: Enabled (Requires per-user email entry at registration)
Please specify an email verification subject: Your verification code
Please specify an email verification message: Your verification code is {####}
Do you want to override the default password policy for this User Pool? No
Specify the app's refresh token expiration period (in days): 30
Do you want to specify the user attributes this app can read and write? No
Do you want to enable any of the following capabilities?
Do you want to use an OAuth flow? No
? Do you want to configure Lambda Triggers for Cognito? Yes
? Which triggers do you want to enable for Cognito Post Confirmation
? What functionality do you want to use for Post Confirmation Create your own module
Succesfully added the Lambda function locally
? Do you want to edit your custom function now? No
Successfully updated resource tevi locally
Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
And then I did amplify push. Then when the function was completed, I updated this one like this:
andres#DESKTOP-CPTOQVN:~/TeVi$ amplify update function
Using service: Lambda, provided by: awscloudformation
? Please select the Lambda Function you would want to update teviPostConfirmation
? Do you want to update permissions granted to this Lambda function to perform on other resources in your project? Yes
? Select the category storage
? Storage has 12 resources in this project. Select the one you would like your Lambda to access User:#model(appsync)
? Select the operations you want to permit for User:#model(appsync) create, update
You can access the following resource attributes as environment variables from your Lambda function
API_TEVI_GRAPHQLAPIIDOUTPUT
API_TEVI_USERTABLE_ARN
API_TEVI_USERTABLE_NAME
ENV
REGION
? Do you want to invoke this function on a recurring schedule? No
? Do you want to edit the local lambda function now? No
Successfully updated resource
Then I did amplify push and I got this error:
andres#DESKTOP-CPTOQVN:~/TeVi$ amplify push
✔ Successfully pulled backend environment dev from the cloud.
Current Environment: dev
| Category | Resource name | Operation | Provider plugin |
| -------- | -------------------- | --------- | ----------------- |
| Function | teviPostConfirmation | Update | awscloudformation |
| Auth | tevi | No Change | awscloudformation |
| Api | tevi | No Change | awscloudformation |
| Storage | s3c1026a67 | No Change | awscloudformation |
? Are you sure you want to continue? Yes
⠼ Updating resources in the cloud. This may take a few minutes...Error updating cloudformation stack
✖ An error occurred when pushing the resources to the cloud
Circular dependency between resources: [functionteviPostConfirmation, authtevi, UpdateRolesWithIDPFunctionOutputs, apitevi, UpdateRolesWithIDPFunction]
An error occured during the push operation: Circular dependency between resources: [functionteviPostConfirmation, authtevi, UpdateRolesWithIDPFunctionOutputs, apitevi, UpdateRolesWithIDPFunction]
This is the backend-config.json I have right now:
{
"auth": {
"tevi": {
"service": "Cognito",
"providerPlugin": "awscloudformation",
"dependsOn": [
{
"category": "function",
"resourceName": "teviPostConfirmation",
"triggerProvider": "Cognito",
"attributes": [
"Arn",
"Name"
]
}
]
}
},
"api": {
"tevi": {
"service": "AppSync",
"providerPlugin": "awscloudformation",
"output": {
"authConfig": {
"additionalAuthenticationProviders": [
{
"authenticationType": "AWS_IAM"
}
],
"defaultAuthentication": {
"authenticationType": "AMAZON_COGNITO_USER_POOLS",
"userPoolConfig": {
"userPoolId": "authtevi"
}
}
}
}
}
},
"storage": {
"s3c1026a67": {
"service": "S3",
"providerPlugin": "awscloudformation"
}
},
"function": {
"teviPostConfirmation": {
"build": true,
"providerPlugin": "awscloudformation",
"service": "Lambda",
"dependsOn": [
{
"category": "api",
"resourceName": "tevi",
"attributes": [
"GraphQLAPIIdOutput"
]
}
]
}
}
}
Amplify CLI Version
4.21.3
Expected behavior
Work with the post-confirmation function and create or update content on the User table with this one.
How can I fix it :/?
As it has been discussed in the related GitHub ticket, we can solve this problem by invoking a function from another.
First, we assume that you used amplify update auth and added Cognito Post Confirmation function to add the user to a group.
Also it seems you followed the this tutorial to make a function for adding user to a custom model after confirmation event. However, you cannot follow the last step of this tutorial as only one function can be bind to the Post confirmation trigger. So let us bind our first function to Post confirmation trigger and keep this new function unbind. I assume that you named this recent function as addusertotable.
If you have added a function with amplify update auth, you will have a file which is called add-to-group.js, modify the function as follow:
/* eslint-disable-line */ const aws = require('aws-sdk');
// My defined function starts here
var addUserToTable = function (event, context) {
// Call function to add user to table Users
var lambda = new aws.Lambda({
region: process.env.AWS_REGION
});
var params = {
FunctionName: 'addusertotable-' + process.env.ENV,
InvocationType: 'RequestResponse',
Payload: JSON.stringify(event, context)
};
lambda.invoke(params, function (err, data) {
if (err) {
console.error(err);
} else {
console.log(params.FunctionName + ': ' + data.Payload);
}
});
};
// My defined function ends here
exports.handler = async (event, context, callback) => {
// Call function to add user to table Users
addUserToTable(event, context); // this is also my code to call my defined function
// Rest are original code from amplify template
// Add group to the user
const cognitoidentityserviceprovider = new aws.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });
const groupParams = {
GroupName: process.env.GROUP,
UserPoolId: event.userPoolId,
};
const addUserParams = {
GroupName: process.env.GROUP,
UserPoolId: event.userPoolId,
Username: event.userName,
};
try {
await cognitoidentityserviceprovider.getGroup(groupParams).promise();
} catch (e) {
await cognitoidentityserviceprovider.createGroup(groupParams).promise();
}
try {
await cognitoidentityserviceprovider.adminAddUserToGroup(addUserParams).promise();
callback(null, event);
} catch (e) {
callback(e);
}
};
As it has been discussed here, we need to add a segment to the lambda policy that allows us to invoke the secondary lambda function (i.e. addusertotable).
Then find the xyzPostConfirmation-cloudformation-template.json file, and then change its PolicyDocument entry like this:
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": {
"Fn::Sub": [
"arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*",
{
"region": {
"Ref": "AWS::Region"
},
"account": {
"Ref": "AWS::AccountId"
},
"lambda": {
"Ref": "LambdaFunction"
}
}
]
}
},
{
"Effect": "Allow",
"Action": "lambda:InvokeFunction",
"Resource": {
"Fn::Sub": [
"arn:aws:lambda:${region}:${account}:function:addusertotable-${env}",
{
"region": {
"Ref": "AWS::Region"
},
"account": {
"Ref": "AWS::AccountId"
},
"lambda": {
"Ref": "LambdaFunction"
},
"env": {
"Ref": "env"
}
}
]
}
Actually what I have added is the second object inside the Statement array.
Please note that addusertotable is the name of the custom function.

Account Locked attribute not getting added in response for scim2 GET Users API in wso2

I am trying retrieve the user list which have locked accounts in WSO2 IS 5.9 version.
I tried after adding account lock attribute to below claims:
http://schemas.xmlsoap.org/ws/2005/05/identity
urn:ietf:params:scim:schemas:core:2.0
urn:ietf:params:scim:schemas:core:2.0:User
urn:ietf:params:scim:schemas:extension:enterprise:2.0:User
I have followed below URL as well to add the custom claim:
https://is.docs.wso2.com/en/5.9.0/develop/extending-scim2-user-schemas/
{ "attributeURI":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:accountLock",
"attributeName":"accountLock",
"dataType":"boolean",
"multiValued":"false",
"description":"Account lock",
"required":"false",
"caseExact":"false",
"mutability":"readwrite",
"returned":"default",
"uniqueness":"none",
"subAttributes":"null",
"multiValuedAttributeChildName":"null",
"canonicalValues":[],
"referenceTypes":[]
}
But still i am not able to get the accountLock attribute in response to GET Users API of scim2.
Response
"totalResults": 10,
"startIndex": 1,
"itemsPerPage": 10,
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:ListResponse"
],
"Resources": [
{
"emails": [
"divya#abc.com"
],
"meta": {
"created": "2020-06-25T07:49:35.465Z",
"lastModified": "2020-06-25T11:20:13.482Z",
"resourceType": "User"
},
"name": {
"givenName": "guest",
"familyName": "guest"
},
"groups": [
{
"display": "Application/sp1"
},
{
"display": "Application/sp2"
},
{
"display": "Application/Read"
}
],
"id": "9ffbed2e-3703-470c-a2c8-e738f4c09709",
"userName": "guest12"
}
]}```
The following reasons may cause to accoutLock attribute does not appear in SCIM2 GET user response.
You might missed to add the new attribute ( "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:accountLock") as a sub attribute of urn:ietf:params:scim:schemas:extension:enterprise:2.0:User object. (Point 3 in https://is.docs.wso2.com/en/5.9.0/develop/extending-scim2-user-schemas/#extending-the-scim-20-api.
"subAttributes":"accoutLock verifyEmail askPassword employeeNumber costCenter organization division department manager")
"attributeURI":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User",
"attributeName":"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User",
"dataType":"complex",
"multiValued":"false",
"description":"Enterprise User",
"required":"false",
"caseExact":"false",
"mutability":"readWrite",
"returned":"default",
"uniqueness":"none",
"subAttributes":"accoutLock verifyEmail askPassword employeeNumber costCenter organization division department manager",
"canonicalValues":[],
"referenceTypes":["external"]
}```
The mapped attribute of the added custom claim (https://is.docs.wso2.com/en/5.9.0/develop/extending-scim2-user-schemas/#add-the-custom-claim) should be an existing attribute in LDAP schema if you are using the default LDAP userstore. (However, if you have done this mistake you won't be able to update/add claim value. It gives One or more attributes you are trying to add/update are not supported by underlying LDAP for user: error)
The response of SCIM2 GET users doesn't contain the attributes which don't have a value. Thus, set true/false to the claim value.
Moreover, it is enough to add the new attribute to urn:ietf:params:scim:schemas:extension:enterprise:2.0:User claim dialect. Follow steps in https://is.docs.wso2.com/en/5.9.0/develop/extending-scim2-user-schemas/

AppSync check if DynamoDB record exists

I am trying to write a resolver for AppSync that derives the value for a Boolean field based on the existence of a record in DynamoDB.
I currently have the following request mapping template:
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"field1": $util.dynamodb.toDynamoDBJson($ctx.args.field1),
"field2": $util.dynamodb.toDynamoDBJson($ctx.args.field2)
}
}
And the following response mapping template:
#if($util.isNull($ctx.result))
#set($exists = false)
#else
#set($exists = true)
#end
$util.toJson({
"field1": $ctx.args.field1,
"field2": $ctx.args.field2,
"exists": $exists
})
This works correctly if the record exists but if it does not then AppSync simply returns "null" for the entire API call and does not seem to evaluate the response mapping template at all. Is there any way I can instruct it not to do this?
Another option would be to perform a query and look at the length of the response but I have no idea how to check length in these templates.
This is an expected behavior for the 2017 version of the Request template. If you would like the $ctx.result to be evaluated, switch to the 2018 version as below:
{
"version": "2018-05-29",
"operation": "GetItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.id),
},
}
Refer to this change log for additional details.