Asynchronous new Vue() to wait for initial authentication - firebase

I am quite new to Vue and currently setting up an app that uses firebase authentication and firestore for basic RBAC.
To prevent unauthorized users from entering routes my router has a custom AuthGuard inspecting the local users roles (The backend implements similar rules)
export default new Router({
routes: [
{
path: '/users',
name: 'Users',
component: Users,
beforeEnter: AuthGuard(user => user.uid && user.roles.admin)
}
..
]})
One issue I have with this setup is that sending people links to protected pages does not work. The auth guard prevents them from loading the page because the local user is not set initialized when the Vue app initializes.
So I came up with a way to delay the creation of my Vue App by structuring my main.js something along:
import Vue from 'vue'
...
init()
.then(() => new Vue(..)
During the init function I am waiting for firebase.auth().onAuthStateChanged to fire and tell me if the user has a valid token or not. It does work but it does not feel right.
Is it ok to asynchronously create a new Vue App? Are there better methods like lazy loading the authentication guard on the route or something?

I do the same with Vue and Azure Active Directory authentication, seems to be the best way of doing it.
Please see Miguels answer: https://stackoverflow.com/a/45899653/2703700
Is that the answer you were looking for?

I'm doing the same thing as well. Here's a snippet in case someone is interested in a more concrete example:
import Vue from 'vue'
import firebase from 'firebase/app'
import 'firebase/auth'
/**
* Initialize Firebase
*/
firebase.initializeApp(FIREBASE_CONFIG)
/**
* Initialize Vue.js
*/
let app;
firebase.auth().onAuthStateChanged(user => {
if (!app) {
app = new Vue({
render: h => h(App)
}).$mount('#dashboard')
}
})

Related

Detecting new user signup in [...nextauth].js

I'm building an app with NextAuth's email provider as the sole means of user registration and login.
In local development everything worked fine, but when deploying the app to Vercel I keep getting Unhandled Promise Rejection errors from Mongo, so I decided to switch to NextAuth's unstable_getServerSession and everything is running very smoothly now.
Before making the switch I used an extra parameter to communicate whether the user was just logging in or newly signing up. Based on this, I sent different emails (a welcome email with login link vs. a shorter message with just the login link).
Here is how the [...nextauth].js file looked before:
export default async function auth(req, res) {
const signup = req.body.signup || false
return await NextAuth(req, res, {
adapter: MongoDBAdapter(clientPromise),
...
providers: [
EmailProvider({
...
sendVerificationRequest(server) {
customLogin(
server.identifier,
server.url,
server.provider, // passing the above data for email content
signup // custom parameter to decide which email to send
)
}
})
],
...
})
}
I adjusted the code sample from the documentation, but wasn't able to pass a custom parameter to it.
Here is my new code:
export const authOptions = {
adapter: MongoDBAdapter(promise),
...
providers: [
EmailProvider({
...
sendVerificationRequest(server) {
customLogin(
server.identifier,
server.url,
server.provider,
)
}
})
],
...
}
export default NextAuth(authOptions)
The customLogin function doesn't do anything except construction the different email options.
Among the things I have tried are wrapping authOptions in a handler function with req and res, setting up a separate API route, and passing parameters to the NextAuth function, but none of those worked.
Any advise on how to implement a custom parameter here would be highly appreciated.

Changing the state management system of existing quasar application from vuex to pinia

Tried this link and created my first store in Quasar using Pinia, I also needed to change the .quasar/app.js manually to add the Pinia store and to make Pinia functional.
import { Quasar } from 'quasar'
import { markRaw } from 'vue'
import RootComponent from 'app/src/App.vue'
import createStore from 'app/src/stores/index'
import createRouter from 'app/src/router/index'
export default async function (createAppFn, quasarUserOptions) {
// Create the app instance.
// Here we inject into it the Quasar UI, the router & possibly the store.
const app = createAppFn(RootComponent)
app.config.devtools = true
app.use(Quasar, quasarUserOptions)
const store = typeof createStore === 'function'
? await createStore({})
: createStore
app.use(store)
const router = markRaw(
typeof createRouter === 'function'
? await createRouter({store})
: createRouter
)
// make router instance available in store
store.use(({ store }) => { store.router = router })
// Expose the app, the router and the store.
// Note that we are not mounting the app here, since bootstrapping will be
// different depending on whether we are in a browser or on the server.
return {
app,
store,
router
}
}
But the problem is .quasar/app.js is re-written with default contents as soon as quasar dev is executed and again I don't have access to the Pinia stores anymore.
As I said this application was based on vuex formerly.
Make sure you have the index file for pinia.
In "src/stores/index.js"
import { store } from 'quasar/wrappers'
import { createPinia } from 'pinia'
/*
* If not building with SSR mode, you can
* directly export the Store instantiation;
*
* The function below can be async too; either use
* async/await or return a Promise which resolves
* with the Store instance.
*/
export default store((/* { ssrContext } */) => {
const pinia = createPinia()
// You can add Pinia plugins here
// pinia.use(SomePiniaPlugin)
return pinia
})
Try checking quasar info
quasar info
Notice #quasar/app-webpack and vuex.
If you are using #quasar/app, try to move to #quasar/app-webpack by upgrading quasar.
quasar upgrade -i
If you have vuex installed in your quasar info output, try to remove it.
npm uninstall vuex
In your package-lock.json, look for "node_modules/vuex" and delete the key and value.
Then delete your "node_modules" folder and run npm i
After that, run quasar clean.
You may try creating a Pinia store via quasar command to validate it.
quasar new store <store_name>
It should generate a pinia store instead of vuex store.
Problem is older version of #quasar/app-webpack package. It got support for Pinia since v3.4.0. Check release notes here. So basically upgrade this package.
Run quasar upgrade -i and then quasar new store <store_name> [--format ts]
It will create a stores/ directory with pinia.
In my case i didn't need to edit any special files, simply replace the index.js in the stores folder. To get quasar CLI to then use pinia when running quasar new store I had to use quasar clean and just like that I had fully transitioned.
My solution was to remove and reinstall node_modules

Handling namespaced modular approach on PINIA, Vue3+Typescript

normally I was using namespaced vuex. But I am deciding to quit vuex because Pinia has the vue core team support. I think it's better for the future developements. Now I am creating store with a modular approach but couldn't really understand how to handle that part on typescript project.
let's say I have a user interface.
interface User {
email: string,
username: string,
}
export default User;
and in store/modules/state.ts I am calling the Type and creating a user state.
import User from "../../types/User"
export const state = () => {
return {
user: {} as User | null,
};
}
and in store/modules/index.ts I should import the state. And make the namespace: true then export it for the defineStore() for pinia store.
import {state} from "./state"
export default {
namespace: true,
state,
}
in store/index.ts
import {defineStore} from "pinia"
import {data} from "./modules"
export const Store = defineStore(data)
okay above, namespace part I use the vuex way. But what is the right approach for the pinia. Additionally, getters and actions as well. How should export and use them.
According to official Pinia docs:
Vuex has the concept of a single store with multiple modules. These modules can optionally be namespaced and even nested within each other. The easiest way to transition that concept to be used with Pinia is that each module you used previously is now a store.
So now you should think about each vuex module as an separated pinia store. Looking at your example it could look like this. create file in store/modules/index.ts and paste:
import { defineStore } from "pinia";
import state from "store/modules/state.ts"; // Assuming that it's path to user state
export const useUserStore = defineStore('some/vuex/module/name', {
state: state,
getters: {
// your getters here, check the Offical Pinia above
},
actions: {
// your actions and mutations here, also check the offical Pinia Docs
}
})
If you want to split getters, actions and state into multiple files, there is discussion on offical repo issue where I provided example, that is working for me. Here is a link

react-native-firebase vs react-redux-firebase?

Background: I have started using react-native-firebase with react-native to integrate with Cloud Firestore. I'm going to start bringing redux into my test application.
Question - Is react-native-firebase ok to continue with as my choice of libraries here? (versus migrating to react-redux-firebase)
Is there an easy way to sum up the difference between the two libraries re when you would you one versus the other? I note with react-native-firebase the installation is quite involved for IOS and Android, so I'm not sure whether react-redux-firebase makes this easier, but if it does do you lose anything in mix?
Main difference:
react-redux-firebase - for using Firebase with Redux
react-native-firebase - for using Firebase JS API with react-native
react-redux-firebase actually supports using react-native-firebase. react-native-firebase provides the Firebase JS API while using native-modules under the hood, meaning you can provide that as your Firebase instance to react-redux-firebase like so:
import { compose, createStore } from 'redux';
import RNFirebase from 'react-native-firebase';
import { getFirebase, reactReduxFirebase } from 'react-redux-firebase';
import thunk from 'redux-thunk';
import makeRootReducer from './reducers';
const reactNativeFirebaseConfig = {
debug: true
};
const reduxFirebaseConfig = {
userProfile: 'users', // save users profiles to 'users' collection
};
export default (initialState = { firebase: {} }) => {
// initialize firebase
const firebase = RNFirebase.initializeApp(reactNativeFirebaseConfig);
const store = createStore(
makeRootReducer(),
initialState,
compose(
reactReduxFirebase(firebase, reduxFirebaseConfig), // pass initialized react-native-firebase app instance
// applyMiddleware can be placed here
)
);
return store;
};
This setup and more is covered in the react-native recipes section of the docs.
Disclaimer: I am one of the authors of react-redux-firebase
react-redux-firebase is helper library for firebase.
I recommended using both react-native-firebase and react-redux-firebase.
react-native-firebase is easy to write, easy to read, easy to understand.
You don't need react-redux-firebase for the small application.
react-native-firebase is awesome.
If you are familiar with firebase, You can use react-native-firebase in 10 minutes.
For example
import Firebase from "react-native-firebase"
....
<Button title="button" onPress={Firebase.analytics().logEvent("pressed")} />

How to get access to native Firebase object when using angularfire2?

I'm using AngularFire2 (2.0.0-beta.2) incombination with angular2 (2.0.0-rc.4). I'd like to get access to the native firebase object (not the AngularFire root object) from Angularfire2.
Within my component, I want to make calls like:
firebase.auth().currentUser.updateEmail("user#example.com")
where firebase is the native firebase object, like that you get from the fragment below:
<script src="https://www.gstatic.com/firebasejs/3.1.0/firebase.js"></script>
<script>
// Initialize Firebase
// TODO: Replace with your project's customized code snippet
var config = {
apiKey: "apiKey",
authDomain: "projectId.firebaseapp.com",
databaseURL: "https://databaseName.firebaseio.com",
storageBucket: "bucket.appspot.com",
};
firebase.initializeApp(config);
</script>
But I don't understand how to setup my angular2 component so that the firebase object is visible within it. Probably a very simple problem to solve, but I don't know how to solve -- I'm not an angular2 expert. I was hoping there would be and AngularFire api to get the object, but there is not.
Also, the reason that I'm trying to do this is that I don't think the angularfire2 api's are complete yet (thats understandable as its still in beta) and I'm trying to work around this. For example I want to update the users email address or password, or send them the forgotten password email. None of this functionality seems to exist yet in AngularFire2, so I'm trying to implement using the native Firebase object.
If you're reading this in September 2016 onwards, this approach might sound good to you.
See the code for superior understanding:
import { Component, Inject } from '#angular/core';
import { AngularFire, FirebaseApp } from 'angularfire2';
#Component({
templateUrl: 'app/auth/resetpassword.component.html'
})
export class ResetpassComponent {
public auth: any;
constructor(private af: AngularFire, #Inject(FirebaseApp) firebaseApp: any) {
this.auth = firebaseApp.auth()
console.log(this.auth);
}
// formData.value.email = 'your#email.com';
onSubmit(formData) {
if(formData.valid) {
console.log('Sending email verification');
this.auth.sendPasswordResetEmail(formData.value.email)
.then( (response) => {
console.log('Sent successfully');
})
.catch( (error) => {
console.log(error);
})
}
}
}
In English:
Import Inject and FirebaseApp
Make available in your component
Start using all the native javascript SDK functions and methods, such as sendPasswordResetEmail()
Doing a auth. doesn't autocomplete with the available methods and functions, so you might need the docs close to you, such as this: https://firebase.google.com/docs/auth/web/manage-users
Any thanks? Give to #cartant : https://stackoverflow.com/a/39069813/1757321
I'm going to try and answer my own question. Let me first say that I'm sure my answer is not the most elegant solution, I'm not yet a javascript/typescript/angular expert. But my answer does work -- even if I don't completely understand.
I used angular-cli to setup my project which is based on angular2 and the latest firebase. Apparently when you use this to setup your project there is a global object created with the name "firebase" in existence. One way to make it visible within your angular 2 component is to put this declaration at the global level in you component (right after the import statements and before your class declaration).
declare var firebase : any;
After you do this the global firebase object is available for use in your component.
RanchoSoftware's solution did not work for me, i used
import {AngularFireModule} from "angularfire2";
import *as firebase from 'firebase';
within the imports in the app.module.ts file
found here:
https://github.com/angular/angularfire2/issues/445

Resources