I am trying to migrate my V2 application to the V3 SDK and I can't seem to figure out how to refresh the credentials after the following call throws a NotAuthorizedException with "Invalid login token. Token expired: 1615301743 >= 1615108625".
credentials = await cognitoIdentity.send(
new GetIdCommand({
Storage: config,
IdentityPoolId: config.get("IdentityPoolId"),
Logins: {
[`cognito-idp.${awsRegion}.amazonaws.com/${upid}`]: idToken,
},
}),
);
In V2 there was a method called refresh() on the Credentials object which I could call and by doing so refresh the credentials. How to do the same thing with the new API?
The following code sample (Check Use case 4) I've found in the following link:
https://www.npmjs.com/package/amazon-cognito-identity-js
//refreshes credentials using AWS.CognitoIdentity.getCredentialsForIdentity()
AWS.config.credentials.refresh(error => {
if (error) {
console.error(error);
} else {
// Instantiate aws sdk service objects now that the credentials have been updated.
// example: var s3 = new AWS.S3();
console.log('Successfully logged!');
}
});
It works for me when implemented in AWS Lambda. Hope this is what you are looking for.
Regards,
Edit:
I've just tested the following code, it works in my react-js app:
return new Promise((resolve, reject) =>
cognitoUser.authenticateUser(authenticationDetails, {
// If the provided credentials are correct.
onSuccess: function(result) {
var accessToken = result.getAccessToken().getJwtToken();
//POTENTIAL: Region needs to be set if not already set previously elsewhere.
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: IdentityPoolId, // Your identity pool id here.
Logins: {
// Change the key below according to the specific Region your User Pool is in.
`cognito-idp.${awsRegion}.amazonaws.com/${upid}`: result
.getIdToken()
.getJwtToken(),
},
});
//refreshes credentials using AWS.CognitoIdentity.getCredentialsForIdentity()
AWS.config.credentials.refresh(error => {
if (error) {
console.error(error);
} else {
resolve(AWS.config.credentials)
}
});
},
// If the provided credentials are incorrect.
onFailure: function(err) {
console.log(err);
reject(
err.message || JSON.stringify(err)
);
},
})
);
Related
Hello there dev community. I´m trying to debug a firebase function and being trying using several tutorials, but with no success...
I´ve tried
(https://medium.com/#mwebler/debugging-firebase-functions-with-vs-code-3afab528bb36)
(https://medium.com/#david_mccoy/build-and-debug-firebase-functions-in-vscode-73efb76166cf)
My purpose is to get google contacts.
functions/index.js
const { google } = require('googleapis');
const oauthUserCredential = require('./oauthUserCredential.json')
const OAuth2 = google.auth.OAuth2
const key = require('./serviceAccountKey.json')
const jwt = new google.auth.JWT(key.client_email, null, key.private_key, 'https://www.googleapis.com/auth/contacts')
exports.getGoogleContacts = functions.https.onCall(async (data, context) => {
const requestingUser = data.requestingUser
console.log('getGoogleContacts-requestingUser', requestingUser)
const oauth2Client = new google.auth.OAuth2(
'client_id',
'client_secret',
'http://localhost:5000/xxx-xxx/us-central1/OAuthCallbackUrl'
);
const contacts = google.people({
version: 'v1',
auth: oauth2Client,
});
console.log('contacts ?', contacts)
(async () => {
const { data: groups } = await contacts.people.get({
resourceName: 'contactGroups',
});
console.log('Contact Groups:\n', groups);
})()
jwt.authorize((err, response) => {
console.log('inside authorize')
if (err) {
console.error(err);
response.end();
return;
}
// Make an authorized request to list contacts.
contacts.people.connections.list({
auth: authClient,
resourceName: 'people/me'
}, function (err, resp) {
if (err) {
console.error(err);
response.end();
return;
}
console.log("Success");
console.log(resp);
response.send(resp);
});
});
// this is another approach I´ve tried, but it´s also not working
const oAuth2Client = new OAuth2(
oauthUserCredential.web.client_id,
oauthUserCredential.web.client_secret,
oauthUserCredential.web.redirect_uris,
)
oAuth2Client.setCredentials({
refresh_token: oauthUserCredential.refresh_token
})
return new Promise((resolve, reject) => {
console.log('[INSIDE PEOPLE CONNECTIONS]')
contacts.people.connections.list({
auth: oauth2Client //authetication object generated in step-3
}, function (err, response) {
if (err) {
console.log('contacts.people.connections error')
console.log(err)
reject(new Error(err))
} else if (response) {
console.log('contacts.people.connections response')
console.log(response)
resolve(response)
}
});
})
.then(result => { return { found: result } })
.catch(err => { return { error: err } })
})
I´ve tried several different approachs and followed different tutorials
(Using Google People API with Cloud Functions for Firebase)
(https://flaviocopes.com/google-api-authentication/)
(https://medium.com/#smccartney09/integrating-firebase-cloud-functions-with-google-calendar-api-9a5ac042e869)
(https://cloud.google.com/community/tutorials/cloud-functions-oauth-gmail)
but none of them show clearly how could I get my contacts list.
I was able to use a client side code by following this tutorial (https://labs.magnet.me/nerds/2015/05/11/importing-google-contacts-with-javascript.html)
but I thought that living the client_id, client_secret and apiKey exposed in the client side would be a security problem...
I´m submitting also a tutorial request to make it very clear how to get contacts list from google account using firebase functions.
The Error you are receiving is because the cloud function cannot find the function named xxxx to execute, as you have not defined any function named xxxx in the index.js file.
Your Cloud Function to execute name, according to the error message is xxxx but the function that you are calling in index.js is getGoogleContacts. Please make sure that these names are the same, for example change getGoogleContacts to xxxx or change function to execute to getGoogleContacts
I am trying to use token authentication with no success. I am wondering if anyone succeed in doing so, as the official ApiRTC documentation is weak on that topic.
1) I have activated secret key below from - Credentials screen
2) For token validation I have setup a service from API - Token authentication screen
3) I have the below code to create the user agent
function createUserAgent(token) {
ua = new apiRTC.UserAgent({
uri: 'token:' + token
});
ua.register({
id : useragentId
}).then(uaRegistered)
.catch(function (error) {
console.log("Registration error");
});
}
function uaRegistered(session) {
console.log("Registration OK");
}
4) This initializes a request to below address. And it fails with HTTP 401
GET https://cloud.apizee.com/api/v2/checkToken?token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhNWQxN2M1ZTVjOWZkYmRiNDJhYTgzMTJlMWQxMmEwYiIsImF1ZCI6ImFwaVJUQyIsImp0aSI6ImE5ZjU4NmNlLTcxMDctNDgxMS04ODYwLTQ5MjY4ODY2NjhiYiIsImlhdCI6MTU1OTg5OTA5MSwiZXhwIjoxNTU5OTAyNjkxLCJncmFudHMiOnsiaWRlbnRpdHkiOiJjbGk5OTQxOTgxNTgifX0.ZfQs_HgUXOWhCAlXB6fTMKhbT-pFslb9MK_JvXu2U5A 401 (Unauthorized)
5) I have also seen that no requests are made to my token validation service.
Thanks
edit: updates according to the answer
function createUserAgent(token) {
apiRTC.setLogLevel(apiRTC.LOG_LEVEL_DEBUG);
var registerInformation = {};
registerInformation.id = useragentId;
registerInformation.token = token;
ua = new apiRTC.UserAgent({
uri: 'apzkey:a5d17c5e5c9fdbdb42aa8312e1d12a0b'
});
$("#sessionStatus").text("Waiting for register response ");
ua.register(registerInformation).then(uaRegistered)
.catch(function (error) {
debugger;
console.log("Registration error");
$("#sessionStatus").text("Failed to register UA");
});
}
function uaRegistered(session) {
debugger;
console.log("Registration OK");
connectedSession = session;
$("#useragentId").text(useragentId);
$("#sessionUsername").text(session.getUsername());
$("#sessionStatus").text("Connected");
debugger;
}
Thanks for pointing this issue in documentation, we have done a first update for using an external validation service here :
https://dev.apirtc.com/authentication/index
On client side, you need to use following code :
registerInformation.token = "myToken"
ua.register(registerInformation).then(function(session) {
// Save session
connectedSession = session;
}).catch(function(error) {
// error
console.error('User agent registration failed', error);
});
the usage of token in uri is for users authentication on Apizee offers
i can't get the Credentials for my CognitoIdentity. When the User is successfully authenticated, he needs to get a Identity to access other AWS Services. In my case thats AWS IoT. But for somehow, i can't get any credentials.
This is the Error Message:
Error retrieving credentials: NotAuthorizedException: Access to
Identity 'eu-central-1:XXXXXXXXXX' is
forbidden.
My Code is almost exactly like the Tutorial on Github:
var cognitoUser = new AWSCognito.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log("Logged in");
console.log('access token + ' + result.getAccessToken().getJwtToken());
// window.location.href = "index.html";
AWS.config.region = AWSConfiguration.region;
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: AWSConfiguration.IdPoolId,
Logins : {
'cognito-idp.eu-central-1.amazonaws.com/eu-central-1_XXXX' : result.getIdToken().getJwtToken()
}
});
var cognitoIdentity = new AWS.CognitoIdentity();
AWS.config.credentials.get(function(err, data) {
if (!err) {
console.log('retrieved identity: ' + AWS.config.credentials.identityId);
var params = {
IdentityId: AWS.config.credentials.identityId
};
cognitoIdentity.getCredentialsForIdentity(params, function(err, data) {
if (!err) {
thingShadows.updateWebSocketCredentials(data.credentials.AccessKeyId,
data.credentials.SecretKey,
data.credentials.SessionToken);
}
else {
console.log('error retrieving credentials: ' + err);
}
});
}
else {
console.log('error retrieving identity:' + err);
}
});
}
});
Please note that i skipped not related code.
authenticated users have full access to all AWS services i'm using.
I don't think you need to call cognitoIdentity.getCredentialsForIdentity(). Your IAM keys should be put into the AWS.config.credentials object when you call AWS.config.credentials.get(). You can access them directly in the callback you provide when you call it.
In other words, when you're logging out the retrieved identity: to the console, the credentials object should already have your secret key, access key id, and session token in it.
All of this (give or take a curly brace):
var params = {
IdentityId: AWS.config.credentials.identityId
};
cognitoIdentity.getCredentialsForIdentity(params, function(err, data) {
if (!err) {
thingShadows.updateWebSocketCredentials(data.credentials.AccessKeyId,
data.credentials.SecretKey,
data.credentials.SessionToken);
}
else {
console.log('error retrieving credentials: ' + err);
}
});
Can probably be replaced with something like this:
thingShadows.updateWebSocketCredentials(AWS.config.credentials.accessKeyId,
AWS.config.credentials.secretKey,
AWS.config.credentials.sessionToken);
If you pass in a Logins map with the user pool id and access token in it, the getCredentialsForIdentity() call might succeed; I didn't test it. I haven't yet run into a use case where I needed to use this particular API, and I suspect you don't need it either.
Source: I work on a 100% javascript application that uses both authenticated and unauthenticated Cognito identities. We don't call getCredentialsForIdentity() anywhere, and trying to insert it produced the same error you're getting.
I have the Accounts-UI config setup to store an offline token for google thusly:
if (Meteor.isClient) {
Accounts.ui.config({
requestOfflineToken: { google: true },
forceApprovalPrompt: { google: true },
requestPermissions: { google: ["https://mail.google.com/"] }
});
}
However, tokens seem to expire. I assume I need to somehow use the refreshToken. I'm not sure how though with meteor. Any help would be lovely. Thanks!
I recommend using Google API Node JS client to refresh your access tokens.
https://github.com/google/google-api-nodejs-client/
It's available as a server-side NPM package, so you might want to use this package to be able to npmRequire it in your Meteor app.
Use this packages.json config to load the latest googleapis package :
{
"googleapis": "2.1.5"
}
Then in your Meteor server code you'll be able to refresh the access tokens like this :
ES2015
const GoogleApis = Meteor.npmRequire('googleapis');
function getAccessToken(user) {
const googleService = user.services.google;
// is token still valid for the next minute ?
if (googleService.expiresAt < Date.now() + 60 * 1000) {
// then just return the currently stored token
return {
access_token: googleService.accessToken,
token_type: 'Bearer',
id_token: googleService.idToken,
expiry_date: googleService.expiresAt,
refresh_token: googleService.refreshToken,
};
}
// fetch google service configuration
const googleServiceConfig = Accounts.loginServiceConfiguration.findOne({
service: 'google',
});
// declare an Oauth2 client
const oauth2Client = new GoogleApis.auth.OAuth2(googleServiceConfig.clientId, googleServiceConfig.secret);
// set the Oauth2 client credentials from the user refresh token
oauth2Client.setCredentials({
refresh_token: user.services.google.refreshToken,
});
// declare a synchronous version of the oauth2Client method refreshing access tokens
const refreshAccessTokenSync = Meteor.wrapAsync(oauth2Client.refreshAccessToken, oauth2Client);
// refresh tokens
const tokens = refreshAccessTokenSync();
// update the user document with the fresh token
Meteor.users.update(user._id, {
$set: {
'services.google.accessToken': tokens.access_token,
'services.google.idToken': tokens.id_token,
'services.google.expiresAt': tokens.expiry_date,
'services.google.refreshToken': tokens.refresh_token,
},
});
//
return tokens;
}
Here is a full example of how to refresh your access tokens before using a google service.
function listMeteorChannel() {
// fetch a user you want to act on behalf who authorized offline access
const user = Meteor.users.findOne({
'services.google.refreshToken': {
$exists: true,
},
});
if (!user) {
return;
}
const googleServiceConfig = Accounts.loginServiceConfiguration.findOne({
service: 'google',
});
// declare oauth2 client and set credentials
const oauth2Client = new GoogleApis.auth.OAuth2(googleServiceConfig.clientId, googleServiceConfig.secret);
// get user access token
const tokens = getAccessToken(user);
oauth2Client.setCredentials(tokens);
// obtain the youtube service at version 3 and perform authentication at service level
const youtube = GoogleApis.youtube({
version: 'v3',
auth: oauth2Client,
});
// declare a synchronous version of youtube.channels.list
const youtubeChannelsListSync = Meteor.wrapAsync(youtube.channels.list, youtube.channels);
// fetch an info snippet from the Meteor official YouTube channel
const result = youtubeChannelsListSync({
part: 'snippet',
// Meteor channel ID
id: 'UC3fBiJrFFMhKlsWM46AsAYw',
});
result.items.forEach((item) => {
// display the channel title, which should be 'Meteor'
console.log(item.snippet.title);
});
}
Meteor.startup(listMeteorChannel);
I am building a phonegap app and use AWS Cognito to store the User data. In the description of Cognito, it is said, that the data is offline available. This does not work in my code:
var add_data;
function getCognitoData(){
var params = {
IdentityPoolId: COGNITO_IDENTITY_POOL_ID,
Logins: {
'graph.facebook.com': FACEBOOK_TOKEN
}
};
AWS.config.region = AWS_REGION;
AWS.config.credentials = new AWS.CognitoIdentityCredentials(params);
AWS.config.credentials.get(function(err) {
if (err) {
console.log("Error: "+err);
return;
}
console.log("Cognito Identity Id: " + AWS.config.credentials.identityId);
var syncClient = new AWS.CognitoSyncManager();
syncClient.openOrCreateDataset('myDataset', function(err, dataset) {
dataset.get('myKey', function(err, value) {
console.log(value, err);
});
add_data = function(thisid, thisval) {
dataset.put(thisid, thisval, function(err, record){
dataset.synchronize({
onSuccess: function(data, newRecords) {
console.log("success", newRecords);
},
onFailure: function(err) {
console.log("error", err);
},
onConflict: function(dataset, conflicts, callback) {
console.log("sync conflict", dataset, conflicts);
var resolved = [];
for (var i=0; i<conflicts.length; i++) {
resolved.push(conflicts[i].resolveWithRemoteRecord());
}
dataset.resolve(resolved, function() {
return callback(true);
});
}
});
});
}
});
});
}
The AWS Credentials for the Identity Pool and the Facebook Token are previously set, and work in the online mode, but I don't get the dataset data, when being offline.
Am I doing something wrong or is it generally not possible to get the Cognito Dataset data while being offline? I read, that the data is actually being held in the local storage.
I am using the current AWS SKD (Release v2.1.42) and the Amazon Cognito JS.
It's possible to get the data offline. You need to synchronize the dataset to get whatever contents may be inside, otherwise them being empty is expected. Are you doing that? If not, try doing that, but if so, can you update your code above?
There was a bug with the aws-sdk-js causing the offline bug. CognitoSync depends on aws-sdk-js. Should be working now as of aws-sdk-js#2.7.21. Make sure you update.