AWS Cognito - Create a user via API Endpoint in Postman - amazon-web-services

Does anybody know if I can make a request to create or a sign up a user in AWS Cognito user pool?
For example, something like below is to display the login screen.
But is there a POST request or endpoint I can call to create a user?
I tried looking through their documentation but no look finding anything concrete.
Keep in mind, if it possible I would like to populate a value for a custom attribute I created.
This is the main reason why I am looking for an endpoint because I cannot seem to find a way to populate the value for a custom attribute via the AWS interface.
So technically I do not need an endpoint if it is possible to populate a custom attribute per user in AWS.
GET https://mydomain.auth.us-east-1.amazoncognito.com/login?
response_type=code&
client_id=ad398u21ijw3s9w3939&
redirect_uri=https://YOUR_APP/redirect_uri&
state=STATE&
scope=openid+profile+aws.cognito.signin.user.admin

It looks like what you need is https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminCreateUser.html or https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_SignUp.html. As far as I'm aware, there is no way to prepopulate the attribute on the Cognito hosted UI. You did not specify what programming language you are using, but at the bottom of the page there are links to documentation with examples for different SDKs. The difference between these two approaches is discussed here: https://docs.aws.amazon.com/cognito/latest/developerguide/signing-up-users-in-your-app.html. So in this case, AdminCreateUser corresponds to option 3 and SignUp to option 1. The difference is mainly in whether or not the user will receive an invite. Also, for AdminCreateUser Cognito will generate a temporary password and require user to enter a new password the first time they log in.

Body
{
"ClientId": "test",
"Password": "Qwerty123",
"UserAttributes": [
{
"Name": "email",
"Value": "test#test.com"
}
],
"Username": "test#test.com"
}
Headers
URL
POST https://cognito-idp.eu-west-1.amazonaws.com/ HTTP/1.1

Related

Google Identity - email templates cannot be changed

We use GCP Identity Platform to manage multi-tenant authentication with email and password provider.
Google provides a way to configure email templates that are sent when user requests a password reset (in our case, this flow is triggered on the fronted by calling https://firebase.google.com/docs/reference/js/v8/firebase.auth.Auth#sendpasswordresetemail).
This will trigger a email send to the user for which identity this method was called.
This email, however, is problematic for us: it is sent from the *.firebaseapp.com domain (we want to use our own domain) and since it's been setup initially, it is now not possible to amend body of the email sent or change the "from" email used to send it.
This is how the configuration looks like in GCP console:
Identity console
Anyone experienced the same issues or know what is the proper procedure to update email templates?
Answering my own question.
After some research, I started to believe GCP Console doesn't offer a way to make email templates configured at parent/top level project and inherit these settings by tenant providers. It is possible, however, with direct API calls:
Using patch https://cloud.google.com/identity-platform/docs/reference/rest/v2/projects.tenants/patch
one needs to update Tenant entity with Inheritance object (setting emailSendingConfig flag) :
{
"name": "projects/<project-id>/tenants/<tenant-id>",
"displayName": "<your tenant>",
"allowPasswordSignup": true,
"inheritance": {
"emailSendingConfig": true
}
}

Switching unauthenticated user to authenticated user

I'm trying to set my app up to allow unauthenticated users to access an AppSync API, as mentioned in https://docs.aws.amazon.com/cognito/latest/developerguide/switching-identities.html . Ideally they would be able to start using the app, then sign in and keep all their data.
I've got:
A user pool. This is set up for Google auth/regular cognito auth
An identity pool
This is linked to the user pool via a Cognito identity provider.
The authenticated/unauthenticated roles have a policy attached to them that gives them access to the GraphQL API
An AppSync API set up with AWS_IAM auth
I create the app sync client like this:
val credentialsProvider = CognitoCachingCredentialsProvider(
context,
"us-east-2:abc...etc",
Regions.US_EAST_2)
appSyncClient = AWSAppSyncClient.builder()
.context(applicationContext)
.awsConfiguration(awsConfiguration)
.credentialsProvider(credentialsProvider)
.build()
This works fine and the identity pool creates an identity for me, and I can interact with the API. Well, it creates two anonymous identity IDs, but it works. The real trouble comes when I log in:
val hostedUIOptions: HostedUIOptions = HostedUIOptions.builder()
.scopes("openid", "email", "aws.cognito.signin.user.admin")
.build()
val signInUIOptions: SignInUIOptions = SignInUIOptions.builder()
.hostedUIOptions(hostedUIOptions)
.build()
runOnUiThread {
mobileClient.showSignIn(
mainActivity,
signInUIOptions,
object : Callback<UserStateDetails?> {
override fun onResult(result: UserStateDetails?) {
Log.i("AwsAuthSignIn", "onResult: " + result?.userState)
}
override fun onError(e: Exception?) {
Log.i("AwsAuthSignIn", "onResult: " + result?.userState)
}
}
)
}
After that I see that it's created a new identity associated with the sign in, rather than use the old one. I thought it was supposed to seamlessly transfer over the old identity ID to be connected with the authenticated user.
I've also tried calling registerIdentityChangedListener to see if it fires on logging in, but it does not. It only fires when first getting the unauth identity IDs.
Also when I log into the same account from two different device it creates two different identity IDs for the same user in the user pool. Since I'm using identityId to track RDB record ownership, this means that the same user sees different items after logging in.
So is identityId the right thing to put in the database? Is it expected to be different for different devices? I'm trying to find something else to use but am coming up dry.
This is what's available in the "identity" section of the context for use with VTL resolvers:
"identity": {
"accountId": "________",
"cognitoIdentityAuthProvider": "\"cognito-idp.us-east-2.amazonaws.com/us-east-2_______\",\"cognito-idp.us-east-2.amazonaws.com/us-east-2_______:CognitoSignIn:____________\"",
"cognitoIdentityAuthType": "authenticated",
"cognitoIdentityId": "us-east-2:___",
"cognitoIdentityPoolId": "us-east-2:___",
"sourceIp": [
"_____"
],
"userArn": "arn:aws:sts::_________:assumed-role/amplify-focaltodokotlin-prod-222302-authRole/CognitoIdentityCredentials",
"username": "__________:CognitoIdentityCredentials"
}
"username" is the only other one that makes sense, but when I call AWSMobileClient.username on my side, it comes up with a different format: "Google_". So I wouldn't be able to match it up in client-side logic.
Is this possible at all or do I need to abandon the idea of unauthenticated use and go with User Pools directly?
I'll have a shot of answering this, I'll stick to rather how Cognito works than what to do with a specific SDK.
Quick recap, in Cognito, when a user authenticates, they will get 3 tokens, access, id & refresh. With an Identity Pool, they can exchange one of these (I forget which one) to get short-term credentials to assume a role. STS is what is used under the hood for this, and that's what you see in the userArn there. You don't want to look at that guy for an ID, it's an STS construct your client needs to assume an IAM role.
I'll go back to the tokens, lets look at the id_token, my favourite:
{
"at_hash": "l.......",
"sub": ".....",
"cognito:groups": [
"ap-southeast-......_Google",
"kibana"
],
"email_verified": false,
"cognito:preferred_role": "arn:aws:iam::...:role/...",
"iss": "https://cognito-idp.ap-southeast-2.amazonaws.com/ap-southeast-...",
"phone_number_verified": false,
"custom:yourapp:foo": "Bar",
"cognito:username": "Google_......",
"nonce": ".........",
"cognito:roles": [
"arn:aws:iam::.....",
"arn:aws:iam::....."
],
"aud": ".....",
"identities": [
{
"userId": "....",
"providerName": "Google",
"providerType": "Google",
"issuer": null,
"primary": "true",
"dateCreated": "1583907536164"
}
],
"token_use": "id",
"auth_time": 1596366937,
"exp": 1596370537,
"iat": 1596366937,
"email": "test#test.com"
}
I have a Google one here too, I removed a bit of stuff to hide my account etc, but anyway the id you want to use is cognito:username which will in-fact be in the form of Google_. This is internal and typically you would not show this to users. So instead in Cognito you can use the another claim preferred_username, which can also be used as an alias to sign-in as mentioned here but not for external identity providers.
You can use create custom claims to help show information on the UI, which will be prefixed with custom:, I have one here: custom:yourapp:foo. But there might be one existing for you already such as email which is available from Google. When you created your external identity provider you would have configured what claims you wanted to map from Google, email would have been there, so in your app you can read the email claim, but you should use the cognito:username in your App's backend, but keep in mind that if a user deletes and recreates their account I don't know that you get the same ID again. You may rather want users to be able to define a preferred_username on signup, which you could display in the UI, but don't use that to save data against, use the cognito:username claim.
And now for Start using the app, then sign in and keep all their data. Typically this would be implemented by saving all the data in local storage on the device, not the backend. The reason being is that if a user has not authenticated, (excluding creating a session upon opening the app), then there is no way to verify that foo#gmail.com is actually foo#gmail.com when they were hitting your API as what you saw with the unauthenticated role. I could hit your API and say that I am foo#gmail even though I was bar#gmail.
The neatest way would be to store the data locally on the device, so it wouldn't matter if the user was authenticated or not. If for some reason your app does need to store this data in the backend to function, and you cannot refactor, what you could do is use a Customized Userpool Workflow, and create a Pre-Signup Lambda, which could take a custom claim (I wouldn't use the sts userArn seems wrong but you could use that too), preferably a GUID of a shopping cart for example custom:yourapp:cartGuid. So what I mean is:
When an anonymous user visits the app for the first time, they would
be issued a GUID for a shopping cart, and save all items in that
cart.
If they choose to sign up, they can pass in a custom claim: custom:yourapp:cartGuid, and in your Lambda function you will create the user in your DB, and add the cart to their account.
Guessing another user's GUID would be near impossible, but if this is a security concern then you could create a signed token.
You probably want to clean up users's carts that don't move to the sign-up after a certain amount of time.
Just give me a comment if you have any questions or are unsure. I believe from memory that you need to use the pre-signup hook because the post-confirm doesn't have access to the claims passed in on the signup process. You may want to create the user with an unconfirmed flag in the pre-hook, and then enable them in the post-hook which I believe is more safe incase another failure would happen your pool and then you have users created in a dirty state. Best of luck I've been through Cognito battles myself and survived!

Save AWS Cognito Users in DynamoDB

I recently started experimenting with AWS AppSync but I had some questions around AWS Cognito.
I would like for users to be able to authenticate with Facebook but I need their profile picture, name and email as data for my public user profiles in my app. So far, I noticed Cognito integrates with Facebook Auth but it does not allow access to the user information and this info does not get saved in a DynamoDB table.
My question is, how can I create a new User in DynamoDB when Cognito receives a new sign in, or return an existing user/id when the user already exists in the db.
I was trying to achieve the same a few weeks ago.
After reading the docs for hours, I realised that Cognito may not help us in regards to the data that comes back from FB or how to save it.
I ended up doing the following:
(1) Using FB-SDK, pulled in the user data.
(2) Invoked a Lambda function that saved this data (like FB_id,etc) to DynamoDB.
(3) If user logged in again, their FB_id (or email) was used to check against DynamoDB entries to retrieve their data.
If Cognito is able to help us and I missed it somehow, I would love to know.
Happy Coding!
You could use custom attributes and federating user from Facebook in your user pool to achieve this. Here are the steps at high level to do this.
You will first have to define custom attributes for the profile information you want to save in each user profile.
Define attribute mapping to link the custom attributes to Facebook attributes you want to save.
Build you application using Cognito hosted pages and federation to allow your users to log in using Facebook.
After this, on each new user log in in your app a new user is created in your user pool with all the attributes that were defined in attribute mapping and values which Cognito gets in the Facebook token. Your app will get these attribute values in the IDToken issued after authentication and you app can use these.
Additionally, if you want to store these attribute values outside of Cognito user pools profile, like your own DynamoDB table, you can configure a PreSignUp trigger in the pool which will be invoked on all new user creations. You can export the user attributes from this trigger to any database of your choice.
Hope this helps.
AWS AppSync allows you to access information in the GraphQL resolver which you can choose to store in a DynamoDB table. In your case for data coming from a Facebook profile you could pass this as arguments to a GraphQL mutation or in a header to AppSync which you can then access in the resolver via $ctx.request.headers.NAME where NAME is your header name. Then you could simply choose which attributes you want to write to DynamoDB for that user as part of the mutation. More information is in the reference guide here: https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html
Since you also asked that you'd like to do a check first to see if the user is already in the DDB first you could just do an existence check first:
{
"version": "2017-02-28",
"operation": "PutItem",
"key": {
"userId": $util.dynamodb.toDynamoDBJson($ctx.identity.username),
},
"attributeValues": $util.dynamodb.toMapValuesJson($ctx.args.input),
"condition": {
"expression": "attribute_not_exists(userId)"
},
}
This checks against the username from Cognito User Pools. If you were using the Cognito Federated Identities feature it would be ctx.identity.cognitoIdentityId. If the record is already there the response that comes back will tell you which means the user is already present. You could also transform the returned message in the response mapping template by looking at $ctx.result with a conditional statement and either building the JSON response by scratch or using one of the $util.error() methods in the guide above.
Finally as you mentioned that you'll have public profile data, you might want to mark this on certain records for control. In AWS AppSync you can filter GraphQL responses on authorization metadata such as this. You would just have an attribute (aka column) on the DynamoDB record marked 'public' or 'private. Then your response template would look like so:
#if($context.result.public == 'yes')
$utils.toJson($context.result)
#else
$utils.unauthorized()
#end
You can see more examples of this here: https://docs.aws.amazon.com/appsync/latest/devguide/security-authorization-use-cases.html#public-and-private-records

how to handle the access token in loopback

This is the boot script code I have added
module.exports = function enableAuthentication(server) {
//enable authentication
server.enableAuth();
};
In postman tool i have tried by setting authorization in header as below for users/logout api (note: I created the user model by extending User model),
authorization LqAHkjJV4JQ7oiW6QrYPeDoJszqUXSSUi7NwTHivKV0jyNK3VSyIyFxon72NfPzZ
But iam getting the below error,
{"error":{"name":"Error","status":500,"message":"could not find accessToken","stack":"Error: could not find accessToken\n at D:\\zauth\\node_modules\\loopback\\common\\models\\user.js:302:12\n at D:\\zauth\\node_modules\\loopback-datasource-juggler\\lib\\dao.js:2056:62\n at D:\\zauth\\node_modules\\loopback-datasource-juggler\\lib\\dao.js:1984:11\n at D:\\zauth\\node_modules\\loopback-datasource-juggler\\node_modules\\async\\lib\\async.js:396:17\n at async.each (D:\\zauth\\node_modules\\loopback-datasource-juggler\\node_modules\\async\\lib\\async.js:153:20)\n at _asyncMap (D:\\zauth\\node_modules\\loopback-datasource-juggler\\node_modules\\async\\lib\\async.js:390:13)\n at Object.map (D:\\zauth\\node_modules\\loopback-datasource-juggler\\node_modules\\async\\lib\\async.js:361:23)\n at allCb (D:\\zauth\\node_modules\\loopback-datasource-juggler\\lib\\dao.js:1912:15)\n at D:\\zauth\\node_modules\\loopback-datasource-juggler\\lib\\connectors\\memory.js:472:7\n at _combinedTickCallback (internal/process/next_tick.js:67:7)\n at process._tickDomainCallback (internal/process/next_tick.js:122:9)"}}
Also i have tried,
http://localhost:3000/api/users/logout?access_token=LqAHkjJV4JQ7oiW6QrYPeDoJszqUXSSUi7NwTHivKV0jyNK3VSyIyFxon72NfPzZ
It's not woking, same error
i have to know how to pass the accesstoken #kamal0808
You need to set ACLs(Access Control Levels) for every API request for the application.
Here's the doc link for ACLs:
https://loopback.io/doc/en/lb2/Controlling-data-access.html
For your code, you need to get the following object in ACLs array of your Users.json file:
{
"accessType": "EXECUTE"
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW",
"property": "find"
}
$authenticated refers to anybody who can log in. You can create custom roles for users too.
The default ACL for built-in User model does not allow listing users:
You can see full configuration here:
https://loopback.io/doc/en/lb3/Managing-users.html#default-access-controls
The above ACL denies all operations to everyone, then selectively allows:
Anyone to create a new user (User instance).
Anyone to log in, log out, confirm their identity, and reset their own password.
A user to perform deleteById, findById, and updateAttributes on their own User record (instance).
You can not directly modify build-in User model, so you will need to extend it.
How to pass access token after successful login?
You have two ways to do that:
As access_token query parameter (Not recommended). Example:
http://localhost:3000/api/users/me/orders?access_token=$ACCESS_TOKEN
As "Authorization: HTTP header (Recommended). Example:
Authorization: $ACCESS_TOKEN
You should use HTTP headers, because using query parameters is not secured enough (they are not crypted by SSL protocol and could stay in browser or other client history).
Check for more information the documentation: https://loopback.io/doc/en/lb3/Making-authenticated-requests.html

How do I find the immutable id of my Google Apps account?

Many of the Directory API calls require a customer parameter referred to as the "Immutable id of the Google Apps account. (string)".
e.g. GET https://www.googleapis.com/admin/directory/v1/customer/customer/domains
I have no idea how to find/generate this for my Google Apps account. I am an admin.
Can someone point me in the right direction please?
I was able to find the customerId as follows
Go to admin.google.com
Security -> Set up single sign-on (SSO)
You will see URLs like this:
https://accounts.google.com/o/saml2/idp?idpid=Cxxxxxxxx
That Cxxxxxxxx is your customerId
Had the same question, so I had to contact their chat support.
The official answer was:
There are no any web interface to look up this information - as I would expect for example in "organisation admin panel". The only way to get this information is from the code.
You have to write extra code to request information about any existing user:
(Link to API Docs - GET: https://www.googleapis.com/admin/directory/v1/users)
And in the response, you can find field customerId which is the same for each user in the company across all domains.
That is the only way to find your organisation customerId...
Not user-friendly, so I will submit "Feature Request" right now to Google.
The easiest way I found was to use the APIs Explorer at the bottom of the documentation for the Customers: get method on the Directory API (Admin SDK). Enter 'my_customer' for the customerKey on the form and hit the 'Authorize and Execute' button.
The response will include the CustomerId (e.g. Cxxxxxxxx) as the "id". The entire response will look something like this:
{
"kind": "admin#directory#customer",
"id": string,
"etag": etag,
"customerDomain": string,
"alternateEmail": string,
"postalAddress": {
"organizationName": string,
"countryCode": string,
},
"language": string,
"customerCreationTime": datetime
}
If you are part of an organization, you can find it here as Organization ID: https://play.google.com/work/adminsettings?pli=1
It works for G Suite accounts and Gmail accounts that are associated with an organization, but not for individual Gmail accounts.
I found something simpler: gcloud organizations list. It will give you DISPLAY_NAME, ID and DIRECTORY_CUSTOMER_ID (this is what your are looking for) for all you organizations.