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);
});
};
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.
Keystonejs has some kind of bug on the Admin UI login page:
While trying to access Keystone Admin UI, with User that Permissions isAdmin = false
In case login/pass is correct Keystonejs screen says: "You're already signed in."
There should be response something like: "You don't have permissions"
It's no too informative so I suggest to change it to something like "You don't have permissions".
So question: is it possible to change text of that message?
The issue is that you have logged in using valid credentials but have not granted that user access to the Admin UI. There is (as at Keystone 4.0.0) no default path for users without the canAccessKeystone permission. I believe the general assumption was that non-admin users should not be logging into Keystone admin, but authenticated sessions are still useful for API endpoints.
So question: is it possible to change text of that message?
I'm not aware of a straightforward way to only change this message, but if you're keen you could always modify the Keystone 4.0 source for your own requirements and submit a pull request to have this considered for merging into the project. I've created issue #4786 in Keystone's GitHub issue tracker to improve the messaging for users who successfully login but do not have access to the admin UI.
In the interim, one possible workaround would be to use the keystone signin redirect configuration to set a preferred entry point that can either handle all user types or redirect appropriately.
For example, you could add the following to your keystone.js config:
keystone.set('signin redirect', '/gohome')
The /gohome route could render a page or do a simple redirection based on user properties:
app.get('/gohome', function(req, res) {
var url = (req.user && req.user.canAccessKeystone) ? '/keystone' : '/user';
res.redirect(url);
});
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.
}
});
I've been working on this for the past day and can't seem to figure it out. I am using this Passport-Soundcloud to implement a soundcloud authentication. However what I don't understand is how I can get, and pass an authentication token to a front-end button push to like a sound.
My front-end code looks like:
function allowLike(){
$('.queueTrack').off('click').on('click', function(user){
console.log('clicked');
SC.put('/me/favorites/' + 21928809);
consol.log('sound liked')
});
};
Whenever I try to login through my app using the /login route, it works as expected and redirects me to my homepage. The problem is that I don't know how to get the oauth token from the passport-soundcloud so I can implement it into the front-end click event.
My routes followed the passport-soundcloud instructions and seem to work, but I can't figure out how to get the oauth token...
Any ideas? I'm totally lost on this.
So, I'm not familiar with the specific details of the soundcloud api. But if it follows the basic patterns of popular oauth apis. Then you'll want to do something like this.
User arrives at your site without a cookie
they authorize your app using oauth
when the user is redirected back to your app, SoundCloud will give you an access key for this user. Store this value, in a database or a cache or in memory. But most importantly, you must create a cookie on that users browser. So that when they return you can lookup the access key again.
When the user clicks like, lookup the accesskey on the backend and hit SoundCloud api with that token.
In the initial oauth flow....
function(accessToken, refreshToken, profile, done) {
User.findOrCreate({
soundcloudId: profile.id,
token: accessToken // <--- store the token!
}, function (err, user) {
return done(err, user);
});
}
then when they click like
app.put('/me/favorites/12345' function(req, res) {
var id = req.user.id; // <--- passport and connect session saved the user id for you
// lookup the token in your database
// use the token to hit the soundcloud api
})
I hope this make some kind of sense. This is completely untested pseudo code.