I have just implemented email/password auth. Now users need to register or sign in before they can see the rest of my site.
However I'm not clear about what to do when the client refreshes the page after they've already signed in. I want their "logged in" state to still be there, and to not require them to re-enter credentials.
It seems like an easy way to do this would be to store email/password in a client side cookie or localstorage. But I feel a little apprehensive about this. Is there a better way, or is this acceptable?
This app has no server other than firebase.com. I'm deploying the front end to github pages.
Why don't you use firebase.auth() function and only show the login page when they are not logged in?
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
Related
I have a react app that has login built into it, but have begun to break out the login/signup pages for architectural reasons.
The login is performed in a separate react app in a subdomain (https://login.mysite.com). After successful login the user gets redirected to the app (https://app.mysite.com). On logout the user gets redirected back to https://login.mysite.com again.
The redirect works as expected, but I cannot fetch the logged in user in my app (https://app.mysite.com). This causes an eternal redirect loop since the user is logged in at the login page, but not recognized as logged in at the app page.
Code from https://app.mysite.com
// app.mysite.com code
console.log(firebase.auth().currentUser) // This logs 'null'
firebase.auth().onAuthStateChanged((user) => {
console.log('user', user) // This logs 'null' - causing an eternal redirect loop
if(user){
// Render app here
}else{
const loginURI = 'https://login.mysite.com'
window.location.replace(loginURI)
}
})
Code from https://login.mysite.com
// login.mysite.com code
firebase.auth().onAuthStateChanged((user) => {
if (user) {
const appURI = 'https://app.mysite.com'
window.location.replace(appURI)
}
})
I guess the problem is that the logged in user cannot get fetched by the app?
The user is clearly logged in at https://login.mysite.com, but not at https://app.mysite.com.
How can I pass the logged in user from the login page to the app?
Kind regards /K
You can not use multiple domain for same authentication.
Each domain/subdomain have their own cookies and localstorage and those can't be shared with another domain.
For your case login subdomain can't able to share credentials (auth token) with your app domain.
I highly suggest you to move from subdomain to path
Like app.domain.com/login
If you still want this with subdomain look for this medium blog
To provide dynamic content delivery, I am using rewrites in fire base hosting. Whenever open website with index.html then the browser request the firebase cloud function main.
"rewrites": [ {
"source": "/index.html",
"function":"main"
}]
Now I am facing a problem to provide dynamic content based on user login status. I also checked about client side authendication using JS.
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
I don't know about web development. Here I have few questions,
How can I find authentication status of user by more flexible way? Does cookies used for this? I am asking because I don't know to pass firebase-token to the cloud function main.
Please address me an idea. Thank you all.
Short answer: End users don't have a sign-in status that's visible on a backend. That's just not how Firebase Authentication works.
Auth clients provide credentials to get a token that's used to identify themself when they invoke backend services. This tokens has a lifetime of 1 hour, and the client must refresh it in order to keep using it. Your backend doesn't know or care if the client has an auth token, if they use it, or if they refresh it. The client just needs to provide that token from whatever device they have signed in so the backend can validate it. There is no way your backend can know if the client obtained a token - you just have to accept the one it is given. This means you're going to have to actually figure out how to pass that token and validate it with the Firebase Admin SDK, or use a callable type function using the Firebase Client SDK to send that token automatically.
I want to allow users to sign in/up via GitHub using firebase by clicking on the same button.
I create a new authentication for every user in the server side.
With the little piece of code, I'm able to detect if either the user is new or not:
const provider = new firebase.auth.GithubAuthProvider();
firebase.auth().signInWithPopup(provider).then((result) => {
if (result.additionalUserInfo.isNewUser) {
// The user is new
} else {
// The user is old
}
But, when the function signInWithPopup is called, if the user is a new user, a new authentication is automatically created for him. How can I avoid this?
And if the user is already authenticate, how can the user sign in from the client side? Where is the link between the authentication done from the back end with the user that wants to sign in the front end?
This is not how OAuth works. If you use an authentication provider like GitHub, they handle auth flow for you. The only thing that you are left with on the frontend side is an idToken with your identity, basic profile info, and a signature so you can as a user using this token. There's no distinction between sign up/sign in actions.
As you have noticed, Firebase is an extra layer in this flow, it creates an account for a user who signs in for the first time. But there's no user limit or extra payment so I wouldn't bother too much about these extra accounts. You might consider periodical cleanups if you care about the security here.
If you want to actually check if the user exists you have to use firebase-admin e.g. in a Firebase Function before the signInWithPopup is called. But still, unless you want to prevent users from signing up, you can hook your server logic into functions.auth.user().onCreate trigger.
To answer your last question, when the user is already signed in, you'll get the user object in firebase.auth().onAuthStateChanged when a page is loaded. Login state is stored by Firebase.js so once you have called signInWithPopup, you don't need extra steps.
I'm using firebaseUI to sign in users to my web app. I am using the redirect option. Upon successful sign in, the users are redirected to signInSuccessUrl, which is the admin page of my web app. However, I want to be able to pass the ID token associated with the user to the admin endpoint, where it can be authenticated and checked whether the user trying to log in has admin permissions or not (this I do by checking the user permissions in my database).
I've considered a few other options, namely:
Using firebase.auth().onAuthStateChanged on the admin page itself, checking if the user is signed in, and making a request to the backend to check if the user is an admin. If these conditions are met, render the page, otherwise, show permission denied.
The problem with this approach is that:
It moves a significant part of the auth to the client side
I can't restrict access at the endpoint level itself. In contrast, if I send an ID token, I can check if the user is an admin or not and accordingly decide what to render, instead of always rendering the admin page and then checking on the client side if the user is logged in and is an admin.
Making a dummy page in between the firebase sign-in page and the admin home page. This dummy page would check if the user is signed in using onAuthStateChanged as mentioned above, make a request to the backend to check if the user has admin permissions, and based on the results, redirect to either the admin home page or show permission denied and go back to the login page.
This is how the config would look like if I do this:
var uiConfig = {
signInSuccessUrl: '/admintestpage/',
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID
]
}
The /admintestpage/ endpoint would render test.html, which would have code something like:
<script type="text/javascript">
initApp = function() {
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
idToken = user.getIdToken();
/*Send idToken to a backend api to check if the corresponding user is an admin*/
/*redirect to https://my-app.com/adminpage/ if user is an admin, otherwise, redirect to https://my-app.com/login/ */
} else {
/*user is signed-off, redirect to https://my-app.com/login */
}
}
</script>
I'm keeping this as the last option as it doesn't look like a very good flow.
Here's how my uiConfig looks right now:
var uiConfig = {
signInSuccessUrl: '/adminpage/',
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID
]
}
The Crux is that I want to render my admin home page only if I know beforehand that the user is logged in and is an admin.
I want to know if there is a way to pass the ID token as a basic auth header when redirecting to the signInSuccessUrl from the firebaseUI page, or if the idea of sending an ID token itself is not necessary and there is an alternate better flow.
I think you're on the right track. I've been struggling to find something elegant to do this as well, but I ended up doing what you did. Ideally I wish signInSuccessUrl passed the jwt payload, therefore, my backend server could verify its authenticity, and I can then look up the user and then set the session or reject the session.
A lot of the API's and docs are written in the context of a "Firebase first" or "Firebase only" so you have to start getting creative integrating with a traditional REST API.
My context is somewhat similar. I'm a mobile-only app that used Firebase auth to offload auth, in exchange, it then linked to my own custom token. Recently I needed to make a few web properties and wanted to implement this same exchange for my own session management in a traditional client/server synchronous REST page.
window.initApp = function() {
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
user.getIdToken().then(function(accessToken) {
redirectPost("/login", {"access_token": accessToken, "authenticity_token": $("meta[name='csrf-token']").attr('content')})
});
} else {
// User is signed out.
}
}, function(error) {
console.log(error);
});
};
I'm writing a webapp where users will need to login with Facebook (a Facebookless login does not make sense in the context of the app). Ideally, after their initial visit, when a user visits /index, my webapp sees a cookie it deposited earlier, and seamlessly logs the user in automatically and goes to the application (/app).
My problem arises when the user logs out of Facebook, and returns to my app. Since their cookie on my domain will still be present, and their oauth_token will still be valid (they are for 60 days now), I can still log the user in automatically, and the app will work as expected.
To me, it doesn't seem right that the app remains signed in with their Facebook account even when they are not signed in to Facebook. I played around on Stackoverflow itself; it allows this behaviour as well. Are my worries misplaced, or is there a recommended way to see if a user is signed into Facebook when they first request /index from my server.
In my opinion, I don't think your app should remain signed in while the user has already signed out of Facebook.
One scenario where this may not be desirable is: what if I am using your app from a public computer. After I logged out of Facebook, your app still "remembers" me. And now anyone who uses this computer will assume my Facebook identity inside your app.
I think the problem here is that you set your own cookie to remember the user's Facebook login status. Obviously, when user signes out of Facebook itself, your cookie is not cleared. So at this point your cookie is out of sync with Facebook status.
I recommend that you don't use your own cookie for the purpose of remembering user's Facebook login status. Always rely on Facebook itself for this purpose.
The general strategy is, whenever user comes to your app, you should check the Facebook login status by using the mechanism provided by Facebook. This way, your app will be in syn with Facebook in terms of user's login status.
I personally use this piece of code to call Facebook Javascript API for the purpose of user login:
/*
* Init code for Facebook connect
*/
window.fbAsyncInit = function() {
FB.init({
appId : FACEBOOK_APP_ID, // App ID
channelUrl : CHANNEL_URL, // Channel File
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true, // parse XFBML
oauth : true
});
// check facebook login status
FB.getLoginStatus(function(response) {
console.log("FB login status: " + response.status);
if (response.status === 'connected') {
showWelcome(); //display welcome message
} else if (response.status === 'not_authorized') {
// the user is logged in to Facebook, but not connected to the app
showFbLogin(); //display Facebook Login button
} else {
// the user isn't even logged in to Facebook.
showFbLogin(); //display Facebook Login button
}
});
// subscribe to facebook events
FB.Event.subscribe('auth.authResponseChange', function(response) {
fbAuthResponseChanged(response);
});
};