Google Firebase Function Firebase auth onCreate event handler - failed to configure trigger - firebase

I am trying to add a Firebase Authentication onCreate user event handler to insert user into Google Cloud SQL database. I got it to work successfully locally with the database public IP, and it successfully adds rows to the SQL database.
I ran firebase deploy -P gircapp2 and this is what I got from the google cloud function logs:
{
"protoPayload": {
"#type": "type.googleapis.com/google.cloud.audit.AuditLog",
"status": {
"code": 13,
"message": "Failed to configure trigger providers/firebase.auth/eventTypes/user.create#firebaseauth.googleapis.com (__gcf__.us-central1.createGircUser)"
},
"authenticationInfo": {
"principalEmail": "gircapptest#gmail.com"
},
"serviceName": "cloudfunctions.googleapis.com",
"methodName": "google.cloud.functions.v1.CloudFunctionsService.UpdateFunction",
"resourceName": "projects/gircapp2/locations/us-central1/functions/createGircUser"
},
"insertId": "46dje7cgk6",
"resource": {
"type": "cloud_function",
"labels": {
"function_name": "createGircUser",
"region": "us-central1",
"project_id": "gircapp2"
}
},
"timestamp": "2021-02-02T19:01:54.537912Z",
"severity": "ERROR",
"logName": "projects/gircapp2/logs/cloudaudit.googleapis.com%2Factivity",
"operation": {
"id": "operations/Z2lyY2FwcDIvdXMtY2VudHJhbDEvY3JlYXRlR2lyY1VzZXIvQU82VHdMQkROTUE",
"producer": "cloudfunctions.googleapis.com",
"last": true
},
"receiveTimestamp": "2021-02-02T19:01:54.579884326Z"
}
Here is my index.js, which works locally and updates successfully google cloud SQL database:
const functions = require("firebase-functions");
const { Sequelize, Model, DataTypes } = require('sequelize')
const {SecretManagerServiceClient} = require('#google-cloud/secret-manager')
var password = ""
const name = 'projects/gircapp2/secrets/postgrespassword/versions/latest';
// Instantiates a client
const client = new SecretManagerServiceClient();
async function accessSecretVersion() {
const [version] = await client.accessSecretVersion({
name: name,
});
// Extract the payload as a string.
const payload = version.payload.data.toString();
password = payload
}
// run this in google functions shell emulator to make a user:
// firebase functions:shell
// firebase > createGircUser({uid: "654321"})
class User extends Model {}
exports.createGircUser = functions.auth.user().onCreate((firebaseuser) => {
(async () => {
const user = 'postgres'
const host = 'gircapp2:us-central1:gircpostgres'
const database = 'postgres'
const port = '5432'
await accessSecretVersion();
const sequelize = await new Sequelize(database, user, password, {
host,
port,
dialect: 'postgres',
logging: false
})
await User.init({
userId: {
type: DataTypes.STRING
},
name: {
type: DataTypes.STRING
},
email: {
type: DataTypes.STRING
},
speciality: {
type: DataTypes.STRING
},
degree: {
type: DataTypes.STRING
},
verified: {
type: DataTypes.BOOLEAN
}
}, {
sequelize,
modelName: "user",
timestamps: true,
})
const userId = firebaseuser.uid
await User.sync({force: false}).then(() => {
// users table created
return User.create({
userId: userId,
verified: false,
});
});
})()
})

It is possible that Firebase Auth is not yet enabled on your project because you are passing uid through a test environment to trigger the event instead of creating a new email on Firebase Console.
Fix it by going to Firebase Console > Firebase Authentication and set up Sign-In method. On your case, you can enable email or a federated identity provider (ex. Google):
Afterwards, redeploy your function then add user on the Firebase Console.
As an addition, when trying to connect to a Cloud SQL instance using Cloud Functions, you're supposed to connect with Unix Sockets by default. With your current code, you will encounter "Not Found" runtime error. You should fix it by changing your host to:
const host = '/cloudsql/gircapp2:us-central1:gircpostgres'

Related

next-auth with googleProvider returns error: TIMED OUT // Try signing in with a different account

Hi I am working with next.js with next-auth googleProvider.
I have finished coding in local environment and now I am testing in production.
The problem I faced is it google API returns an error when try to signIn. The symptom is like below
it prints "Try signing in with a different account." in the browser
it returns error message like below in server
>>>> redirect callback /welcome http://test.abc.com:5000
[next-auth][error][GET_AUTHORIZATION_URL_ERROR]
https://next-auth.js.org/errors#get_authorization_url_error connect ETIMEDOUT 172.217.26.237:443 {
message: 'connect ETIMEDOUT 172.217.26.237:443',
stack: 'Error: connect ETIMEDOUT 172.217.26.237:443\n' +
' at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1187:16)',
name: 'Error'
}
[next-auth][error][SIGNIN_OAUTH_ERROR]
https://next-auth.js.org/errors#signin_oauth_error connect ETIMEDOUT 172.217.26.237:443 {
error: {
message: 'connect ETIMEDOUT 172.217.26.237:443',
stack: 'Error: connect ETIMEDOUT 172.217.26.237:443\n' +
' at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1187:16)',
name: 'Error'
},
provider: {
id: 'google',
name: 'Google',
type: 'oauth',
wellKnown: 'https://accounts.google.com/.well-known/openid-configuration',
authorization: { params: [Object] },
idToken: true,
checks: [ 'pkce', 'state' ],
profile: [Function: profile],
clientId: 'private.info.apps.googleusercontent.com',
clientSecret: 'user_secret',
httpOptions: { timeout: 6000000, agent: false },
signinUrl: 'http://test.abc.com:5000/api/auth/signin/google',
callbackUrl: 'http://test.abc.com:5000/api/auth/callback/google'
},
message: 'connect ETIMEDOUT 172.217.26.237:443'
}
So... at first, I guess it is a firewall issue. However I could receive data from google endpoints.(i.e. curl https://accounts.google.com/.well-known/openid-configuration)
I was also able to fetch curl 172.217.26.237:443, but it returned zero bytes.
Below is my [...nextAuth.js].(Nothing special I think)
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
const AUTH_TIMEOUT = 60000;
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
authorization: {
params: {
prompt: 'consent',
access_type: 'offline',
response_type: 'code',
},
},
// https://github.com/nextauthjs/next-auth/issues/3920
httpOptions: {
timeout: AUTH_TIMEOUT,
},
}),
],
callbacks: {
async signIn({ account, profile }) {
console.debug('>>>> signIn callback', account, profile);
if (account.provider === 'google') {
return profile.email_verified && profile.email.endsWith('myhost.com');
}
return false;
},
async redirect({ url, baseUrl }) {
console.log(process.env.HTTPS_PROXY);
console.debug('>>>> redirect callback', url, baseUrl);
if (url.startsWith('/')) return `${baseUrl}${url}`;
if (new URL(url).origin === baseUrl) return url;
return baseUrl;
},
async session({ session, user, token }) {
console.debug('>>>> session callback', session, user, token);
const mergedSession = { ...session };
if (token && token.id_token) {
mergedSession.user.id_token = token.id_token;
}
return mergedSession;
},
async jwt({
token, user, account,
profile, isNewUser,
}) {
console.debug('>>>> jwt callback', token, user, account, profile, isNewUser);
const mergedTokenObject = { ...token };
if (account && !token.id_token) {
mergedTokenObject.id_token = account.id_token;
}
return mergedTokenObject;
},
},
secret: process.env.APP_SECRET,
});
Here is the question.
Could it be a firewall issue? - I just do not get it since I can fetching some data from those urls with curl.
If not, what kind of things I could try at this moment? thx

Fauna returning 'unauthorized' when using Auth0 as a third party access provider

I am trying to integrate Fauna and Auth0 into my Vue 3 app.
To achieve that I am following this Auth0 guide and this youtube video.
In short, I have configured Auth0 as a Provider inside Fauna. And I am sending the Auth0 generated JWT token as the Fauna secret. Fauna should then decode the JWT and give access to the call.
To test it out my code fetches some dummy "products" data from Fauna and prints it to the console.
But when I make the call it returns as unauthorized.
What am I doing wrong?
Here is the script inside my Vue component that is making the call:
import { defineComponent, inject } from "vue";
import { query as q, Client } from "faunadb";
export default defineComponent({
name: "Api",
setup() {
let apiMessage = null;
let executed = false;
const auth = inject("Auth");
const callApi = async () => {
const accessToken = await auth.getTokenSilently();
console.log(accessToken);
try {
const client = new Client({ secret: accessToken });
const { Paginate, Documents, Collection } = q;
const data = await client.query(
Paginate(Documents(Collection("products")))
);
console.log(data);
apiMessage = data;
executed = true;
} catch (e) {
console.log(e);
apiMessage = `Error: the server responded with '${e.response.status}: ${e.response.statusText}'`;
}
};
return {
callApi,
};
},
});
Here is a copy of the unauthorized response object that is returned:
{
"name": "Unauthorized",
"message": "unauthorized",
"description": "Unauthorized",
"requestResult": {
"method": "POST",
"path": "",
"query": null,
"requestRaw": "{\"paginate\":{\"documents\":{\"collection\":\"products\"}}}",
"requestContent": {
"raw": {
"paginate": {
"raw": {
"documents": {
"raw": {
"collection": "products"
}
}
}
}
}
},
"responseRaw": "{\"errors\":[{\"code\":\"unauthorized\",\"description\":\"Unauthorized\"}]}",
"responseContent": {
"errors": [
{
"code": "unauthorized",
"description": "Unauthorized"
}
]
},
"statusCode": 401,
"responseHeaders": {
"content-length": "65",
"content-type": "application/json;charset=utf-8",
"x-txn-time": "1634006015704445"
},
"startTime": 1634006014934,
"endTime": 1634006015885
}
}
Figured it out.
The client has to be initiated with some other values, most importantly is the domain value.
var client = new faunadb.Client({
secret: 'YOUR_FAUNA_SECRET',
domain: 'db.fauna.com',
// NOTE: Use the correct domain for your database's Region Group.
port: 443,
scheme: 'https',
})

NextAuth with custom Credential Provider Not creating session

I am attempting to implement NextAuth in my NextJs app. I am following the official documentation. But for one reason or the other, it seems like the user session object is not generated on login.
Here is my code from my pages/api/auth/[...nextauth].js file
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
import axios from "axios";
export default (req, res) =>
NextAuth(req, res, {
providers: [
Providers.Credentials({
id: 'app-login',
name: APP
authorize: async (credentials) => {
console.log("credentials_:", credentials);
try {
const data = {
username: credentials.username,
password: credentials.password
}
// API call associated with authentification
// look up the user from the credentials supplied
const user = await login(data);
if (user) {
// Any object returned will be saved in `user` property of the JWT
return Promise.resolve(user);
}
} catch (error) {
if (error.response) {
console.log(error.response);
Promise.reject(new Error('Invalid Username and Password combination'));
}
}
},
}),
],
site: process.env.NEXTAUTH_URL || "http://localhost:3000",
session: {
// Use JSON Web Tokens for session instead of database sessions.
// This option can be used with or without a database for users/accounts.
// Note: `jwt` is automatically set to `true` if no database is specified.
jwt: true,
// Seconds - How long until an idle session expires and is no longer valid.
maxAge: 1 * 3 * 60 * 60, // 3 hrs
// Seconds - Throttle how frequently to write to database to extend a session.
// Use it to limit write operations. Set to 0 to always update the database.
// Note: This option is ignored if using JSON Web Tokens
updateAge: 24 * 60 * 60, // 24 hours
},
callbacks: {
// signIn: async (user, account, profile) => { return Promise.resolve(true) },
// redirect: async (url, baseUrl) => { return Promise.resolve(baseUrl) },
// session: async (session, user) => { return Promise.resolve(session) },
// jwt: async (token, user, account, profile, isNewUser) => { return Promise.resolve(token) }
},
pages: {
signIn: '/auth/credentials-signin',
signOut: '/auth/credentials-signin?logout=true',
error: '/auth/credentials-signin', // Error code passed in query string as ?error=
newUser:'/'
},
debug: process.env.NODE_ENV === "development",
secret: process.env.NEXT_PUBLIC_AUTH_SECRET,
jwt: {
secret: process.env.NEXT_PUBLIC_JWT_SECRET,
}
});
const login = async data => {
var config = {
headers: {
'Content-Type': "application/json; charset=utf-8",
'corsOrigin': '*',
"Access-Control-Allow-Origin": "*"
}
};
const url = remote_user_url;
const result = await axios.post(url, data, config);
console.log('result', result);
return result;
};
What am I not getting it right here? Thanks for the help.
I managed to resolve the issue eventually. Something was wrong due to specifying the 'id' and 'name' options for the custom credential provider
I have removed them and the code is working now.

Connect to multiple Firebase DB

My flutter app needs to connect to multiple firebase databases. the vision is that the user will start the application and will enter Firebase parameters and click "Connect".
I find the following source lines
FirebaseApp app;
Future<FirebaseApp> FBDB() async {
WidgetsFlutterBinding.ensureInitialized();
final FirebaseApp app = await FirebaseApp.configure(
name: "FB.AAA.com", // "package_name"
options: const FirebaseOptions(
googleAppID: "1:79046:android:2fb2895a41bde78da062d",
// "mobilesdk_app_id"
gcmSenderID: "???",
//"project_number":
apiKey: "AIzamVHoX0C_zimD3UhUITF4ml7Be4fsI",
// "api_key": [ { "current_key"
projectID: "fb-b64d", //"project_id"
),
);
}
The Google Services file is (changed the data):
{
"project_info": {
"project_number": "123",
"firebase_url": "https://fb-b64d.firebaseio.com",
"project_id": "fb-b64d",
"storage_bucket": "fb-b64d.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:79046:android:2fb2895a41bde78da062d",
"android_client_info": {
"package_name": "FB.AAA.com"
}
},
"oauth_client": [
{
"client_id": "123-5555.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzamVHoX0C_zimD3UhUITF4ml7Be4fsI"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "793081656-5555.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}
The result of the source code above is null (the object was not created) so my questions are:
Did I take the right parameters from the google Json file to the source code above? what should be in the gcmSenderID?
what is the next step? how should I receive the
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance
In order to start query the DB?
If you want to get a Firebase service for a specific FirebaseApp instance, you pass that app into the service's constructor or call a factory method.
For example:
FirebaseDatabase(app: this.app).reference();
For for auth:
FirebaseAuth.fromApp(app)

Vue pwa with firebase cloud messaging not working properly

im trying the following code:
navigator.serviceWorker.register('service-worker.js')
.then((registration) => {
const messaging = firebase.messaging().useServiceworker(registration)
console.log(messaging)
messaging.requestPermission().then(function () {
console.log('Notification permission granted.')
messaging.getToken().then(function (currentToken) {
if (currentToken) {
console.log(currentToken)
}
})
})
})
my manifest:
{
"name": "Herot-Eyes",
"short_name": "herot-eyes",
"gcm_sender_id": "103953800507",
"icons": [
{
"src": "/static/img/icons/herot-eyes-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/static/img/icons/herot-eyes-512x512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/static/img/icons/apple-touch-icon-180x180.png",
"sizes": "180x180",
"type": "image/png"
}
],
"start_url": "/",
"display": "fullscreen",
"orientation": "portrait",
"background_color": "#000000",
"theme_color": "#2196f3"
}
what is going wrong? my console.log(messaging) is returning a factory error, the following:
bad-push-set : "The FCM push set used for storage / lookup was not not
a valid push set string." bad-scope
"The service worker scope must be a string with at least one
character." bad-sender-id
"Please ensure that 'messagingSenderId' is set correctly in the
options passed into firebase.initializeApp()." bad-subscription
"The subscription must be a valid PushSubscription." bad-token : "The
FCM Token used for storage / lookup was not a valid token string."
bad-vapid-key
"The public VAPID key is not a Uint8Array with 65 bytes."
bg-handler-function-expected
"The input to setBackgroundMessageHandler() must be a function."
delete-scope-not-found
"The deletion attempt for service worker scope could not be performed
as the scope was not found." delete-token-not-found
"The deletion attempt for token could not be performed as the token
was not found." failed-delete-vapid-key
"The VAPID key could not be deleted."
failed-serviceworker-registration
"We are unable to register the default service worker.
{$browserErrorMessage}" failed-to-delete-token
"Unable to delete the currently saved token." get-subscription-failed
"There was an error when trying to get any existing Push
Subscriptions." incorrect-gcm-sender-id
"Please change your web app manifest's 'gcm_sender_id' value to
'103953800507' to use Firebase messaging." invalid-delete-token
"You must pass a valid token into deleteToken(), i.e. the token from
getToken()." invalid-public-vapid-key
"The public VAPID key must be a string." invalid-saved-token
"Unable to access details of the saved token."
no-fcm-token-for-resubscribe
"Could not find an FCM token and as a result, unable to resubscribe.
Will have to resubscribe the user on next visit." no-sw-in-reg
"Even though the service worker registration was successful, there was
a problem accessing the service worker itself."
no-window-client-to-msg
"An attempt was made to message a non-existant window client."
notifications-blocked
"Notifications have been blocked." only-available-in-sw
"This method is available in a service worker context."
only-available-in-window
"This method is available in a Window context." permission-blocked
"The required permissions were not granted and blocked instead."
permission-default
"The required permissions were not granted and dismissed instead."
public-vapid-key-decryption-failed
"The public VAPID key did not equal 65 bytes when decrypted."
should-be-overriden
"This method should be overriden by extended classes."
sw-reg-redundant
"The service worker being used for push was made redundant."
sw-registration-expected
"A service worker registration was the expected input."
token-subscribe-failed
"A problem occured while subscribing the user to FCM: {$message}"
token-subscribe-no-push-set
"FCM returned an invalid response when getting an FCM token."
token-subscribe-no-token
"FCM returned no token when subscribing the user to push."
token-unsubscribe-failed
"A problem occured while unsubscribing the user from FCM: {$message}"
token-update-failed
"A problem occured while updating the user from FCM: {$message}"
token-update-no-token
"FCM returned no token when updating the user to push."
unable-to-resubscribe
"There was an error while re-subscribing the FCM token for push
messaging. Will have to resubscribe the user on next visit.
{$message}" unsupported-browser
"This browser doesn't support the API's required to use the firebase
SDK." use-sw-before-get-token
"You must call useServiceWorker() before calling getToken() to ensure
your service worker is used."
Configure to server to receive notifications
Inside public folder, add the following line to manifest.json:
{
//...manifest properties
"gcm_sender_id": "103953800507" <--- add this line to the file
}
Note: if the project wasn't created using Vue Cli, manually create the manifest.json file. (Thanks #natghi)
firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-messaging.js');
var config = {
messagingSenderId: <Sender ID>
};
firebase.initializeApp(config);
let messaging = firebase.messaging();
In your main.js file add the following code
var config = {
apiKey: <API_KEY>,
authDomain: <DOMAIN>,
databaseURL: <DATABASE_URL>,
projectId: <PROJECT_ID>,
storageBucket: <STORAGE_BUCKET>,
messagingSenderId: <SENDER_ID>
};
firebase.initializeApp(config);
Vue.prototype.$messaging = firebase.messaging()
navigator.serviceWorker.register('/firebase-messaging-sw.js')
.then((registration) => {
Vue.prototype.$messaging.useServiceWorker(registration)
}).catch(err => {
console.log(err)
})
Receive notifications
Then in your App.vue, add this code to the created() function
created() {
var config = {
apiKey: <API_KEY>,
authDomain: <DOMAIN>,
databaseURL: <DATABASE_URL>,
projectId: <PROJECT_ID>,
storageBucket: <STORAGE_BUCKET>,
messagingSenderId: <SENDER_ID>
};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging
.requestPermission()
.then(() => firebase.messaging().getToken())
.then((token) => {
console.log(token) // Receiver Token to use in the notification
})
.catch(function(err) {
console.log("Unable to get permission to notify.", err);
});
messaging.onMessage(function(payload) {
console.log("Message received. ", payload);
// ...
});
}
Send notification
UPDATE
const admin = require("firebase-admin")
var serviceAccount = require("./certificate.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
const Messaging = admin.messaging()
var payload = {
webpush: {
notification: {
title: "Notification title",
body: "Notification info",
icon: 'http://i.imgur.com/image.png',
click_action: "http://yoursite.com/redirectPage"
},
},
topic: "Doente_" + patient.Username
};
return Messaging.send(payload)
Older version
Then, in postman you do the following request
POST /v1/projects/interact-f1032/messages:send HTTP/1.1
Host: fcm.googleapis.com
Authorization: Bearer <SENDER_TOKEN>
Content-Type: application/json
{
"message":{
"token" : The token that was in the console log,
"notification" : {
"body" : "This is an FCM notification message!",
"title" : "FCM Message"
}
}
}
Sender Token
In your backend, use the following code, where the file "certificate.json" was got in the firebase dashboard (https://firebase.google.com/docs/cloud-messaging/js/client - Generate pair of keys)
const {google} = require('googleapis');
function getAccessToken() {
return new Promise(function(resolve, reject) {
var key = require('./certificate.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
["https://www.googleapis.com/auth/firebase",
"https://www.googleapis.com/auth/cloud-platform"],
null
);
jwtClient.authorize(function(err, tokens) {
if (err) {
reject(err);
return;
}
resolve(tokens.access_token);
});
});
}
getAccessToken().then(senderToken => console.log(senderToken))
The senderToken is used on the Authorization header to send a notification

Resources