How to get user attributes (username, email, etc.) using cognito identity id - amazon-web-services

I have AWS Cognito Identity Pool that is configured with Cognito User Pool as an authentication provider.
Assume I have identity ID of an identity in Cognito Identity Pool (e.g. us-east-1:XXaXcXXa-XXXX-XXXX-XXX-XXXXXXXXXXXX) where this identity has a linked login to a user in Cognito User Pool.
Using identity ID, how can I get the linked user details (email, phone, username)?

The ID Token that you exchange with Cognito federated identity service to get the identity id and credentials already has all user attributes. You do not need an extra call to any service.
It is a JWT token and you can use any library on the client to decode the values. You can read this guide for more information about the tokens vended by Cognito user pools.
Alternatively, you can also use the Access Token to call GetUser API which will return all the user information.

Using REST API
AccessToken
Thought that this could be very helpful to someone as I've spent a lot of time trying to figure out how to get UserAttributes with only accessToken and region ( Similar to this but with REST API ( Without using aws-sdk )
You can get UserAttributes with accessToken using this HTTP request. ( GetUser )
Method: POST
Endpoint: https://cognito-idp.{REGION}.amazonaws.com/
Content-Type: application/x-amz-json-1.1
Content-Length: 1162 // Access Token bytes length
X-Amz-Target: AWSCognitoIdentityProviderService.GetUser
Body: {"AccessToken":"ACCESS_TOKEN"}
And if the accessToken is valid, you should receive example response like the following
{
"UserAttributes": [
{
"Name": "sub",
"Value": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"
},
{
"Name": "email_verified",
"Value": "true"
},
{
"Name": "name",
"Value": "Jason"
},
{
"Name": "phone_number_verified",
"Value": "true"
},
{
"Name": "phone_number",
"Value": "+xxxxxxxxxxx"
},
{
"Name": "email",
"Value": "xxxx#gmail.com"
}
],
"Username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"
}

AWS cognito-idp list-users has a filter option that allows you to filter based on attribute. 'sub' is the attribute that matches the identity id you are describing.
e.g. at the command line:
aws cognito-idp list-users --user-pool-id us-east-1_abcdFghjI --filter "sub=\":XXaXcXXa-XXXX-XXXX-XXX-XXXXXXXXXXXX\""
This also requires the user-pool-id, which I suspect you have. Additionally, I have no idea how this is implemented or how it performances when filtering a large number of users, but I take custom attributes not being usable in filters as a hint that there is some form of indexing behind the curtain.

I faced the similar issue and after too much of scratching i was not able to find the exact way of pulling out the details. My usecase was to get the details in android APP.
After looking into their AWSMobile client API code. I found below and it is working from me.
Log.i(TAG, "User Details"+ AWSMobileClient.getInstance().getUserAttributes().toString());
Recommendation - Try use AWSMobileclient incase you are using it for Android Development as this is new library that is recommended for development.

Just struggled with this for a while, and the way I got the user name, using Java API is:
identityManager.login(this, new DefaultSignInResultHandler() {
#Override
public void onSuccess(Activity activity, IdentityProvider identityProvider) {
...
String userName = ((CognitoUserPoolsSignInProvider) identityProvider).getCognitoUserPool().getCurrentUser().getUserId();

There is a listener we can initialize that will listen to changes in our authentication state and allow us to have access to the type of authentication event that happened and update the application state based on that data.
With Amplify, the Hub module allows us to do this pretty easily:
import { Hub } from 'aws-amplify';
Hub.listen('auth', (data) => {
const {payload} = data;
if (payload.event === 'signOut') {
console.log('signOut');
} else if (payload.event === 'signIn') {
console.log('A new auth event has happened: ', data.payload.data.username + ' has ' + data.payload.event);
}
});

For those who are looking how to get the value of email parameter in Java programmatically
I assume you have already figured out how to get the needed / all users from the pool.
Say I have ListUsersResult with my all users and say I want to check the email value of the first user:
ListUsersResult allUsers = getAllUsers();
UserType userType = allUsers.getUsers().get(0);
First I can get user's all attributes:
List<AttributeType> attributes = userType.getAttributes();
Then loop through the attributes looking for the one we're interested in (our case email):
for (AttributeType att : attributes) {
if (att.getName().equals("email")) {
// do whatever you want
}
}
Remember that printing in to the console will most probably not work since it is sensitive data. But you can compare it like this:
att.getValue().equals("mymail#mail")

Use this piece of code
GetDetailsHandler detailsHandler = new GetDetailsHandler() {
#Override
public void onSuccess(CognitoUserDetails cognitoUserDetails) {
CognitoUserAttributes cognitoUserAttributes=cognitoUserDetails.getAttributes();
stringStringHashMap=new HashMap<>();
stringStringHashMap =cognitoUserAttributes.getAttributes();
userNumber=stringStringHashMap.get("phone_number");
e1.setText(userNumber);
Log.d("Response"," Inside DEATILS HANDLER");
// Store details in the AppHandler
AppHelper.setUserDetails(cognitoUserDetails);
// Trusted devices?
handleTrustedDevice();
// e1.setText(input.getText().toString());
}
#Override
public void onFailure(Exception exception) {
closeWaitDialog();
showDialogMessage("Could not fetch user details!", AppHelper.formatException(exception), true);
}
};
private void getDetails() {
AppHelper.getPool().getUser(username).getDetailsInBackground(detailsHandler);
}

console.log('username is ' + cognitoUser.getUsername());

Related

Unable to create user in WSO2 IS via SCIM2 API with external claims

Im trying to create user with external claims, but something wents wrong.
I added my claims to scim2-schema-extension.config, to external claims and local claims, but API's response for my request is:
{
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:Error"
],
"detail": "Error in adding the user: testoviy22#mail.ru to the user store.",
"status": "500"
}
In logs i have
: ERR_13735_ELEMENT_FOR_OID_DOES_NOT_EXIST ATTRIBUTE_TYPE for OID comment does not exist!]; remaining name 'mail=testoviy22#mail.ru'
at java.naming/com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3280)
at java.naming/com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3205)
at java.naming/com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2996)
at java.naming/com.sun.jndi.ldap.LdapCtx.c_bind(LdapCtx.java:452)
at java.naming/com.sun.jndi.toolkit.ctx.ComponentDirContext.p_bind(ComponentDirContext.java:299)
at java.naming/com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.bind(PartialCompositeDirContext.java:217)
at org.wso2.carbon.user.core.ldap.UniqueIDReadWriteLDAPUserStoreManager.persistUser(UniqueIDReadWriteLDAPUserStoreManager.java:312)
... 73 more
Local claim
External claim
Request sample:
{
"name": {
"familyName": "Zubenko",
"givenName": "Michael"
},
"password": "qwerty",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"comment": "test"
},
"userName": "testoviy22#mail.ru"
}
Seems you are using an LDAP server as your primary userstore, and that server doesn't support the attribute named "comment"
Refer to the note in https://is.docs.wso2.com/en/latest/references/extend/provisioning/extend-scim2-user-schemas/#map-the-custom-claim
You can use the word "customClaim" (or any other preferred word) as
the Mapped Attribute only when using a JDBC userstore because JDBC
userstores will automatically create a new attribute if it does not
already exist in the user store. However, If you are using LDAP or
Active Directory, you will have to use an attribute that exists in the
user store already.
Change the mapped attribute of the local claim to some valid attribute in the LDAP schema

Instagram: Graph API does not return id of user who commented

I am using Facebook Graph v7.0 to access Instagram data.
I can get comments made on instagram media using the following query:
Request:
https://graph.facebook.com/v7.0/18132613492054980?fields=id,ig_id,caption,timestamp,owner,username,media_type,permalink,children,comments.limit(100){hidden,id,like_count,media,text,timestamp,user,username},comments_count&access_token
Response:
{
"id": "18132613492054980",
"ig_id": "2263043983231761272",
"caption": "Sprite",
"timestamp": "2020-03-12T06:51:27+0000",
"owner": {
"id": "17841430463493290"
},
"username": "jobyjohn123456",
"media_type": "IMAGE",
"permalink": "https://www.instagram.com/p/B9n8oM7nTt4/",
"comments": {
"data": [
{
"hidden": false,
"id": "18132938077057326",
"like_count": 0,
"media": {
"id": "18132613492054980"
},
"text": "Nice sprite \u0040yziaf__07",
"timestamp": "2020-03-12T06:52:27+0000",
"username": "zimba_birbal"
}
]
},
"comments_count": 2
}
In the response, I do not get the User Id of user who commented. It just includes the username of the commenter.
Though, I pass user in the query, the result does not include it.
Do I need any special permission to get user id of the user who commented in the comment response?
There is Facebook API "business discovery" to get the user details of other Instagram User.
API request:
https://graph.facebook.com/178430463490?fields=business_discovery.username(user_name_you_wantto_get_its_IgUserId){followers_count,media_count,username,ig_id}
Response:
We can pass the user name in that API request, then we will get the Instagram User Id.
The documentation says it has one limitation, this will not work for "age-gated Instagram Business IG Users" but I do not exactly know what it means. When I tested for both older Instagram account and new Instagram account (just recently created account) and this API is returning data for both.
When I tested for private Instagram account, it did not work so it seems this api works only for business account.
If that api does not work, there is one workaround. The following request help to get User Id using its username but I have not found any API documentation regarding this API. It looks like this is not a standard API and moreover it does not need any token.
https://www.instagram.com/user_name_you_wantto_get_its_IgUserId/?__a=1

Can I register an IDP with multiple certificates on AWS IAM

What I have is:
An OpenId Complaint service (Rest) that provides tokens
This service has multiple certificates (keyPairs) for signing tokens depending on some factors when requesting a token
- The service is implementing the 2 OpenId Endpoints (well known and certs)
What I did:
I registered the service as an IDP on AWS IAM service successfully (hence my two OpenId Endpoints are working other wise AWS wont accept the IDP)
I created roles on IAM that are to be assumed using the IDP service tokens
I got two tokens from the IDP service to be used for assuming role (each signed with different key)
Problem:
AssumeRole is failing and I'm getting invalid token exception for both tokens.
I tried to set the "kid" claim in the tokens each with the corresponding kid of the certificate and it didn't work :(.
Note:
I'm assuming role using Java AWS API
When I remove one of the certificates (from the below sample response) the remaining certificate works fine. So the problem is with having 2 certs, but I need to have and AWS should have a way of working with such case I just don't know how.
Sample of how my certs endpoint looks like:
{
"keys": [
{
"kid": "kid",
"kty": "kty",
"use": "use",
"alg": "alg",
"n": "nValue",
"e": "eValue",
"x5c": [
"cert1"
],
"x5t": "x5t=",
"x5t#S256": "x5t#S256"
},
{
"kid": "kid1",
"kty": "kty",
"use": "use",
"alg": "alg",
"n": "nValue",
"e": "eValue",
"x5c": [
"cert2"
],
"x5t": "x5t=",
"x5t#S256": "x5t#S256"
}
]
}
So the problem was that I tried to set the kid in the "claims" of the JWT.
However it turned out that ,in order for AWS to distinguish that this JWT was signed with this key (from the jwks response), it checks in the headers of the JWT. If a kid is found in the header then it looks in the jwk response for the certificate with the corresponding kid.
So to solve the issue I just had to set the kid of in the "headers" of the JWT.
So if you are in java :
public String buildToken(Key key) {
Map<String, Object> claims = new HashMap();
Map<String, Object> headers = new HashMap();
claims.put(claimName, someClaim);
...
headers.put(KID, KID_OF_THIS_TOKENS_CERTIFICATE);
...
return Jwts.builder().setClaims(claims).setHeader(headers).signWith(SignatureAlgorithm.RS256, key).compact();
}

AWS Kinesis: user address in event encoded / encrypted

In my React Native mobile app, I use AWS Amplify to send info about user actions (screen views, button taps, swipes, etc.) by means of Analytics.record(...) to AWS Pinpoint which in turn feeds them into a AWS Kinesis Data Stream. I have created an AWS Lambda Python 3 function that listens to events in this data stream.
Setup has been a breeze, thanks to outstanding documentation and everything works fine - except for one thing:
When a user logs in, I update the Pinpoint Endpoint with the user ID, email address and some more attributes using Analytics.updateEndpoint(...). In the lambda function, I base64-decode the event payload as shown in this sample code and a sample event payload looks roughly like this:
{
"event_type": "_session.start",
"event_timestamp": 1572345161558,
"application": {
"app_id": "<some app ID>",
"cognito_identity_pool_id": "us-east-1:<some pool ID>",
"sdk": {},
"version_name": "<the app version I put in using updateEndpoint(...)>"
... <snipped for brevity> ...
},
"attributes": {},
"endpoint": {
"ChannelType": "APNS",
"Address": "=ABAQRuUDJD ... <some longish binary value> j0eL+69lsY=",
"EndpointStatus": "ACTIVE",
"Location": {
"Country": "US"
},
"Demographic": {
"Make": "iPhone",
"Model": "iPhone X",
"ModelVersion": "13.1.3",
...
"Platform": "ios"
},
"User": {
"UserId": "us-east-1:<Cognito ID of the user that logged in>",
"UserAttributes": {}
},
... <snipped for brevity> ...
},
"awsAccountId": "<my account ID>"
}
The user email address in the "Address" field above is not contained in the Kinesis Data Stream event as plain text, but encoded (or encrypted ?) somehow.
My question: Can anybody tell me how it is encoded / encrypted ? And, ideally, how to get the plain text address ?
I tried to base64-decode it or decrypt it using my default AWS KMS key (and a combination thereof), but no luck.
Alternatively, I could use the (plain text) user ID to look up the email address in the AWS Cognito user pool used to manage auth & auth, but getting it from the event directly would obviously be a lot simpler...
I have searched the web up and down, asked in the AWS-Amplify channel on gitter, but that Address encoding / encryption just does not seem to be documented anywhere...

How to set Cognito User Pool User DeviceAttributes

GetDevice seems to return DeviceAttributes I can't find an API for setting them. I would like to use them to store the mobile push notification token with the device
{
"Device": {
"DeviceAttributes": [
{
"Name": "string",
"Value": "string"
}
],
"DeviceCreateDate": number,
"DeviceKey": "string",
"DeviceLastAuthenticatedDate": number,
"DeviceLastModifiedDate": number
}
}
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_GetDevice.html
I am on the Cognito team. At first we intended DeviceAttributes for a similar use case to the one you are describing, associate the mobile push notification token with the device. However, this has not been released yet and hence there is no API to update the device attributes. I will mention your use case within the team and add a +1 on the feature request.