aws amplify javascript invalidate token on signOut - javascript

I am using javascript version of aws amplify in my reactjs application.
https://github.com/aws-amplify/amplify-js
When I call signOut method as mentioned in the document below:
https://github.com/aws-amplify/amplify-js/blob/a047ce73/packages/aws-amplify-react/src/Auth/SignOut.tsx#L109
code:
import { Auth } from 'aws-amplify';
async function signOut() {
try {
await Auth.signOut();
} catch (error) {
console.log('error signing out: ', error);
}
}
It just clears data from local storage and cookies. I want it to invalidate id token as well as access token. These tokens can still be used to access AppSync or API Gateway. We can use await Auth.signOut({global: true}); But there is a difference between these 2 methods. The former is used to signOut from the current session, but the other is used to signOut users from all the devices.
I read about calling invalidateTokens(true) here: https://github.com/aws-amplify/aws-sdk-android/pull/2415
Is this available in javascript version of aws-amplify? Please let me know any alternate solution to invalidate tokens on Auth.signOut() call.
thanks

As documented in the amplify documentation,
https://docs.amplify.aws/lib/auth/emailpassword/q/platform/react-native/#sign-out
Amazon Cognito now supports token revocation, and Amplify (from version 4.1.0) will revoke Amazon Cognito tokens if the application is online. This means the Cognito refresh token cannot be used anymore to generate new Access and Id Tokens.
You need to use the RevokeToken function to invalidate the token as documented in this one
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#revokeToken-property
var params = {
ClientId: 'STRING_VALUE', /* required */
Token: 'STRING_VALUE', /* required */
ClientSecret: 'STRING_VALUE'
};
cognitoidentityserviceprovider.revokeToken(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
Another option is to use the revoke endpoint.
https://docs.aws.amazon.com/cognito/latest/developerguide/revocation-endpoint.html

Related

How do I make authenticated requests to Amplify GraphQL using Firebase Auth as OIDC?

I need help setting up Firebase Auth + Amplify GraphQL. I'm trying to log in using federated sign with securetoken.google.com/PROJECT-ID as the provider, and it seems to log in alright because when I call Auth.currentAuthenticatedUser() I get the token, and when listening to Hub "signIn" event I get the token. My problem is making authenticated requests to my GraphQL API.
const signIn = async () => {
try {
// already logged in using firebase so I just need to get the token from the current user
const tokenResult = await currentUser?.getIdTokenResult()
await Auth.federatedSignIn('securetoken.google.com/PROJECT-ID', {
token: tokenResult?.token,
})
const res = await Auth.currentAuthenticatedUser()
console.log('token', res.token) // eyjhxxxxxxxxxx...
} catch (error) {
// ...
}
}
const client = new AWSAppSyncClient({
url: AppSyncConfig.aws_appsync_graphqlEndpoint,
region: AppSyncConfig.aws_appsync_region,
auth: {
type: AppSyncConfig.aws_appsync_authenticationType,
jwtToken: () => getToken(),
},
})
const getToken = async () => {
const token = await Cache.getItem('#accessToken')
return token
}
When calling Auth.currentSession() I get "No current user". Also, I do see the token in the Authorization header when I attempt to fetch data.
I have had a similar issue so here are some things you can have a look at.
In the Appsync in the AWS Console
https://eu-west-1.console.aws.amazon.com/appsync/home
Make sure that your primary authorization mode is set to Open Id Connect, or add another authorization provider specifying "OpenId Connect" if you are happy with the primary.
If that does not solve it, you can try to add the #aws_oidc AppSync directive to your GraphQL schema.
type Query {
getPosts:[Post!]! #aws_oidc
}
or
type Post
#model
#auth(
rules: [
{ allow: owner, provider: oidc }
...
more here: https://aws.amazon.com/blogs/mobile/graphql-security-appsync-amplify/
Lastly, if you have more than one authorization provider, you might have to switch the primary authorization provider to "OpenId Connect" - the issue I had was that Cognito (primary) blocked my secondary API Key authorization provider.
Update
AWS uses IAM roles for everything related to security. So when you authenticate with whichever authentication provider an IAM role will be assigned to that request, and that IAM role needs permission on the resource in question, like execute permission on GraphQL queries, scanning of DynamoDB tables etc. as per this image:
So you might need specific rules set in the IAM console for the IAM role in question - or at least check that it has permission - if not, you'll also get an unauthorized error message in the Appsync GraphQL query console.
more here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WIF.html?icmpid=docs_ddb_console
and here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/specifying-conditions.html?icmpid=docs_ddb_console
Try removing the cookie storage configuration in aws-exports.js may solve it. Maybe this helps you.
More discussion here Link-1 and Link-2

AWS Cognito login with Facebook

I have an Angular 10 site. I have an AWS Lambda (ASP.NET Core 3.1) that does authentication with AWS Cognito for users with email/password. But I want to allow users to also use Facebook (and eventually Google) to log in as well. In the Facebook/Google scenario my thought is not to allow access to AWS services directly for now (like S3, etc) but to interact with with my other lambda via a bearer token. I have a Cognito User Pool for which I created a Facebook identity provider and mappings. I read somewhere that I'd need an Identity Pool. So I created that and put in my Cognito user pool as a provider as well as Facebook.
Using the JavaScript code:
loginWithFacebook = () => {
const login$ = from(this.facebookService.login());
login$.subscribe(
(response: LoginResponse) => {
console.log(response);
this.facebookLoginToAWS(response);
},
error => {
console.error(error);
}
);
};
I can get a Facebook auth response no problem. Then using this code that you see on every blog, Stack Overflow post, and even in AWS's documentation (of course, substituting my own IdenityPoolId):
private facebookLoginToAWS = (facebookResponse: LoginResponse) => {
console.log('facebookLoginToAWS', facebookResponse);
if (facebookResponse.status === 'connected' && facebookResponse.authResponse) {
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
Logins: { 'graph.facebook.com': facebookResponse.authResponse.accessToken }
}, {
region: 'eu-west-1'
});
AWS.config.credentials.get((err) => {
if (err) {
return console.log("Error", err);
}
console.log("Cognito credentials", AWS.config.credentials);
console.log("Cognito Identity Id", AWS.config.credentials.identityId);
});
} else if (facebookResponse.status === 'not_authorized') {
document.getElementById('facebookStatus').innerHTML = 'Please log into this app.';
} else {
document.getElementById('facebookStatus').innerHTML = 'Please log into Facebook.';
}
};
I can get back a session token (in addition to a ton of other stuff like accesKeyId, identityId, and secretAccessKey).
But what do I do with that session token? Perhaps I'm confused, but I would think because there is a mapping between Facebook fields and Cognito fields, that somehow that Facebook user would be migrated into the Cognito user pool and I could get a JWT token for that user for my other lambdas. But after checking the AWS dashboard, I can see a log in (I think) in the Identity Pool but there is no corresponding User Pool entry.
Do I somehow have to manually migrate it over (using the AWS JavaScript SDK)? I don't want to use the Amplify library. Am I thinking about it wrong? Do I somehow use the session token as a bearer token with my other lambdas? Do I need to add that person as a user pool user somehow?

How do you get client-side firebase cloud messaging token into google cloud function?

I'm working towards implementing push notifications that appear on change to a firebase firestore document. I'm using the react-native-firebase module. My google cloud function listens for changes to the firestore and then sends messages via firebase-admin.
google's reference says you can specify a single device to message with:
// This registration token comes from the client FCM SDKs.
var registrationToken = 'YOUR_REGISTRATION_TOKEN';
var message = {
data: {
score: '850',
time: '2:45'
},
token: registrationToken
};
// Send a message to the device corresponding to the provided
// registration token.
admin.messaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
client-side in my react-native app I get a token using react-native-firebase:
function getToken() {
let fcmToken = await AsyncStorage.getItem("fcmToken");
if (!fcmToken) {
fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
await AsyncStorage.setItem("fcmToken", fcmToken);
}
}
}
Do I have to store the google cloud messaging token somewhere other than async storage or is there a way to access it as is, inside my google cloud function? It seems like I should be storing the auth token inside firestore and accessing firestore with cloud functions. is this the best way to do this?
You don't need AsyncStorage to access the token, it is available right from fcmToken = await firebase.messaging().getToken(); in your code.
From there you can either send it to a callback Cloud Function with something like:
var sendMessage = firebase.functions().httpsCallable('sendMessage');
addMessage({ token: fcmToken }).then(function(result) {
// ...
});
This is based on the example in the documentation here. You can then use this value in your Cloud Functions code to send a message by calling the FCM API through the Admin SDK.
Or store it in a database, such as Cloud Firestore with something like this:
db.collection("tokens").add(docData).then(function() {
console.log("Token successfully written to database!");
});
Which is based on the example in the documentation here. You can then read this value from the database in your Cloud Function and use it to again send a message by calling the FCM API through the Admin SDK.

Firebase, Auth0, React. The custom token format is incorrect. Please check the documentation

I’m trying to use Auth0 JWT Tokens with Firebase, with no much luck.
When using the token with Firebase:
const token = localStorage.getItem('id_token'); //from auth0
firebase.auth().signInWithCustomToken(token).catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
console.log(error);
console.log(token);
});
All I get is:
“The custom token format is incorrect. Please check the documentation.”
As far as I saw in Firebase’s documentation Auth0 and Firebase tokens are different:
https://firebase.google.com/docs/auth/admin/create-custom-tokens
Apparently, Firebase expects an uid which is not present in the one generated by Auth0 which uid equivalent is in sub.
I tried to create a rule to modify the Auth0’s token to include a copy of sub named uid to see if this could be a solution, but it’s not working, nothing is added to the body of the token.
function (user, context, callback) {
context.idToken.uid = user.user_id;
callback(null, user, context);
}
Any idea / suggestion?
PS:
1.I checked the token in jwt.io and its valid.
2.I tried reducing the expiring time to less than 5min, as I saw some people considering this a possible solution, but its not.
You can't use an Auth0 token directly with Firebase. You need to create a server-side API that uses the firebase-admin SDK to create a Firebase Custom Token using the Auth0 data.
There's a full tutorial over on the OAuth site. Check out the API Routes section on how to use firebaseAdmin.auth().createCustomToken given the OAuth token:
// Auth0 athentication middleware
const jwtCheck = jwt({
secret: jwks.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${config.AUTH0_DOMAIN}/.well-known/jwks.json`
}),
audience: config.AUTH0_API_AUDIENCE,
issuer: `https://${config.AUTH0_DOMAIN}/`,
algorithm: 'RS256'
});
// Initialize Firebase Admin with service account
const serviceAccount = require(config.FIREBASE_KEY);
firebaseAdmin.initializeApp({
credential: firebaseAdmin.credential.cert(serviceAccount),
databaseURL: config.FIREBASE_DB
});
// GET object containing Firebase custom token
app.get('/auth/firebase', jwtCheck, (req, res) => {
// Create UID from authenticated Auth0 user
const uid = req.user.sub;
// Mint token using Firebase Admin SDK
firebaseAdmin.auth().createCustomToken(uid)
.then(customToken =>
// Response must be an object or Firebase errors
res.json({firebaseToken: customToken})
)
.catch(err =>
res.status(500).send({
message: 'Something went wrong acquiring a Firebase token.',
error: err
})
);
});

firebase.auth(...).signInWithEmailAndPassword is not a function in Cloud Function

I m getting Error while invoked function.
I m using LsignInWithEmailAndPassword Method.Any Special Configuration is Require?
const functions = require('firebase-functions');
const firebase=require('firebase-admin');
firebase.initializeApp(functions.config().firebase);
exports.login = functions.https.onRequest((request, response) => {
var data = {
email : 'demo#gmail.com',
password : 'demo123'
};
var auth = null;
firebase
.auth()
.signInWithEmailAndPassword(data.email, data.password)
.then( function(user){
response.send("Authenticated successfully with payload");
// console.log("Authenticated successfully with payload:", user);
auth = user;
})
.catch(function(error){
response.send("Login Failed!");
// console.log("Login Failed!", error);
});
// response.send("Hello from Firebase!");
});
When you call firebase.auth() from the Admin SDK, you're getting an object of type Auth. As you can see from the API docs, it doesn't have a method called signInWithEmailAndPassword.
It seems you're mixing up the javascript client SDK with the Admin SDK. There's no reason to use the client SDK in Cloud Functions. It's supposed to be used in the browser only, since signing in only makes sense on the device that the user is actually using.
signInWithEmailAndPassword is only available in a browser environment. To get access to who made a particular request, you can use firebase.auth().getToken to get a JWT token, and send that along to your cloud function endpoint. Then from there you can call verifyIdToken to get the uid of whoever made the request.

Categories