Asana NodeJS API - Cannot authenticate request error when using client.useOauth - javascript

I’m currently setting up and integration with Asana and want to have my users connected through the oAuth side of the library. I have successfully got the auth flow working in the sense of having the user navigated to Asana, authorize the app, then storing the access token and refresh token for use down the line. I’m aware that the access tokens expire after 1 hour and thus when making calls to the api it’s best to use the ‘client.useOauth’ side of things to pass in the access & refresh tokens allows the library to refresh it as needs be without having to do it manually which is great. But with my code implementation below I am getting an error:
/**
* Create a new asana client
*/
const client = Asana.Client.create({
clientId: asanaCredentials.id,
clientSecret: asanaCredentials.secret,
redirectUri: asanaCredentials.redirect
});
/*
* Get the access token and refresh token from the parameters passed in
*/
const { access_token, refresh_token } = data;
/*
* Pass the client access & refresh tokens
*/
client.useOauth({
access_token, refresh_token
});
/*
* Get the users workspaces
*/
const workspaces = client.workspaces.getWorkspaces().then((result) => { return result.data; });
/*
* Return the results back to the app
*/
return workspaces;
When this runs, I am getting the following error printed out:
Unhandled error Error: Cannot authenticate a request without first obtaining credentials
at
OauthAuthenticator.authenticateRequest (/workspace/node_modules/asana/lib/auth/oauth_authenticator.js:42)
at
doRequest (/workspace/node_modules/asana/lib/dispatcher.js:247)
at
(/workspace/node_modules/asana/lib/dispatcher.js:295)
at
Promise._execute (/workspace/node_modules/bluebird/js/release/debuggability.js:300)
at
Promise._resolveFromExecutor (/workspace/node_modules/bluebird/js/release/promise.js:481)
at
Promise (/workspace/node_modules/bluebird/js/release/promise.js:77)
at
Dispatcher.dispatch (/workspace/node_modules/asana/lib/dispatcher.js:244)
at
Dispatcher.get (/workspace/node_modules/asana/lib/dispatcher.js:321)
at
Function.Resource.getCollection (resource.js:36)
at
Workspaces.Resource.dispatchGetCollection (resource.js:77)
at
Workspaces.getWorkspaces (/workspace/node_modules/asana/lib/resources/gen/workspaces.js:73)
at
(/workspace/lib/src/integrations.js:132)
at
Generator.next ()
at
(/workspace/lib/src/integrations.js:8)
at
Promise ()
at
__awaiter (/workspace/lib/src/integrations.js:4)
Any help on what could be causing this would be greatly appreciated, thanks!

Fixed: My problem was the way in which I was passing in the two tokens, the client.useOauth needed to look like this:
client.useOauth({
credentials: { access_token, refresh_token }
});

Related

How to configure msalv2 js library to renew token on ExpiresOn instead of extExpiresOn?

I'm trying to use msalv2 js library to do SSO authentication. It mostly works but I find an issue with token expiring and how to renew it.
I am calling the aquireTokenRedirect function to get the new token and auto renew it but I see that it keeps returning the same token. I try to auto renew it when my server side code detects that the token is expired. It uses a msal jwt library to check.
My guess as to what is happening is, my app is sending the id token to the server (I use that instead of access token), and the server decodes it and sees the exp value, which is the ExpiresOn value from the msal js response.
When this time has expired, it will return a 401 error which is fine. However then I try to renew it on the client side, but find it gives me back the same token value...
I check the msal response from earlier and notice there is a extExpiresOn value which is a little later in time. So now i'm thinking the msal library only renews on this time.
So the question is, how can I configure msalv2 js library to renew on the ExpiresOn instead of the extExpiresOn?
Thanks
function getTokenRedirect(request) {
/**
* See here for more info on account retrieval:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
*/
request.account = myMSALObj.getAccountByUsername(username);
return myMSALObj.acquireTokenSilent(request)
.catch(error => {
console.warn("silent token acquisition fails. acquiring token using redirect");
if (error instanceof msal.InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenRedirect(request);
} else {
console.warn(error);
}
});
}

How to retrieve Token for API calls in cypress

Due to a lack of knowledge in JS, I do face with below problem.
I do automation testing with cypress and for external API endpoints, I need to use Dynamic Token that expires every 3600 secs.
I did some short research and find out that, for reaching to JWT Token I will need 1st SSO login (that my app has). Then use the below script.
But I don't know what to do after this.
it('the value of JWT Token should exist in localStorage', () => {
cy.getLocalStorage('JWT_DATA').then(lsData => {
cy.log('the token', lsData); // I get JWT Token in here
});
});
The result of this script is only printing the TOKEN.
But I need to store it somehow and then be able to reuse it for every different API endpoint call.
You can use Cypress.env for this. Then you can use the token throughout the test anywhere.
it('the value of JWT Token should exist in localStorage', () => {
cy.getLocalStorage('JWT_DATA').then((lsData) => {
cy.log('the token', lsData) // I get JWT Token in here
Cypress.env('token', lsData)
})
})
To use it, you can do
Cypress.env('token')

Unable to call function after getting token back from Azure to access Microsoft Graph

I'm writing an Azure function that takes an OAuth token from Microsoft, which I've been able to successfully obtain. I'm trying to use that token to access to Microsoft Graph. After I receive the token from Microsoft my function times-out after ten minutes and doesn't get past context.log('CALLING MS GRAPH'.) I'm new to Azure and haven't been able to figure out why I can't call my second function with the value of the token returned from Microsoft or with a hard coded value.
Any help is greatly appreciated :)
I've tried hardcoding the token value into the function, changing the timeout, and adding various context.log()'s - but can't get past receiving the token. I've also tried removing the .end() to my POST call.
const https = require('https');
const querystring = require('querystring');
getAccessToken = (context, callback) => {
const postData = querystring.stringify({
'client_id': {clientID},
'scope': 'https://graph.microsoft.com/.default',
'client_secret': {clientSecret},
'grant_type': 'client_credentials'
});
const msTokenOptions = {
hostname: 'login.microsoftonline.com',
port: 443,
path: `/${tenantID}}/oauth2/v2.0/token`,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
};
const oauthReq = https.request(msTokenOptions, (res) => {
res.setEncoding('utf8');
res.on('data', (d) => {
let accessToken = JSON.parse(d).access_token;
// Error happens here.
context.log('CALLING MSGRAPH')
// I never make it into the functions below, regardless of how they're called.
callback(accessToken);
accessMsGraph(accessToken)
});
});
oauthReq.on('error', (e) => {
context.log('ERROR: Problem obtaining MS Token. ' + e);
});
oauthReq.write(postData);
oauthReq.end();
return;
};
accessMsGraph = (token) => {
// GET request to MS Graph here - I never make it into this function.
};
module.exports = (context, req) => {
getAccessToken(context, (token) => {
context.log('Accessing graph')
accessMsGraph(context, token)
accessMsGraph('123456')
});
};
Please check the Access token lifespan which has been set in your tenant.
This actually isn't determined by Microsoft Graph but rather by Azure Active Directory.For a given tenant, the life-time can be configured using Configurable token lifetimes in Azure Active Directory (Public Preview).
This functionality is still in Preview, so functionality may change between now and general release.
This configuration is per tenant, service principal, or application. If you configure it on the application, then the policy will apply on multi-tenant applications unless superseded by a policy on the service principal or tenant level.
The maximum lifetime for an Access token is 24 hours (minimum is 10 minutes, default is 1 hour).
In general, rather than adjusting the lifetime of the Access Token you should rely on the Refresh Token instead. These have a much longer lifetime of 14 days.
Refresh Token
When a client acquires an access token to access a protected resource, the client also receives a refresh token. The refresh token is used to obtain new access/refresh token pairs when the current access token expires. A refresh token is bound to a combination of user and client. A refresh token can be revoked at any time, and the token's validity is checked every time the token is used. Refresh tokens are not revoked when used to fetch new access tokens - it's best practice, however, to securely delete the old token when getting a new one.

Testing (Firebase) FCM

In order to understand how to use Firebase Cloud Messaging, I am following this document:
https://firebase.google.com/docs/cloud-messaging/admin/send-messages?hl=en-us
Precisely I am looking at this section: Send to individual devices
I can see in the code that I need a registrationToken. My question is how do I concretely get one?
I first want to send a message to my own iPhone, laying on my desk and later to all iPhones of registered users.
When I work in IOS-Swift, you have to add this method in your AppDelegate.swift file:
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
let dataDict:[String: String] = ["token": fcmToken]
NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
// TODO: If necessary send token to application server.
// Note: This callback is fired at each app startup and whenever a new token is generated.
}
If you need to access the token directly, use this:
InstanceID.instanceID().instanceID { (result, error) in
if let error = error {
print("Error fetching remote instange ID: \(error)")
} else if let result = result {
print("Remote instance ID token: \(result.token)")
self.instanceIDTokenMessage.text = "Remote InstanceID token: \(result.token)"
}
}
For more information visit:
https://firebase.google.com/docs/cloud-messaging/ios/client
When working with FCM notifications, the devices generate tokens, which are renewed every so often, so if you need to send a push to a device you must know your token, you must implement a class that inherits FirebaseMessagingService, and overwrite an onNewToken method, this method is called in the background every time the device token is updated.
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the InstanceID token
* is initially generated so this is where you would retrieve the token.
*/
#Override
public void onNewToken(String token) {
Log.d(TAG, "Refreshed token: " + token);
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(token);
}
It is recommended that this token be sent to your server so that from there you can send the push to the devices with registered tokens. If you want to force a first token, you can use:
FirebaseInstanceId.getInstance().getInstanceId();

AWS Cognito - Developer Authenticated Identities in JavaScript(Browser)

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.

Categories

Resources