I am trying to configure the Microsoft Azure AD B2C identity provider in my Cognito user pool.
I am using the https://www.npmjs.com/package/#aws-amplify/auth library on my front-end.
I have named the new identity provider Microsoft. I have also enabled it on my user pool. And I am passing the same (Microsoft) to the Auth.federatedSignIn method.
Now, when I try to authenticate the user with Microsoft, the user gets authenticated from the Microsoft side but the Cognito gives me the ?error_description=Invalid+ProviderName/Username+combination.+Please+check+again+&state=XmnGedOhmT99RnTlw0LypyMmqwCRbCZr&error=invalid_request error.
This seems like a configuration issue to me but I am unable to figure out what is it.
Please help.
After some time we managed to resolve this issue.
The problem was that the user is linked with ProviderAttributeValue to lower case, but in the sub claim from the OIDC provider there are capital letters, and that's where the whole confusion was coming from. If the user is linked with the original value provided from the sub claim everything works fine
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminLinkProviderForUser.html
{
"DestinationUser": {
"ProviderAttributeName": "string",
"ProviderAttributeValue": "string",
"ProviderName": "string"
},
"SourceUser": {
"ProviderAttributeName": "string",
"ProviderAttributeValue": "string" //ensure that value is same as in sub claim,
"ProviderName": "string"
},
"UserPoolId": "string"
}
Related
I have setup a SuperSet with a Keycloak integrated as an authentication provider. In the official API documentation, we can get the access token by calling /security/login API with corresponding credential data provided. The example of the document is using "db" as provider. In my case, I believe I should change this option. However, I have tried to search for the suitable provider name in my scenario but no luck (i.g. ido,idoc,none of them works..)...
Does anyone know what provider name should I put in this case?
POST /security/login
Request body
{
"password": "complex-password",
"provider": "db",
"refresh": true,
"username": "admin"
}
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!
I am not able to get custom attribute in ID_TOKEN returned from AWS Cognito after successful user login.
Steps I tried :
1.Created user pool
2.Created app client and checked the custom attribute(customattrib1,customattrib2)
User Pool screen :
Check custom attribute in app client config
3.Created user using admin-create-user api
Below image shows the value for user attributes:
4.Signed in user using aws-cognito-auth.js in client app.The ID token returned do not contain the custom attribute.
ID_TOKEN
{
"at_hash": "PKfjYDaiEty5mUOyJZlPQA",
"sub": "639d5016-2bd3-4c6f-b82d-21ae38071b09",
"email_verified": true,
"iss": "https://cognito-idp.ap-south-1.amazonaws.com/ap-south-1_XXXXXXX",
"phone_number_verified": true,
"cognito:username": "testuser",
"aud": "XYXYXYXYX",
"token_use": "id",
"auth_time": 1549349674,
"phone_number": "##########",
"exp": 1549353274,
"iat": 1549349674,
"email": "testuser#somedomain.com"
}
I have already checked links below, which had some info regarding this issue, but nothing helped so far.
Adding Cognito custom attributes post pool creation?
Cognito User Pool custom attributes do not show up in the ID token if user pool is configured with a SAML identity provider
Cognito User Pool custom attributes do not show up in the ID token if user pool is configured with a SAML identity provider
https://www.reddit.com/r/aws/comments/a07dwg/cognito_add_custom_attribute_to_jwt_token/
Please help me figure out if I am missing something..
In your Cognito user pool go to General Settings -> App Clients, then for each app client click on Show Details, then Set attribute read and write permissions. Check the checkbox next to your attribute name under Readable Attributes.
In your Cognito user pool go to App client settings -> Allowed OAuth Scopes and enable profile scope.
I had the same trouble and your question came up when I was searching for a solution.
My custom attributes started to appear in ID token when I enabled profile scope in 'App client settings'. (available at: AWS console-> 'User pools'-> click your pool -> 'App client settings' -> 'Allowed OAuth Scopes')
(BTW: I was misled by this sentence from the documentation: "The openid scope returns all user attributes in the ID token that are readable by the client". In my case openid scope was not enough.)
For me the problem was that I was getting my token (after authenticating with the Amplify js library) from:
Auth.currentSession().then(u => u.getAccessToken().getJwtToken())
Instead of:
Auth.currentSession().then(u => u.getIdToken().getJwtToken());
After changing it worked fine! Hope it helps!
For anyone coming here that is using the Amplify SDK like the OP - Copy and Paste calls out an important point that your client ALSO needs to explicitly ask for the scope.
In my case we I am using Angular. After adding "profile" to the User Pool "Allowed OAuth Scopes" - you also need to specify it in your client configuration:
Environment
Clustered API Manager 2.1.0 (two workers, one manager) and Identity Server 5.3.0 (two workers)
Issue
When logging into a webpage, I login as the application creator. Which causes the JWT claims to be the application creator instead of user who logged in.
Idea of issue
My idea of the problem is since the application was created by one user it uses applications client_id as authorization. Since the application is linked to the user who created the application, it logs others in as that user. So when we get the JWT claims back it has the information for the user who created the application instead of the user who logged in.
What types of things should I be looking at as a possible fix for this problem? I believe the JWT configurations can fix the problem. Which I know either api-manager.xml or identity.xml could have the answer.
How do I get the correct JWT claims back from the access token?
JWT Sample
{
"sub": "12345678",
"http://wso2.org/claims/applicationtier": "Unlimited",
"http://wso2.org/claims/client_id": "bzuM29gewg5gxazegXiNfkwsgz",
"http://wso2.org/claims/keytype": "SANDBOX",
"http://wso2.org/claims/version": "1.0.1",
"iss": "wso2.org/products/am",
"http://wso2.org/claims/applicationname": "CellPhones",
"http://wso2.org/claims/enduser": "null",
"http://wso2.org/claims/enduserTenantId": "null",
"http://ourdomain.com/claims/client_rest_of_name": "Ben",
"http://ourdomain.com/claims/client_surname": "Kenobi",
"http://wso2.org/claims/subscriber": "DOMAIN/benk",
"http://wso2.org/claims/tier": "Bronze",
"http://ourdomain.com/claims/client_claim_source": "CLIENT_SUBSCRIBER",
"http://ourdomain.com/claims/client_roles": [
"Full-Time Employee",
"Employee",
"Technology Engineer"
],
"http://wso2.org/claims/applicationid": "8",
"http://wso2.org/claims/usertype": "APPLICATION_USER", <- Client app
"exp": 1493232200,
"email": "user#gmail.com",
"http://wso2.org/claims/apicontext": "/jwt/1.0.1"
}
This was an issue with custom claims. Just had to alter code.
I'm trying to build an application that will help manage different ad accounts for different customers (multiple businesses, so it has to handle multiple ad accounts). I'm looking at the ads API documentation for ad account groups, and the examples aren't working in the graph API explorer. Things like GET requests to
https://graph.facebook.com/v2.4/<AD_ACCOUNT_GROUP_ID>/users
(from Facebook's documentation here) are returning the following error:
{
"error": {
"message": "(#275) Ad account cannot be determined for this request",
"type": "OAuthException",
"code": 275
}
}
As best as I can tell, the documentation is incorrect - I think it can't tell the difference between an ad account ID and an ad group ID (I know this pattern is also used for things like managing custom audiences which is why I'm guessing it thinks it should be seeing an add account ID). Is there a better guide on updating ad account group membership via the API I can reference, or an endpoint I can substitute in for Facebook's official documentation?
First off please note at this time, you can still use v2.3 Facebook graph calls, prior versions have been deprecated by Facebook.
You will get this error. If you try older graph versions:
{
"error": {
"message": "(#2635) You are calling a deprecated version of the Ads API. Please update to the latest version.",
"type": "OAuthException",
"code": 2635
}
}
At the time of this writing you can use v2.3 calls and it still works.
Next I presume you want to know all the users, within accounts so the first thing to do is to get the list of accounts... Then iterate through each.. on an ad account basis.
Look up the api call to get a list of ad accounts, for a Facebook user id.
You will need the ad_read permission on the token in order to get the list.
Your api call should then look like this to get the users on each account.
Notice I put act_ in front of the actual account id. This is required and tells Facebook you are dealing with an ad account.
https://graph.facebook.com/v2.3/act_999998730499999/users?access_token=CAZZZZZpCZBjEBAJmcyfqbcluGAJZCtqfv4kI6CtLC7JGHaJ7IO2ImGCfkQFZC9NXCAZC2CAbtEdQcWMYFpqsFAkgJVqNqjnKGQkMrukyl53WZBIdq7vofFYyxvaTJTsWVOQhWTrjNoox0QqRCt3vGaDsRGHLBFDxqKLfXOcDKfS1oppj1nDjKdPe2GHYrHirlBkhxWS95MNgW7ajZZZZZ
Note: I have added ZZZZZs and 9999s in places to disguise my actual account and token.
The token used must be the ad account holder's token for the app. The user must have authorized the app for the ad_read permission on this token creation for the call to work.
The result looks like this:
{
"data": [
{
"name": "Joe Programmer",
"permissions": [
1,
2,
3,
4,
5,
7
],
"role": 1001,
"id": "999995304499999"
}
]
}
When using the development access of the Facebook Ads API, you need to specify which adaccounts you are going to use and you must own the adaccounts.
See the following guide:
https://developers.facebook.com/docs/reference/ads-api/access#standard_accounts