I am using a nodejs lamdas to get authentication tokens from AWS Cognito and in the front end code I am using the "aws-sdk": "^2.74.0" javascript / typescript sdk :
var creds = new AWS.CognitoIdentityCredentials({
IdentityPoolId: environment.identityPoolId
})
AWS.config.update({
region: environment.region,
credentials: creds
});
var lambda = new AWS.Lambda();
when I sign the token and identity id to my AWS.CognitoIdentityCredentials.params the following way :
creds.params['IdentityId'] = output.identityId;
creds.params['Logins'] = {};
creds.params['Logins']['cognito-identity.amazonaws.com'] = output.token;
creds.expired = true;
I am able to get the following lamda.invoke calls to use authenticated role arn configured for my federated identity pool.
The issue I am having is when I try to sign the user out. I read many forums posts but nobody seem to have a clear explanation on this. I tried using the following in my front end logout function which didn't help:
creds.clearCachedId();
creds.refreshPromise();
any examples showing how the javascript aws-sdk would clear the session/authentication information and switch back to unauthenticated user role arn or logout user and update the config so that next call a AWS service ( lambda.invoke in my case ) would use the unauthenticated role arn instead of trying to use the authenticated role. So it seems Cognito is not aware of the sigout, or I am missing the call to make it aware. I was hoping creds.clearCachedId() would do it but apparently not.
Well it turns out I needed to clear the creds.params manually :
creds.params['IdentityId'] = null;
creds.params['Logins'] = null;
I would think the below would do it, but apparently not.
creds.clearCachedId();
creds.refreshPromise();
Related
I am currently working on multi-tenancy using admin auth as per the documentation: https://cloud.google.com/identity-platform/docs/multi-tenancy
I initialized a Tenant Auth with a registered tenantId:
const tenantManager = admin.auth().tenantManager();
const tenantAuth = tenantManager.authForTenant(tenantId);
let tenantData = await tenantManger.getTenant(tenantId)
When I perform any operations using this tenantAuth or get the tenant data, I get the following error
An internal error has occurred. Raw server response: "{"error":{"code":403,"message":"The caller does not have permission","status":"PERMISSION_DENIED"}}"
I can understand it is an IAM role required for the service account key I used for initializing the admin SDK. Can anyone tell what's the valid role to be added?
I developed a chrome extension using Rally's WSAPI v2.0, and it basically does the following things:
get user and project, and store them
get current iteration everytime
send a post request to create a workitem
For the THIRD step, I sometimes get error ["Not authorized to perform action: Invalid key"] since end of last month.
[updated]Error can be reproduced everytime if I log in Rally website via SSO before using the extension to send requests via apikey.
What's the best practice to send subsequent requests via apikey in my extension since I can't control end users' habits?
I did see some similar posts but none of them is helpful... and in case it helps:
I'm adding ZSESSIONID:apikey in my request header, instead of user /
password to authenticate, so I believe no security token is needed
(https://comm.support.ca.com/kb/api-key-and-oauth-client-faq/kb000011568)
url starts with https://rally1.rallydev.com/slm/webservice/v2.0/
issue is fixed after clearing cookies for
https://rally1.rallydev.com/, but somehow it appears again some time
later
I checked the cookie when the issue was reproduced, and found one with name of ZSESSIONID and its value became something else rather than the apikey. Not sure if that matters though...
code for request:
function initXHR(method, url, apikey, cbFunc) {
let httpRequest = new XMLHttpRequest();
...
httpRequest.open(method, url);
httpRequest.setRequestHeader('Content-Type', ' application\/json');
httpRequest.setRequestHeader('Accept', ' application\/json');
httpRequest.setRequestHeader('ZSESSIONID', apikey);
httpRequest.onreadystatechange = function() {
...
};
return httpRequest;
}
...
usReq = initXHR ('POST', baseURL+'hierarchicalrequirement/create', apikey, function(){...});
Anyone has any idea / suggestion? Thanks a million!
I've seen this error when the API key had both read-only and full-access grants configured. I would start by making sure your key only has the full-access grant.
I am managing a Keycloak realm with only a single, fully-trusted external IdP added that is intended to be the default authentication mechanism for users.
I do not want to allow user to register, i.e. I want to manually create a local Keycloak user, and that user should then be allowed to link his external IdP account to the pre-existing Keycloak account, having the email address as common identifier. Users with access to the external IdP but without an existing Keycloak account should not be allowed to connect.
I tried the following First Broker Login settings, but whenever a user tries to login, he gets an error message (code: invalid_user_credentials).
Do you have any idea what my mistake might be?
Looks like they integrated this feature in version 4.5.0.
See automatic account link docs.
Basically you need to create a new flow and add 2 alternative executions:
Create User If Unique
Automatically Link Brokered Account
According to the doc: https://www.keycloak.org/docs/latest/server_admin/index.html#detect-existing-user-first-login-flow, you must create a new flow like this:
et voilà :)
As per this discussion:
https://keycloak.discourse.group/t/link-idp-to-existing-user/1094/5
It’s a bug in keycloak and they seem to be a reluctant to fix it for
whatever reason. I have very few users so I solved it by manually
querying the idp for the information keycloak uses and then copying it
into the relevant fields in the UI. So there is no sign up process for
my users I just make them myself. Obviously that’s a poor solution
though, what we really need is someone to take over that PR and
persuade the maintainers to merge it.
This is the PR: https://github.com/keycloak/keycloak/pull/6282
As it is described in this GitHub issue response the solution is to use a JavaScript authenticator that handles this.
In order to do so, you need to do the folowing:
Enable [custom authenticators using JavaScript in your server[(https://www.keycloak.org/docs/latest/server_installation/#profiles) by https://stackoverflow.com/a/63274532/550222creating a file profile.properties in your configuration directory that contains the following:
feature.scripts=enabled
Create the custom authenticator. You have to create a JAR file (essentially a ZIP file) with the following structure:
META-INF/keycloak-scripts.json
auth-user-must-exist.js
The content of the files are in this Gist, but I am including them here as well:
META-INF/keycloak-scripts.json:
{
"authenticators": [
{
"name": "User must exists",
"fileName": "auth-user-must-exists.js",
"description": "User must exists"
}
]
}
auth-user-must-exist.js:
AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError")
ServicesLogger = Java.type("org.keycloak.services.ServicesLogger")
AbstractIdpAuthenticator = Java.type("org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator")
IdpCreateUserIfUniqueAuthenticator = Java.type("org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticator")
var IdpUserMustExists = Java.extend(IdpCreateUserIfUniqueAuthenticator)
function authenticate(context) {
var auth = new IdpUserMustExists() {
authenticateImpl: function(context, serializedCtx, brokerContext) {
var parent = Java.super(auth)
var session = context.getSession()
var realm = context.getRealm()
var authSession = context.getAuthenticationSession()
if (authSession.getAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO) != null) {
context.attempted()
return
}
var username = parent.getUsername(context, serializedCtx, brokerContext)
if (username == null) {
ServicesLogger.LOGGER.resetFlow(realm.isRegistrationEmailAsUsername() ? "Email" : "Username")
authSession.setAuthNote(AbstractIdpAuthenticator.ENFORCE_UPDATE_PROFILE, "true")
context.resetFlow()
return
}
var duplication = parent.checkExistingUser(context, username, serializedCtx, brokerContext)
if (duplication == null) {
LOG.info("user not found " + username)
context.failure(AuthenticationFlowError.INVALID_USER)
return
} else {
authSession.setAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO, duplication.serialize())
context.attempted()
}
}
}
auth.authenticate(context)
}
Then, you can define as follows:
User Must Exist -> ALTERNATIVE
Automatically Set Existing User -> ALTERNATIVE
Honestly i am surprised by the keycloak auto creating behavior. I tried to add new Authentication flow as descibed here https://www.keycloak.org/docs/latest/server_admin/index.html#automatically-link-existing-first-login-flow
My flow :
1 - Create User If Unique [ALTERNATIVE]
2 - Automatically Link Brokered Account [ALTERNATIVE]
My use case : Authenticating users from Github ( Github as IDP )
Result : when a github user logon with an existing "username" keycloak links the github account to my local user ( based on his username ). I expected using his email instead of username.
I follow the steps described in the link: http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-integrating-user-pools-with-identity-pools.html to integrate my user pool with cognito identity. But every time I am trying to access amazone S3 using the Authentication providers I get the following error:
E/CognitoCachingCredentialsProvider: Failure to get credentials
com.amazonaws.services.cognitoidentity.model.NotAuthorizedException:
Logins don't match. Please include at least one valid login for this
identity or identity pool. (Service: AmazonCognitoIdentity; Status
Code: 400; Error Code: NotAuthorizedException; Request ID:
ff4da8ad-9a96-11e6-9c64-67a5c841c727)
at
com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:712)
at
com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:388)
at
com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:199)
at
com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient.invoke(AmazonCognitoIdentityClient.java:558)
at
com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient.getId(AmazonCognitoIdentityClient.java:444)
at
com.amazonaws.auth.AWSAbstractCognitoIdentityProvider.getIdentityId(AWSAbstractCognitoIdentityProvider.java:172)
at
com.amazonaws.auth.AWSEnhancedCognitoIdentityProvider.refresh(AWSEnhancedCognitoIdentityProvider.java:76)
at
com.amazonaws.auth.CognitoCredentialsProvider.startSession(CognitoCredentialsProvider.java:561)
at
com.amazonaws.auth.CognitoCredentialsProvider.getCredentials(CognitoCredentialsProvider.java:371)
at
com.amazonaws.auth.CognitoCachingCredentialsProvider.getCredentials(CognitoCachingCredentialsProvider.java:441)
at
com.amazonaws.auth.CognitoCachingCredentialsProvider.getCredentials(CognitoCachingCredentialsProvider.java:76)
at
com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:4369)
at
com.amazonaws.services.s3.AmazonS3Client.putObject(AmazonS3Client.java:1704)
at
com.amazonaws.mobileconnectors.s3.transferutility.UploadTask.uploadSinglePartAndWaitForCompletion(UploadTask.java:203)
at
com.amazonaws.mobileconnectors.s3.transferutility.UploadTask.call(UploadTask.java:85)
at
com.amazonaws.mobileconnectors.s3.transferutility.UploadTask.call(UploadTask.java:44)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:864).
Here is the code:
public static TransferUtility getTransferUtility(Context context) {
if (sTransferUtility == null) {
sTransferUtility = new TransferUtility(getS3Client(context.getApplicationContext()),
context.getApplicationContext());
}
return sTransferUtility;
}
public static AmazonS3Client getS3Client(Context context) {
if (sS3Client == null) {
sS3Client = new AmazonS3Client(getCredProvider(context.getApplicationContext()));
}
return sS3Client;
}
private static CognitoCachingCredentialsProvider getCredProvider(Context context) {
if (sCredProvider == null) {
sCredProvider = new CognitoCachingCredentialsProvider(
context.getApplicationContext(),
Constants.COGNITO_POOL_ID,
Regions.EU_WEST_1);
Map<String, String> logins = new HashMap<>();
logins.put("cognito-idp.eu-west-1.amazonaws.com/eu-west-1_xxxxxxxxx", idToken);
sCredProvider.setLogins(logins);
}
return sCredProvider;
}
Here how I get the token
AuthenticationHandler authenticationHandler = new AuthenticationHandler() {
#Override
public void onSuccess(CognitoUserSession cognitoUserSession, CognitoDevice device) {
Log.e(TAG, "***Auth Success***");
idToken = cognitoUserSession.getIdToken().getJWTToken();
AppHelper.setCurrSession(cognitoUserSession);
AppHelper.newDevice(device);
closeWaitDialog();
launchUser();
}
The transferutility is part of com.amazonaws.mobileconnectors.s3.transferutility package.
Thank you for your help.
felini
The problem may be configuration or the way you have created your token (the provider name part looks right). But most likely you just need to GetIdResult.
"Logins don't match. Please include at least one valid login for this identity or identity pool." is coming from the "AWSCognitoIdentityService.GetCredentialsForIdentity" api request (in java i think it is GetCredentialsForIdentityResult)
This can happen when you have an identityId for one identity, then you provide logins for another. If you change identities you need to do a "AWSCognitoIdentityService.GetId" (in java i think this is GetIdResult)
The error is telling you that either it could not find the identity provider associated with the identity pool, or the pool does not have that identity provider configured (I think this generates a different error but not sure) , or it could not associate the identityId with the logins entry token (if the username claim in the ID token did not match the identity it had for instance).
I think you need to either fix your logins dictionary, or do a GetIdResult call to make sure you have the right identityId for the logins hash you are providing.
Or... if it is configuration, make sure that you have the user pool and client id properly configured in the Authentication Providers list as a Cognito user pool authentication provider. Note that if you also configure it in IAM as an identity provider you must have the audience match that same client id. (which also works).
Note: This problem might happen if you configured "unauthenticated" access, then tried to get credentials with a logins hash, but using the unauthenticated identity. I think you would need to do a getIdResult to switch.
It was a configuration problem. I added to an existing identity pool, Cognito user pool as Authentication Providers. I provided the user pool Id and client Id.
Then I click save changes. It was shown in green on the dashboard that my changes was saved. But in reality it was not! That was the reason of the error.
As solution I created a new identity pool from scratch and added during the creation, cognito user pool as authentication provider. Only then it was properly saved after the pool creation.
In case anyone else has the same warning, it is possible to login to a Cognito Identity Pool with an unverified email, but that same user will not be able to authorise against a Cognito Identity Provider, instead this error will be thrown:
Error: Logins don't match. Please include at least one valid login for
this identity or identity pool.
A user can be set to 'Confirmed' via the Cognito console. This does not equate to 'email verified'. The user CAN login to the pool though.
A fully unconfirmed user cannot login at all.
Here's my current operations:
1./ User accepts app and the app callback stores the oauth_token and oauth_token_secret into the database (as access_token and access_token_secret).
2./ We use a cli script to handle autoposting to twitter. We load the twitter oauth object as follows:
public function post()
{
$consumerKey = $this->getConsumerKey();
$consumerSecret = $this->getConsumerSecret();
$accessToken = $this->getAccessToken();
$accessSecret = $this->getAccessSecret();
$twitteroauth = new TwitterOAuth($consumerKey,$consumerSecret,$accessToken,$accessSecret);
$message = $this->getPostMessage();
$result = $twitteroauth->post('statuses/update', array('status' =>$message));
$this->log($result);
}
Now this assumes we are using the API consumer key and secret assigned to the app and the user's stored access tokens.
The result we are getting is:
Invalid or expired token
I don't quite understand why we are receiving this. We are using the access token and access token secret provided to us by twitter.
I did notice that there is a GET parameter oauth_verifier. This isn't something we need to be using somewhere?
In any case, I'm not quite sure whats wrong here.
Do I need to log in or something before doing posting?
your code is correct.
The problem is that the library's Util.urlParameterParse() method is broken.