AWS Cognito - Developer Authenticated Identities in JavaScript(Browser) - javascript

I have trouble getting credentials in a browser script.
The authentication server returns cognito_identityId and cognito_token.
Then I set a Cookie:
$.cookie('cognito_identityId')
$.cookie('cognito_token')
I tried to get credentials in 4 ways on the browser, and all Failed:
CognitoIdentityCredentials
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxxxxxxxxx'
IdentityId: $.cookie('cognito_identityId'),
Logins: {
'myauth': $.cookie('cognito_token')
}
});
// => Error: Missing required key 'IdentityId' in params
assumeRoleWithWebIdentity
var params = {
RoleArn: 'arn:aws:iam::xxxxxxxxxxxx:role/Cognito_xxxxxxxAuth_Role',
RoleSessionName: 'xxxxxxxxxxx',
WebIdentityToken: $.cookie('cognito_token'),
DurationSeconds: 900,
ProviderId: 'myauth'
};
var sts = new AWS.STS({apiVersion: '2011-06-15'});
sts.assumeRoleWithWebIdentity(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
// => AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity
PolicyDocument
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "us-east-1:xxxxxxxxxxxxx"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "authenticated"
}
}
}
]
}
GetCredentialsForIdentity
var params = {
IdentityId: $.cookie('cognito_identityId'),
Logins: {
"myauth": $.cookie('oauth.io_token')
}
};
var cognitoidentity = new AWS.CognitoIdentity({apiVersion: '2014-06-30'});
cognitoidentity.getCredentialsForIdentity(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log(data); // successful response
}
});
// => InvalidParameterException: Please provide a valid public provider
WebIdentityCredentials
AWS.config.credentials = new AWS.WebIdentityCredentials({
RoleArn: 'arn:aws:iam::xxxxxxxx:role/Cognito_xxxxxxxxxxAuth_Role',
WebIdentityToken: $.cookie('cognito_token')
});
// => Error: There were 2 validation errors:
// * MissingRequiredParameter: Missing required key 'IdentityPoolId' in params
// * MissingRequiredParameter: Missing required key 'IdentityId' in params
Questions:
What am I doing wrong?
What is the correct way to use this?
Thank you.
Thank you for your kindness.
I tyied your advice, but did not change.
Error messages.
POST https://cognito-identity.us-east-1.amazonaws.com/ 400 (Bad Request)
POST https://cognito-identity.us-east-1.amazonaws.com/ 400 (Bad Request)
Error: Missing required key 'IdentityId' in params
at fail (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:2163:37)
at validateStructure (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:2084:14)
at validateMember (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:2110:21)
at validate (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:2059:10)
at Request.VALIDATE_PARAMETERS (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:800:32)
at Request.callListeners (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:3913:20)
at callNextListener (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:3903:12)
at chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:787:9
at finish (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:126:7)
at chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:142:9
There are source code below link.
https://github.com/bisque33/my-custom-dictionary
and server side is a AWS Lambda Function.
var aws = require('aws-sdk');
aws.config.region = 'us-east-1';
var cognitoidentity = new aws.CognitoIdentity();
var identityPoolId = 'us-east-1:0dccff0d-5fd7-4d14-b38f-d27204feaecc';
console.log('Loading function');
exports.handler = function(event, context) {
console.log('token: %s', event.token);
var params = {
IdentityPoolId: identityPoolId,
Logins: {
'oauth.io': event.token
}
};
cognitoidentity.getOpenIdTokenForDeveloperIdentity(params,function(err,data){
if(err){
console.log(err);
context.fail('Something went wrong');
}else{
context.succeed(data);
}
});
};
This program is Google-Chrome-Extension.
AWS Lambda Function returns token by getOpenIdTokenForDeveloperIdentity.
app/scripts/popup.js calls Lambda Function and set cookies.
app/scripts/background.js calls AWS.config.credentials.get, and returns error.
Am I using it wrong?
Update for Additional Information
Thank you for the additional information.
Error appears on 104 line on background.js
AWS.config.credentials.get(function(){
and 115 line on background.js
dataset.synchronize(
And, My explaination was not enough. Facebook authentication needs the domain(ex. http :// example.com). However, Google-Chrome-Ext does not have domain. It has a domain 'chrome-extension://xxxxxxxxxxxxxxxxxxxx'. Then, I use https://oauth.io. It proxies any authentication and accepts chrome-extension domain.
Popup.js does Facebook authentication through oauth.io sdk. It gets a facebook token, and gives to getOpenIdTokenForDeveloperIdentity. I think facebook token.substr(0,14) is unique. But, If it is wrong, I use another unique identifier(ex. email-address.)
Sorry, I was wrong. AWS.config.credentials.get gives an Error:
Error: Invalid login token.
And, dataset.synchronize shows this Error:
Error: Missing required key 'IdentityId' in params

The first approach you have, using CognitoIdentityCredentials, is most likely the best approach for you to take. I can't spot exactly what's causing the error for you but lets try a couple things:
When using Developer Authenticated Identities, you do need to specify the IdentityId when initializing CognitoIdentityCredentials. You need to get the IdentityId value from the call to GetOpenIdTokenForDeveloperIdentity. However, you shouldn't need to preserve the IdentityId value in a cookie as CognitoIdentityCredentials will cache the id by default in the browser's local storage.
As for your Logins map: It looks like you're trying to use Developer Authenticated Identities. With the JavaScript SDK, use the key 'cognito-identity.amazonaws.com' and make sure the value is the token returned from your backend's call to getOpenIdTokenForDeveloperIdentity.
If you continue to have problem using the CognitoIdentityCredentials approach, please reply here with some more info such as the exact method/code you're calling when you receive the error message, and the traced output (i.e. with console.log('%o',..)) of the params input just before your call to the CognitoIdentityCredentials constructor.
Update Based on Additional Information Provided
I still need to know exactly which line of code you receive the error on, but based on the information provided I think I can still help...
Based on what I see in background.js, it looks like you're trying to initialize CognitoIdentityCredentials using a Developer Authenticated Identities provider. This is where I'm guessing that you're receiving the error.
However, in Popup.js, it looks like you're trying to authenticate the user with Facebook. If you're authenticating your users with Facebook, you should just pass the facebook access token into your Logins map when using Cognito. Just use graph.facebook.com as the key in the Logins map and the access token from Facebook. More detail on how to do this is in the Facebook Integration topic of the Amazon Cognito developer guide.
Facebook vs Developer Authenticated Identities
We can get Developer Authenticated Identities to work for you, but in this case, it doesn't look like the right solution for you since you're not actually doing any additional authentication on the identity in your Lambda function and the unique user identifier that you're passing into the getOpenIdTokenForDeveloperIdentity operation appears to be the facebook token, which is not good by the way since the token itself will change between user sessions even for the same user. Usually a good unique identifier is an email address or a user id used by an internal system.
Facebook Login & Redirects
Since you're ultimately trying to use Facebook for login and Amazon Cognito has built-in integration for Facebook, the best thing for you to do is get an access token from Facebook and pass in the Facebook token to Cognito's login map directly. I'm not sure if this will work with Auth.io or not (I'm just not familiar with it), but as long as Auth.io gives your JavaScript code a bonefide facebook token and you add the same Facebook App ID to both Auth.io and Amazon Cognito's Console, it should work. However, you mentioned you want to use Auth.io to avoid Facebook doing a redirect to a landing page. I could be mistaken, but I'm pretty sure if you're using Facebook's JavaScript SDK you won't need a redirect page. You should only need the redirect page if you're doing Facebook's Manually Build a Login Flow.

Related

endpoints_resolution_error in msal react

I was trying to acquire token from our Microsoft tenant. I have no knowledge about the Azure AD or whatsoever, because I only tasked to develop front end for our Microsoft Dynamics App in React. I only got some of the credential like tenant id, client id, client secret and resource.
I used MSAL Node library and function ConfidentialClientApplication() to acquire the token
But when I check it in the Ms. Edge's console log it throw an error
{"errorCode":"endpoints_resolution_error","errorMessage":"Error: could
not resolve endpoints. Please check network and try again. Detail:
ClientAuthError: openid_config_error: Could not retrieve endpoints.
Check your authority and verify the .well-known/openid-configuration
endpoint returns the required endpoints. Attempted to retrieve
endpoints from: verify
url","subError":"","name":"ClientAuthError","correlationId":""}
When I click the veryfy url (Cannot show you the url because it might contain sensitive information)
It shows all the metadata of the open id so I thought maybe it's normal.
But why is the error endpoints_resolution_error throwed when everything is normal?
Here is some snapshot of my code
const config = {
auth: {
clientId: clientID
authority: "https://login.microsoftonline.com/{tenantID}/",
clientSecret: clientSecret,
knownAuthorities: ["login.microsoftonline.com"],
protocolMode: "OIDC"
}
};
// Create msal application object
const cca = new msal.ConfidentialClientApplication(config);
// With client credentials flows permissions need to be granted in the portal by a tenant administrator.
// The scope is always in the format "<resource>/.default"
const clientCredentialRequest = {
scopes: ["resource/.default"], // replace with your resource
};
cca.acquireTokenByClientCredential(clientCredentialRequest).then((response) => {
console.log("Response: ", response);
}).catch((error) => {
console.log(JSON.stringify(error));
});
I've tried changing the authority and the protocol mode several times, but same result

How to make authenticated Cognito request

I can successfully run describeIdentityPool command for the Cognito Identity Pool with "Enable access to unauthenticated identities" enabled:
var AWS = require("aws-sdk");
let cognitoidentity = new AWS.CognitoIdentity();
var params = {IdentityPoolId: "myIdentityPoolID"};
cognitoidentity.describeIdentityPool(params, function(err, data) {
if (err) {
console.log(err, err.stack);
}
else {
console.log( "...getCredentials.describeIdentityPool.data:", data);
}
});
But if I disable the "Enable access to unauthenticated identities" check box I get the error:
NotAuthorizedException: Unauthenticated access is not supported for this identity pool.
Is there a way to authorize the request I make to Cognito Identity Pool to avoid this error?
That's completely expected. Almost all APIs on AWS require an access key id and a secret access key. The describeIdentityPool API is one of them.
If you enable access for unauthenticated users they receive temporary credentials that allow them to perform that action. One of the actions allowed by default is cognito-identity:*. This is the one that allows unauthenticated users to call this API. You will be able to find it in a role assigned to the "Unauthenticated identities" setting.
When you disable unauthenticated entities they no longer receive temporary credentials and are therefore unable to call that API.
Long story short: Works as expected.

How to validate JWT token from Google pub/sub push (No pem found for envelope)

Context
I'm following Google's RTDNs guide on enabling Real-Time Developer Notifications. I've successfully created the topic and subscription and have received the push notifications sent to the API that I have created. I would now like to authenticate and validate these messages. For that, I'm following this guide on Authentication and Authorization. Their developer documentation here and here has a seemingly useful example.
The Issue
After following the resources outlined above, I get the following error:
Error: No pem found for envelope: {"typ":"JWT","alg":"HS256"}
Relevant Code
const authClient = new OAuth2Client();
// ...
app.post('/pubsub/authenticated-push', jsonBodyParser, async (req, res) => {
// Verify that the push request originates from Cloud Pub/Sub.
try {
// Get the Cloud Pub/Sub-generated JWT in the "Authorization" header.
const bearer = req.header('Authorization');
const [, token] = bearer.match(/Bearer (.*)/);
// Verify and decode the JWT.
// Note: For high volume push requests, it would save some network
// overhead if you verify the tokens offline by decoding them using
// Google's Public Cert; caching already seen tokens works best when
// a large volume of messages have prompted a single push server to
// handle them, in which case they would all share the same token for
// a limited time window.
// verifyIdToken is failing here with the `No pem found for envelope` error
const ticket = await authClient.verifyIdToken({
idToken: token,
audience: 'example.com',
});
// ...
} catch (e) {
res.status(400).send('Invalid token');
return;
}
res.status(200).send();
});
The Questions
From this, I'm assuming I need to have some public key.
Where do I get said public key?
Where do I put said public key so that the google client is initialized with it?
How can I generate an example JWT to test my endpoint?
Edits
I was able to find the source of this error in their code here:
if (!Object.prototype.hasOwnProperty.call(certs, envelope.kid)) {
// If this is not present, then there's no reason to attempt verification
throw new Error('No pem found for envelope: ' + JSON.stringify(envelope));
}
However, I've verified that the kid attribute does indeed exist in the decoded object:
{"alg":"RS256","kid":"7d680d8c70d44e947133cbd499ebc1a61c3d5abc","typ":"JWT"}
Turns out the kid was invalid and therefore threw the No pem found for envelope error. Once a valid kid was supplied, the error no longer persisted.

JWT verification error: JsonWebTokenError: invalid algorithm

i am trying to implement a single sign on for my web application. I am using gravitee.io for the access managment and token generation.
I followed the steps in gravitees quickstart tutorial and i am now at the point that i want to verify my id_token.
In order to do that i am using the node-jsonwebtoken library. i am using total.js for my backend (which should not be as important, but i still wanted to mention it).
What i have done so far.
i have my client-id and my client-secret as well as my domain secret in the total.js config file
./configs/myconfig.conf (key/secret is changed)
url : https://sso.my-graviteeInstance.com/am
client-id : myClientId
client-secret : uBAscc-zd3yQWE1AsDfb7PQ7xyz
domain : my_domain
domain-public-key : EEEEEB3NzaC1yc2EAAAADAQABAAABAQCW4NF4R/sxG12WjioEcDIYwB2cX+IqFJXF3umV28UCHZRlMYoIFnvrXfIXObG7R9W7hk6a6wbtQWERTZxJ4LUQnfZrZQzhY/w1u2rZ3GEILtm1Vr1asDfAsdf325dfbuFf/RTyw666dFcCcpIE+yUYp2PFAqh/P20PsoekjvoeieyoUbNFGCgAoeovjyEyojvezxuTidqjaeJvU0gU4usiiDGIMhO3IPaiAud61CVtqYweTr2tX8KabeK9NNOXlTpLryBf3aTU1iXuU90mijwXZlmIzD28fWq+qupWbHcFZmmv3wADVddnxZHnFIN7DHGf5WVpb3eLvsGkIIQpGL/ZeASDFa
i added a model to handle the login workflow for total.js in order to get the jwt tokens from gravitee by REST-call.
So far everything works as expected. a session is created and stores the response in it. the gravitee response is the expected json which looks like this
{
access_token: 'some-long-token',
token_type: 'bearer',
expires_in: 7199,
scope: 'openid',
refresh_token: 'another-long-token',
id_token: 'last-long-token'
}
I split up the tokens in seperate cookies because when i tried to save them as a single cookie, i got an error that told me the cookie exceeds the 4096 length limit.
So far everything works just fine. in the frontend ajax call the success callback will be executed, just setting the window.location.href='/'; to call the dashboard of my application. I set this route to be accessible only when authorized, so that when my dashboard is called, the onAuthorize function is called by totaljs.
F.onAuthorize = function (req, res, flags, callback) {
let cookie = mergeCookies(req.cookie);
// Check the cookie length
if (!cookie || cookie.length < 20) {
console.log(`cookie not defined or length to low`);
return callback(false);
}
if (!cookie) {
console.log(`cookie undefined`);
return callback(false);
}
// Look into the session object whether the user is logged in
let session = ONLINE[cookie.id];
if (session) {
console.log(`there is a session`);
// User is online, so we increase his expiration of session
session.ticks = F.datetime;
jwt.verify(
session.id_token,
Buffer.from(CONFIG('client-secret')).toString('base64'),
function(err, decoded){
if (err) {
console.log(`jwt verify error: ${err}`);
return callback(false);
}
console.log(`decoded token user id: ${decoded.sub}`);
return callback(true, session);
})
}
console.log(`false`);
callback(false);
};
I also tried to just send the CONFIG('client-secret') without buffering. I also tried to send the CONFIG('domain-public-key'). But the error i get is always the same:
jwt verify error: JsonWebTokenError: invalid algorithm
When i copy and paste the id_token into the debugger at jwt.io with algorithm beeing set to RS256 i'll see the following decoded values:
// header
{
"kid": "default",
"alg": "RS256"
}
// payload
{
"sub": "some-generated-id",
"aud": "myClientId",
"updated_at": 1570442007969,
"auth_time": 1570784329896,
"iss": "https://sso.my-graviteeInstance.com/am/my_domain/oidc",
"preferred_username": "myUsername",
"exp": 1570798729,
"given_name": "Peter",
"iat": 1570784329,
"family_name": "Lustig",
"email": "peter.lustig#domain.com"
}
i copied the public key from my domain in to the respective textfield and i also tried to use the client-secret. no matter what i do, the error i am getting here is
Warning: Looks like your JWT signature is not encoded correctly using
base64url (https://www.rfc-editor.org/rfc/rfc4648#section-5). Note that
padding ("=") must be omitted as per
https://www.rfc-editor.org/rfc/rfc7515#section-2
I dont understand why there is an algorithm error when i try to verify the token in my backend and some encoding error at jwt.io debugger.
can somebody explain to me on how to fix the issue? Thanks in advance
Pascal
edit: changed title

Failed sending mail with googleapis' service account and JWT auth in nodejs

I'm trying to send an email using a service account and JWT authentication and keep getting and error with a very unhelpful message: { code: 500, message: null }
This code snippet is from the following StackOverflow link: Failed sending mail through google api in nodejs
It seems like the solution there was to change the key in the parameters to resource instead of message but it's not working for me. This is strange because in the JS example in the docs (https://developers.google.com/gmail/api/v1/reference/users/messages/send) it claims the key is still message
I'm authenticating with
var jwtClient = new google.auth.JWT(config.SERVICE_EMAIL, config.SERVICE_KEY_PATH, null, config.ALLOWED_SCOPES);
then sending an email with
jwtClient.authorize(function(err, res) {
if (err) return console.log('err', err);
var email_lines = [];
email_lines.push("From: \"Some Name Here\" <rootyadaim#gmail.com>");
email_lines.push("To: hanochg#gmail.com");
email_lines.push('Content-type: text/html;charset=iso-8859-1');
email_lines.push('MIME-Version: 1.0');
email_lines.push("Subject: New future subject here");
email_lines.push("");
email_lines.push("And the body text goes here");
email_lines.push("<b>And the bold text goes here</b>");
var email = email_lines.join("\r\n").trim();
var base64EncodedEmailSafe = new Buffer(email).toString('base64').replace(/\+/g, '-').replace(/\//g, '_');
var params = {
auth: jwtClient,
userId: "myaddress#gmail.com",
resource: {
raw: base64EncodedEmailSafe
}
};
gmail.users.messages.send(params, function(err, res) {
if (err) console.log('error sending mail', err);
else console.log('great success', res);
});
}
The comments in the library seem to say that resource is the correct property as well (https://github.com/google/google-api-nodejs-client/blob/master/apis/gmail/v1.js)
What am I missing?
According to #ryanseys on github
You cannot authorize Gmail API requests with JWT, you must use OAuth 2.0 because it needs to be auth'd to a specific user. Or else you'd be able to do some really shady things like send messages impersonating someone else. The Google APIs Explorer is authenticated with OAuth 2.0 that's why it works. See https://developers.google.com/gmail/api/auth/about-auth for more information.
As you can see in Failed sending mail through google api in nodejs, auth: OAuth2Client, they are using the OAuth2 client to authenticate. There is currently no way for you to send messages using the GMail API without authenticating as a specific GMail user. Service accounts do not have access to GMail the same way that regular users do.
Hopefully this helps someone else out there trying to use a service account to send mail!

Categories

Resources