How to use SSR Firebase Authentication in Nuxt-js with Middleware? - firebase

In my project , user can login in system . If users do not choose "Remember me " option in beginning, their session will end after page closed entirely.
My firebase works Client Side
in firebase.js
export default ({ store, $storage }) => {
onAuthStateChanged(auth, async user => {
if (user && !user.isAnonymous) {
store.commit('firebase-auth/setUser', user);
if (user.emailVerified) {
$storage.setCookie('firebase_token', user.accessToken);
}
} else {
enableGoogleOneTapSignIn(user);
}
});
};
in MiddleWare auth.js
export default ({ $storage, req, redirect }) => {
if (process.server) {
const token = !!req.headers.cookie.firebase_token;
if (!token) return redirect('/');
} else if (process.client) {
const token = !!$storage.getCookie('firebase_token');
if (!token) return redirect('/');
}
};
This code controls if user signed in or not . This works in server and client side.
The problem is ; When user chose "remember me" option at the beginning , session will not finish even if user closes entire browser. And if user opens the browser again , his session will continue and he will be logged-in already but in this scenario I can not reach cookie because it was deleted when browser was closed .
If user comes with a direct link like abc.com/user/my-profile when browser was closed, user will not reach his profile beacuse cookie was cleaned . I need to find a good way for this problem .

Related

Unable to catch the 'auth' event in Hub.listen while calling Auth.federatedSignIn

I am using SolidJS and building a SPA (no server rendering). For authentication, I use the #aws-amplify/core and #aws-amplify/auth packages. At the application root I call the Hub.listen function:
Hub.listen('auth', ({ payload }) => console.log(payload));
In the SignUp component I call Auth.federatedSignIn:
const SignUp = () => {
return (
<button onClick={() => {
Auth.federatedSignIn({ provider: CognitoHostedUIIdentityProvider.Google });
}}>
Sign up
</button>
);
}
I have configured the Amplify as such:
Amplify.configure({
Auth: {
region: import.meta.env.VITE_AWS_REGION,
userPoolId: import.meta.env.VITE_AWS_POOL_ID,
userPoolWebClientId: import.meta.env.VITE_AWS_POOL_CLIENT_ID,
oauth: {
domain: import.meta.env.VITE_AUTH_URL,
responseType: 'code',
redirectSignIn: location.origin + '/account/external',
redirectSignOut: location.origin + '/my',
},
},
});
When I click on the button I am redirected to the import.meta.env.VITE_AUTH_URL (simply outside of my app), choose an account, and then return back to the /account/external page. At that time I expect a consoled payload object in Web tools, but there is nothing. I get it when I call Auth.signOut(), so I assume that I configured Amplify correctly and Hub is subscribed to the auth channel.
My thoughts were that Hub cannot catch any events because after returning the application basically renders again in a new context and Hub simply isn't able to catch anything (events aren't sent from AWS?). I tried to declare the urlOpener function under the oauth property in the config and Google's sign page opened in a new tab, but even then I couldn't get any events in the preserved old page (from which I called Auth.federatedSignIn).
Questions:
How should I organize the code to get the signIn and signUp events?
Can I pass some data into the Auth.federatedSignIn to get it back in the Hub.listen, so I will be able to join the CognitoUser with the data that existed at the time of starting Sign in/Sign up (I want to add a new login type to existed user)?
Here is an example regarding the first question. Just check that your listener is set before you call the Auth.federatedSignIn() method.
export default class SignInService {
constructor(private landingFacade: LandingFacade) {
this.setupAuthListeners(); // Should be called at the top level.
}
private setupAuthListeners() {
Hub.listen('auth', ({ payload: { event, data } }) => {
switch (event) {
case 'signIn':
this.landingFacade.signInSuccess();
break;
case 'signIn_failure':
console.log('Sign in failure', data);
break;
case 'configured':
console.log('the Auth module is configured', data);
}
});
}
public async signIn(): Promise<void> {
await Auth.federatedSignIn();
}
}
For the second one: I'll use a local state and set/query the object you need.

How to differentiate NextAuth user signin and signup?

I am building a blog platform using Nextjs 12, NextAuth(Google), Prisma(MySQL). When a user first signs up to my platform, NextAuth automatically saves user's google email address and google name to my database. I want to make user change their nickname when first signing up.
How would I know if the user is signing up or signing in? Currently in NextAuth, you click signup() button and you are good for both signup and signin..
I would extend the base user model with a property such as customName, and then check on the front- or back-end whether the customName property is undefined/empty, and redirect or show a modal accordinglty.
You can perform the front-end check via useSession
const { data: session, status } = useSession()
if(status === "authenticated" && session.user.customName === ""){ // or whatever your default value for non-defined fields is
// Show your modal or redirect to the page where the user can change his username
// after user enters his new name, make an API call and update it in your DB
}
or on the back-end in pages/api/auth/[...nextauth].js:
...
callbacks: {
async signIn({ user, account, profile, email, credentials }) {
if (user.customName) {
return true
} else {
// User has no custom name yet, redirect him
return '/pathWhereUserCanSetHisName'
}
}
}
...
If you want to ensure every user has to set a name before continuing, I would put the logic above into an API middleware or next's edge middleware like this:
For example, if you're using NextAuth JWT sessions via cookies, add a custom cookie name for the sessionToken and parse it within the next.js middleware:
middleware.ts (or .js)
export default async function middleware(request: NextRequest) {
const response = NextResponse.next();
const userCookie = request.cookies.get(YOUR_CUSTOM_COOKIE_NAME) // the cookie name you set for NextAuth's sessionToken
if(!userCookie){
// user not logged in
return NextResponse.redirect('/login')
}
const user = yourCustomUserParsingFunction(userCookie) // parsing the JWT contained in the cookie
if(user.customName){
return response; // user has a custom name, don't intervene
}
// user has no customName, intervene
return NextResponse.redirect('/pathWhereUserCanSetHisName');
}

User redirect and authentication with middleware of Nuxt

I'm trying to redirect a user to a login page if the user is not logged in when he tries to access certain pages with the following code.
// middlware/authenticated.js
import firebase from 'firebase'
export default function ({ store, redirect }) {
let user = firebase.auth().currentUser
store.state.user = user //this doesn't work
if (!user) {
console.log('redirect')
return redirect('/login')
}
}
However, the problem is with this code when I refresh a page I'm redirected to login page although without using the middleware, I can stay in the same page with logged in. For some reasons, which I don't know why, firebase can't work in middleware.
How should I modify this middleware or implement this function?
Thanks.
//middleware/authenticated.js
export default function ({
store,
redirect
}) {
if (!store.getters['index/isAuthenticated']) {
return redirect('/login')
}
}
//post.vue
async mounted () {
if (process.browser) {
let user;
if (!this.user) user = await auth(); // this auth is a plugin
await Promise.all([
this.user ? Promise.resolve() : this.$store.dispatch("setUser", { user: user || null })
]);
this.isLoaded = true;
}
},
//plugins/auth.js
import firebase from '~/plugins/firebase'
function auth () {
return new Promise((resolve, reject) => {
firebase.auth().onAuthStateChanged((user) => {
resolve(user || false)
})
})
}
export default auth
By default Firebase persists the users logged in status on successful authentication. This example uses the session, to store the user uid and cookies to store the users token and used in situations where the sessions has ended (example when browser is closed) and then a new session started but where the user is still authenticated according to Firebase. In cases like these the user will not need to sign in to view protected resources.
Your basic Middleware to protect it should look like this (if you have a Store Module called User)
export default function ({ store, redirect }) {
if (!store.getters['modules/user/isAuthenticated']) {
return redirect('/auth/signin')
}
}
In your main Store you use the ServerInit Function to get the User if there is one saved in the Cookies and load it into your User Store Module which will be used for verification in the Middleware.
Your User Store Module should look like this, and keep in mind that you remove the Cookie when you Log the User out so that he is fully logged out.
I used the things i mentioned above as the beginning of my Authentication and modified it a bit, which you can also do. Most of the credit goes to davidroyer who has set up this nice Github Repo which includes all needed files as a good example on how to accomplish your goal.

Logout user when app redirect to another app Meteor JS

I have 2 apps, one for auth while the other for products. After login or verification of email, the user is redirected to the products app. On redirection, the recently logged in user is instantly logged out, so a reference to the logged in user becomes null, and I'll need the logged in credentials in the auth app to authenticate on the second. How do I maintain the logged in state in the auth app when it has redirected to the products app?
This is the login function on the auth app
var callLogin = function (email, password, router) {
Meteor.loginWithPassword(email, password, ( error )=> {
if (error) {
sAlert.error( error );
} else {
sAlert.success("Logged in successfully");
window.location.replace( "http://localhost:3300/" + Meteor.userId() );
}
});
}
This is the onCreated function on the products app
Tracker.autorun(function () {
let router = FlowRouter.getParam("_id");
let AuthConnection = DDP.connect( AuthURL );
if ( AuthConnection ) {
console.log( router );
AuthConnection.call('logins.user', router, ( error, response )=> {
if ( error ) {
console.log( error );
} console.log( response );
} );
}
});
The logged in user is always present until the redirection that it becomes null. What do I do to maintain the logged in state of the user in the auth app?
I assume both apps connect to the same database?
When you redirect your local state changes - specifically in this case your local storage state which tracks user resume tokens.
If you want to allow one app to authenticate for another you need some form of SSO - a trivial implementation would be after login to request a resumeToken from the server, pass that to your second app in the URL, then use Meteor.loginWithToken. A more hacky (but maybe simpler?) way might be to copy the token saved in localStorage from app1, pass it in the URL to app2 then use save it there too.

Not able to maintain user authentication session firebase

I am using email & password authentication to logging in user, from my firebase dashboard i have set session expiration time to 2 months . However when i am closing my app from background and then after reopening of app i am getting var user = ref.getAuth(); as null
Does firebase does't take care of this? How to keep user logged in for a long period of time?
Below is the piece of code i am using to login user. I am using react-native
ref.authWithPassword({
email : 'username',
password : 'password'
}, function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
navigatorReference.push({name:'myFeed'})
console.log("Authenticated successfully with payload:", authData);
}
});
Firebase should take care of this. Double check your configuration in the Login & Auth tab in your App Dashboard to make sure you have that setup properly.
You could also try passing along the configuration like so...
ref.authWithPassword({
email : 'username',
password : 'password'
}, function(error, authData) { /* Your Code */ }, {
remember: "default"
});

Resources