Sending Custom Tokens Back To Client From Firebase Admin - javascript

I am working with the Firebase Admin SDK for Nodejs so that I can mint custom tokens for authentication in iOS devices. In the Nodejs docs it states that you send the token back to the client after it is created.
let uid = 'some-uid';
admin.auth().createCustomToken(uid)
.then(function(customToken) {
// Send token back to client
})
.catch(function(error) {
console.log('Error creating custom token:', error);
});
My question is the most efficient way to do this. I've been thinking about creating Could Function to send it back in a response body but I feel like I may be over thinking it. Is this the recommended method or is there a simpler way I am missing?

Here is the working code from my application (cloud function) that is as simple as copy paste for your reference.
exports.getCustomToken = functions.https.onRequest(async (req, res) => {
return cors(req, res, async () => {
try {
const token = await createCustomToken(req.body.uid);
return res.json(token);
} catch (error) {
res.status(500).json({ message: 'Something went wrong' });
}
});
});
async function createCustomToken(userUid, role = '') {
let createdCustomToken = '';
console.log('Ceating a custom token for user uid', userUid);
await firebaseAdmin.auth().createCustomToken(userUid)
.then(function (customToken) {
// Send token back to client
console.log('customToken is ', customToken)
createdCustomToken = customToken;
})
.catch(function (error) {
console.log('Error creating custom token:', error);
});
return createdCustomToken;
}

I wouldn't worry too much about efficiency at this point. The token is small and is quick to generate. The first thing to do is just make it work. Use Cloud Functions if you prefer, but the Admin SDK will work on any modern nodejs backend.

Related

why old Email returns after updating it in firebase and VUE.JS?

am trying to allow users to change their primary email in my VUE App which uses firebase as authentication,
the code am using works fine and it gives me that the email has been updated, however after the email is updated I can log with the new email for one time only and once I have logged out then like it has never been changed, and the old email is working again.
What is am doing wrong that keeps getting the old email assigned with the user
currently am using the following code :
firebase.auth()
.signInWithEmailAndPassword(oldEmailAddress, currentPass)
.then(
() => {
firebase.auth().currentUser.updateEmail(newEmailAddress).then(() => {
console.log('Email Updated');
}).catch((error) => {
console.log('Email Error updating user:', error);
});
},
(err) => {
console.log('log in user error:', err);
}
);
try using this function from firebase/auth as the docs say:
const auth = getAuth();
updateEmail(auth.currentUser, "user#example.com").then((result) = { console.log(result) })

res.redirect not workig when working with React

I am using react as frontend and made api using express I have the following code I have stored jwt token in the cookies while logging in for the first then when trying to login in again I check if there is already a token in the cookies if there is token in the cookie (currently I am not verifying it I just want it to work) redirect the user to profile page but it doesn't work.
Although an XMLHttpRequest can be seen in the network tab (click for screenshot) but it doesn't work.
PS - I am using Axios in the frontend to make a get request.
loginRouter.get("/", async (req, res) => {
try {
const cookieFound = req.cookies["login-token"];
if (cookieFound) {
res.redirect("profile");
} else {
res.redirect("login");
}
} catch (error) {
console.log(error);
res.status(500).json("Ooops something went wrong!");
}
});
code to make a get request in the frontend
useEffect(() => {
Axios.get("/login");
}, []);
EDIT :-
Backend
loginRouter.get("/", async (req, res) => {
try {
const cookieFound = req.cookies["login-token"];
if (cookieFound) {
res.send("/profile");
}
// res.status(200).json(cookieFound);
} catch (error) {
console.log(error);
res.status(500).json("Ooops something went wrong!");
}
});
Client
useEffect(() => {
const alreadyLoggedIn = async () => {
const url = await Axios.get("/login");
window.location = url.data;
};
alreadyLoggedIn();
}, []);
To what you have entered I think you should change window.location = url.data to window.location = window.location.hostname + url.data;
In your current setup the total url will be set to /profile while you want yourwebsite.com/profile

How to get a variable from front to a service worker?

Some context
I've created a service worker to send notifications to registered users.
It works well until I tried to implement a sort of id to each people who register to a service worker (to send notification).
I do that because I have to delete old registration from my database, so I took the choice to let each users three registration (one for mobile device and two others for different navigator on computer) and if there is more, I want to remove from the database the older.
Tools
I'm using nodejs, express and mySql for the database.
The issue
When I launch a subscription I got this error:
SyntaxError: Unexpected token o in JSON at position 1
at JSON.parse (<anonymous>)
I saw in an other post that it's because they try to JSON.parse what's already an object.
But in my case, I can't find where I parse, see the part which are concerned:
// service.js (service worker file)
// saveSubscription saves the subscription to the backend
const saveSubscription = async (subscription, usrCode) => {
const SERVER_URL = 'https://mywebsite:4000/save-subscription'
subscription = JSON.stringify(subscription);
console.log(subscription); // I got here what I expect
console.log(usrCode); // <-------------------------------- HERE I GOT UNDEFIND
const response = await fetch(SERVER_URL, {
method: 'post',
headers: {
'Content-Type' : 'application/json',
},
body : {
subscription: subscription,
usrCode: usrCode
}
})
return response
}
But when I console.log(usrCode) in my inspector, I got the good value.
So how should I do to get the value in service.js
Maybe the problem is from:
const bodyParser = require('body-parser')
app.use(bodyParser.json())
At the beginning I thought that the issue is from the back (because I'm not really good with async function).
And here is the back, If maybe I got something wrong.
// index.js (backend)
// Insert into database
const saveToDatabase = async (subscription, usrCode) => {
// make to connection to the database.
pool.getConnection(function (err, connection) {
if (err) throw err; // not connected!
console.log(usrCode);
console.log(subscription);
connection.query(`INSERT INTO webpushsub (webpushsub_info, webpushsub_code) VALUES ('${subscription}', '${usrCode}')`, function (err, result, fields) {
// if any error while executing above query, throw error
if (err) throw err;
// if there is no error, you have the result
console.log(result);
connection.release();
});
});
}
// The new /save-subscription endpoint
app.post('/save-subscription', async (req, res) => {
const usrCode = req.body.usrCode; // <------------------ I'm not sure about this part
const subscription = req.body.subscription
await saveToDatabase(JSON.stringify(subscription, usrCode)) //Method to save the subscription to Database
res.json({ message: 'success' })
})
By searching on google, I've found this tutorial. So the reason why usrCode is undefined is because the service worker doesn't have access to a data stored in front.
First you have to pass it in the URL as following:
// swinstaller.js (front)
// SERVICE WORKER INITIALIZATION
const registerServiceWorker = async (usrCode) => {
const swRegistration = await navigator.serviceWorker.register('service.js?config=' + usrCode); //notice the file name
return swRegistration;
}
And then get it in the service worker:
// service.js (service worker file)
// get the usrCode
const usrCode = new URL(location).searchParams.get('config');

Trying to subscribe to topic on Firebase Cloud Messaging gives Error

When i try to subscribe to a topic i get the following error:
.subscribeToTopic is not a function
const messaging = firebase.messaging();
messaging
.requestPermission()
.then(() => {
return messaging.getToken();
})
.then(token => {
messaging
.subscribeToTopic(token, 'allUsers')
.then(response=> {
console.log(JSON.stringify(response));
})
.catch(function(error) {
console.log('Error subscribing to topic:', error);
});
})
.catch(err => {
console.log('Unable to get permission to notify.', err);
});
If I remove that line of .subscribeToTopic and add a POST call via http it works using the following url:
https://iid.googleapis.com/iid/v1/TOKEN/rel/topics/TOPIC_NAME
I took a look to this question and the docs
Cloud Messaging in Cloud Functions: admin.messagin(...).send is not a function
https://firebase.google.com/docs/cloud-messaging/js/topic-messaging
ah i solved it by handling on backend side ( nodeJS ) where the documentation is easy to handle topic.
so in this case we have alr generate token on frontend side then in backend (nodeJS) we tried to subscribe to topic by the token.
so in frontend end when we stream or firebase.messaging().onMessage(payload => { would like to trigger and show the message by topic.
FYI : https://github.com/firebase/firebase-js-sdk/issues/5289#issuecomment-899542765
so from the link we know that
Notification.vue
// these from frontend side ( for example vueJS )
import firebase from 'firebase/app'
import 'firebase/messaging'
// firebase only for get token, onMessaging, request permission check, there is no function to subscribe topic by the token, so we handle on backend side my alternative
then in server.js
// these from backend side ( for examle nodeJS )
const { admin } = require('./firebase-config');
// admin.messaging().sendToTopic()
// admin.messaging().subscribeToTopic()
// admin.messaging().sendToDevice()
if you are looking for the firebase-config.js here is
/*
* Initialize firebase
*/
var admin = require("firebase-admin");
var serviceAccount = require("./firebase.json"); // you can get the .json file on firebase service account .
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://project-xxxxxxx.firebaseio.com"
});
module.exports.admin = admin
my implementation :
app.get('/firebase/notification', (req, res)=>{
const registrationToken = req.body.registrationToken;
admin.messaging().subscribeToTopic(registrationToken, 'myTopic')
.then(response => {
console.log('Successfully subscribed to topic:', response)
const options = notification_options;
const message_notification = {
notification: {
title: 'Yogi Arif Widodo',
body: '2 10 pm',
url: 'https://localhost:8080',
other: 'other data',
}
};
admin.messaging().sendToTopic('myTopic', message_notification, options).then( response => {
so when i tested on firebase console send by topic myTopic my Notification.vue trigger these code
firebase.messaging().onMessage(payload => {
.....console.log
}
You need to use the method send not sendToTopic:
// The topic name can be optionally prefixed with "/topics/".
var topic = 'highScores';
var message = {
data: {
score: '850',
time: '2:45'
},
topic: topic
};
// Send a message to devices subscribed to the provided topic.
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);
});
send() was released and replaced sendtotopic/sendtodevice in version FCM v1
https://firebase.googleblog.com/2018/02/firebase-cloud-messaging-v1-now.html
https://firebase.google.com/docs/cloud-messaging/js/topic-messaging

AWS Cognito: Can't get Credentials

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.

Categories

Resources