Send email when user is created on firestore using Cloud Functions - firebase

I'm trying to send the email verification link after the user is created on my flutter app, but the email isn't sent and in my Cloud Functions Log I'm receiving the message when I deploy:
{"#type":"type.googleapis.com/google.cloud.audit.AuditLog","status":{"code":9,"message":"FAILED_PRECONDITION"},"authenticationInfo":{"principalEmail":"*************"},"requestMetadata":{"callerIp":"186.216.140.62","callerSuppliedUserAgent":"FirebaseCLI/6.5.0,gzip(gfe),gzip(gfe)","requestAttributes":{"time":"2019-03-29T23:21:10.130Z","auth":{}},"destinationAttributes":{}},"serviceName":"cloudfunctions.googleapis.com","methodName":"google.cloud.functions.v1.CloudFunctionsService.UpdateFunction","authorizationInfo":[{"permission":"cloudfunctions.functions.update","granted":true,"resourceAttributes":{}},{"resource":"projects/pppppp-9800a/locations/us-central1/functions/sendVerificationEmail","permission":"cloudfunctions.functions.update","granted":true,"resourceAttributes":{}}],"resourceName":"projects/pppppp-9800a/locations/us-central1/functions/sendVerificationEmail","request":{"#type":"type.googleapis.com/google.cloud.functions.v1.UpdateFunctionRequest","function":{"labels":{"deployment-tool":"cli-firebase"},"eventTrigger":{"eventType":"providers/cloud.firestore/eventTypes/document.create","resource":"projects/pppppp-9800a/databases/(default)/documents/users/{userId}","service":"firestore.googleapis.com"},"sourceUploadUrl":"https://storage.googleapis.com/gcf-upload-us-central1-dc1829cf-3a07-4951-be81-1a15f892ed8d/8ea3f162-c860-4846-9064-04a855efca2f.zip?GoogleAccessId=service-73683634264#gcf-admin-robot.iam.gserviceaccount.com&Expires=1553903464&Signature=******************","name":"projects/pppppp-9800a/locations/us-central1/functions/sendVerificationEmail"}}}
My code:
exports.sendVerificationEmail = functions.firestore.document('users/{userId}').onCreate((snap, context) => {
const user = snap.data();
console.log("----------------------");
console.log("user created: " + user.uidColumn);
admin.auth().generateEmailVerificationLink(user.email).then((link) => {
console.log("**********" + link);
sendVerificationEmail(user.emailColumn, link);
return 0;
}).catch(e => {
console.log(e);
})
return 0;
});
function sendVerificationEmail(email, link) {
var smtpConfig = {
host: 'smtp.gmail.com',
port: 465,
secure: true, // use SSL
auth: {
user: 'myappemail#gmail.com',
pass: 'password'
}
};
var transporter = nodemailer.createTransport(smtpConfig);
var mailOptions = {
from: "qeaapp#gmail.com", // sender address
to: email, // list of receivers
subject: "Email verification", // Subject line
text: "Email verification, press here to verify your email: " + link,
html: "<b>Hello there,<br> click here to verify</b>" // html body
};
transporter.sendMail(mailOptions, function (error, response) {
if (error) {
console.log(error);
} else {
console.log("Message sent: " + response.message);
}
return 0;
});
return 0;
}
When I the the command firebase deploy I get the message functions: failed to update function sendVerificationEmail
HTTP Error: 400, Change of function trigger type or event provider is not allowed
I'm new in JS and I don't know what these erros mean

Delete your first function called sendVerificationEmail, then redeploy. It looks like you maybe initially deployed it as something other than a Firestore trigger.

Related

missing domain in continue url

I am trying to Authenticate with Firebase Using an Email Link in React native and expo.
Here is my code
const actionCodeSettings = {
url: 'ndimensions-9c677.web.app',
handleCodeInApp: true,
};
emailAuth: async (email) => {
try {
firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings)
.then(() => {
// The link was successfully sent. Inform the user.
// Save the email locally so you don't need to ask the user for it again
// if they open the link on the same device.
window.localStorage.setItem('emailForSignIn', email);
// ...
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
console.log(errorCode, errorMessage);
// ...
});
} catch (error) {
console.log('Something went wrong with Email Link Authentication: ', error);
}
},
I am getting the errors: auth/invalid-continue-uri Missing domain in continue url
What should be the value of url?
You are missing the protocol for the URL, eg.
url: 'https://ndimensions-9c677.web.app'

Sync user with Firebase functions to Hasura GraphQL

I want to use firebase to authenticate users and then firebase functions to insert users into Hasura but having problems with the firebase functions.
When I try to create a user from the app the "registerUser" function, which can be found below, it ends with an error:
Error detected in registerUser:
{"#type":"type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.Insight",
"errorGroup":"CLic1cmw6emOsAE",
"errorEvent":{"message":"Error: The uid must be a non-empty string with at most 128 characters.
at FirebaseAuthError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseAuthError.PrefixedFirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:88:28)\
at new FirebaseAuthError (/srv/node_modules/firebase-admin/lib/utils/error.js:147:16)
at AuthRequestHandler.AbstractAuthRequestHandler.setCustomUserClaims (/srv/node_modules/firebase-admin/lib/auth/auth-api-request.js:996:35)
at Auth.BaseAuth.setCustomUserClaims (/srv/node_modules/firebase-admin/lib/auth/auth.js:342:40)
at exports.registerUser.functions.https.onCall (/srv/index.js:32:18)
at func (/srv/node_modules/firebase-functions/lib/providers/https.js:272:32)
at corsHandler (/srv/node_modules/firebase-functions/lib/providers/https.js:292:44)\n at cors (/srv/node_modules/cors/lib/index.js:188:7)
at /srv/node_modules/cors/lib/index.js:224:17","eventTime":"2020-06-10T08:25:03.017Z","serviceContext":{"service":"registerUser","resourceType":"cloud_function"}}}
If I instead create a user directly via the firebase console my "processSignUp" runs
but ends with another error:
ReferenceError: fetch is not defined
at GraphQLClient.<anonymous> (/srv/node_modules/graphql-request/dist/src/index.js:108:25)
at step (/srv/node_modules/graphql-request/dist/src/index.js:44:23)
at Object.next (/srv/node_modules/graphql-request/dist/src/index.js:25:53)
at /srv/node_modules/graphql-request/dist/src/index.js:19:71
at new Promise (<anonymous>)
at __awaiter (/srv/node_modules/graphql-request/dist/src/index.js:15:12)
at GraphQLClient.request (/srv/node_modules/graphql-request/dist/src/index.js:98:16)
at exports.processSignUp.functions.auth.user.onCreate (/srv/index.js:60:25)
at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:132:23)
at /worker/worker.js:825:24
I've tried pretty much everything I could think of. I've used https://hasura.io/jwt-config/ to setup the JWT on Heroku. I've triple checked passwords and graphQL endpoint. I have no problems with the mutations or query variables when I play around in hasura console but I'm unable to connect the firebase functions to hasura. Thanks in advance.
functions/index.js
...
const client = new request.GraphQLClient(
"https://app-name.herokuapp.com/v1/graphql",
{
headers: {
"content-type": "application/json",
"x-hasura-admin-secret": "Password",
},
}
);
...
// On register.
exports.registerUser = functions.https.onCall((data) => {
const { email, password } = data;
try {
const userRecord = admin.auth().createUser({ email, password });
const customClaims = {
"https://hasura.io/jwt/claims": {
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": ["user"],
"x-hasura-user-id": userRecord.uid,
},
};
admin.auth().setCustomUserClaims(userRecord.uid, customClaims);
return userRecord.toJSON();
} catch (e) {
let errorCode = "unknown";
let msg = "Something went wrong, please try again later";
if (e.code === "auth/email-already-exists") {
errorCode = "already-exists";
msg = e.message;
}
throw new functions.https.HttpsError(errorCode, msg, JSON.stringify(e));
}
});
...
// On sign up.
exports.processSignUp = functions.auth.user().onCreate(async (user) => {
const { uid: id, email } = user;
const mutation = `
mutation($id: String!, $email: String) {
insert_users(objects: [{
id: $id,
email: $email,
}]) {
affected_rows
}
}
`;
try {
const data = await client.request(mutation, { id, email });
return data;
} catch (e) {
throw new functions.https.HttpsError("invalid-argument", e.message);
}
});
In the package.json for your functions, try changing the node engine to 10 and your grapql-request package to 1.8.2.

OAuth2 fails to return auth token using simple-oauth2 and Firebase Functions for Spotify Authentication

I have been working on a oauth2 flow for spotify by following this similar tutorial by the Firebase team for Instagram HERE
I am able to submit my credentials and return the user code and state in the url, but when I run the method to submit the code to return an auth token, the auth token that I print to console in the Firebase functions returns: Auth Token Error Not Found. Here's my workflow:
Here's the Spotify docs
FIRST, I have a function to configure my spotifyOAuth:
function spotifyOAuth2Client() {
// Spotify OAuth 2 setup
const credentials = {
client: {
id: functions.config().spotify.clientid,
secret: functions.config().spotify.clientsecret,
},
auth: {
tokenHost: 'https://accounts.spotify.com',
authorizePath: '/authorize'
},
};
return require('simple-oauth2').create(credentials);
}
I use that function in this Firebase function that is called using https://us-central1-<my project string>.cloudfunctions.net/redirect:
exports.redirect = functions.https.onRequest((req, res) => {
const oauth2 = spotifyOAuth2Client();
cookieParser()(req, res, () => {
const state = req.cookies.state || crypto.randomBytes(20).toString('hex');
console.log('Setting verification state:', state);
res.cookie('state', state.toString(), {
maxAge: 3600000,
secure: true,
httpOnly: true,
});
const redirectUri = oauth2.authorizationCode.authorizeURL({
redirect_uri: OAUTH_REDIRECT_URI,
//scope: OAUTH_SCOPES,
state: state,
});
console.log('Redirecting to:', redirectUri);
res.redirect(redirectUri);
});
});
The code above returns a url string with the proper parameters, the following code block is where my code breaks, I have another cloud function that runs after being redirected from the res.redirect(redirectUri) above. And when I try to run the getToken() method, it appears to not return anything because I hit the catch block instead? This is where I observe the Auth Token Error Not Found.
const oauth2 = spotifyOAuth2Client();
try {
return cookieParser()(req, res, async () => {
console.log('Received verification state:', req.cookies.state);
console.log('Received state:', req.query.state);
if (!req.cookies.state) {
throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
} else if (req.cookies.state !== req.query.state) {
throw new Error('State validation failed');
}
console.log('Received auth code:', req.query.code);
console.log(OAUTH_REDIRECT_URI);
// Get the access token object (the authorization code is given from the previous step).
const tokenConfig = {
code: req.query.code,
redirect_uri: 'http://localhost:8100/popup'
};
// Save the access token
try {
const result = await oauth2.authorizationCode.getToken(tokenConfig)
const accessToken = oauth2.accessToken.create(result);
console.log('inside try');
console.log(result);
console.log(accessToken);
} catch (error) {
console.log('Access Token Error', error.message);
}
I've double checked my spotify client/secret credentials in the config, what is going wrong with this OAuth2 flow?
Resolved my issue, I was not using the correct endpoints:
const credentials = {
client: {
id: functions.config().spotify.clientid,
secret: functions.config().spotify.clientsecret,
},
auth: {
tokenHost: 'https://accounts.spotify.com',
authorizePath: '/authorize',
tokenPath: '/api/token'
},
};

Using IBM Watson Text-to-Speech with Firebase Cloud Functions?

I'm trying to use set up a Firebase Cloud Function to access IBM Watson Text-to-Speech. The problem is writing the returned audiofile to my Firestore database.
This test to return the list of voices worked, logging the response to the Functions log:
exports.test = functions.firestore.document('IBM_Watson_Token/Test_Value').onUpdate((change, context) => {
var textToSpeech = new TextToSpeechV1({
username: 'groucho',
password: 'swordfish'
});
return textToSpeech.listVoices(null, function(error, voices) {
if (error) {
console.log(error);
} else {
console.log(JSON.stringify(voices, null, 2));
}
});
});
Here is the documentation example Node code for returning an audiofile and writing it to the server:
var TextToSpeechV1 = require('watson-developer-cloud/text-to-speech/v1');
var fs = require('fs');
var textToSpeech = new TextToSpeechV1({
username: '{username}',
password: '{password}'
});
var synthesizeParams = {
text: 'Hello world',
accept: 'audio/wav',
voice: 'en-US_AllisonVoice'
};
// Pipe the synthesized text to a file.
textToSpeech.synthesize(synthesizeParams).on('error', function(error) {
console.log(error);
}).pipe(fs.createWriteStream('hello_world.wav'));
Firebase doesn't allow writing files to the server using fs, you have to write to a Firestore database. I changed the last line of the example code to write to Firestore, using a promise:
exports.test = functions.firestore.document('IBM_Watson_Token/Test_Value').onUpdate((change, context) => {
var textToSpeech = new TextToSpeechV1({
username: 'groucho',
password: 'swordfish'
});
var synthesizeParams = {
text: 'Hello world',
accept: 'audio/wav',
voice: 'en-US_AllisonVoice'
};
return textToSpeech.synthesize(synthesizeParams).on('error', function(error) {
console.log(error);
}).then(function (audiofile) {
admin.firestore().collection('IBM_Watson_Token').doc('hello_world').set({
'audiofile': audiofile
})
})
.catch(function (error) {
console.log(error);
});
});
The error message was
TypeError: textToSpeech.synthesize(...).on(...).then is not a function
How do I save the audiofile that comes back from Watson to Firestore?
That would be because the synthesize method is not returning a promise. You will need to use a callback construct that looks like
textToSpeech.synthesize(params, function (err, body, response) {
if (err) {
...
} else {
// body is the audio
...
}
});

Nativescript with Firebase : Works login in offline mode

I use nativescript-plugin-firebase to connect with Firebase. It does work when create new user / login / CRUD operations when app is online mode.
When the app is offline mode, Login authentication / Create new user is not working rest of the above mentioned things are working.
My code base:
app.component.ts
ngOnInit(): void {
firebase.init({
persist: true,
storageBucket: 'gs://**************/',
onAuthStateChanged: (data: any) => {
console.log(JSON.stringify(data))
if (data.loggedIn) {
BackendService.token = data.user.uid;
}
else {
BackendService.token = "";
}
}
}).then(
function (instance) {
console.log("firebase.init done");
},
function (error) {
console.log("firebase.init error: " + error);
}
);
firebase.keepInSync(
"/users", // which path in your Firebase needs to be kept in sync?
true // set to false to disable this feature again
).then(
function () {
console.log("firebase.keepInSync is ON for /users");
},
function (error) {
console.log("firebase.keepInSync error: " + error);
}
);
in service file
login(user: User) {
return firebase.login({
type: firebase.LoginType.PASSWORD,
email: user.email,
password: user.password
}).then((result: any) => {
BackendService.token = result.uid;
return JSON.stringify(result);
}, (errorMessage: any) => {
// alert(errorMessage);
alert('Email address / Password is wrong!!!');
});
}
Anyone help me how login will work in when offline mode.

Resources