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.
Related
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'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);
});
};
How do I determine which user has logged in in an express js application? I want to determine who is sending the request in my server program.
MeanJS stack
You can use req.user
exports.some_method = function(req, res) {
var user = req.user;
//do something
};
But you have to use users.requiresLogin to have persisted user
app.route('/model/:modelId').get(users.requiresLogin, my_controller.some_method)
It's implementation is pretty simple
exports.requiresLogin = function(req, res, next) {
if (!req.isAuthenticated()) {
return res.status(401).send({
message: 'User is not logged in'
});
}
next();
};
It's session based implementation indeed, but still good. That's it
Pure expressJS
You must use middleware that would detect current user by its cookie
It's more complicated indeed, and you have to write own implementation
But there are plenty of plugins, like passport, that would validate user by your fields. Also can serialize into req.user and vice versa
But i would strongly recommend to checkout MeanJS stack implementation, it's pretty easy to understand. As the name implies, it's MongoExpressAngularNode, so it's express based stack.
More
It depends on what kind of auth schema you are using, if it's REST, then you have to pass token in all requests to server, so that server checks db and get's user with corresponding token. If it's sessions based, then you can simple use any session based plugins. But the idea is same, when signing in, serialize user to session table, set cookie, when receiving request take cookie from requester, deserialize from session table, you got user now
I have been using passport.js. It works and I am able to get the oauth_token and Verifier easily from APIs.
In passport.js each API has a strategy which basically decide how to communicate with that API server. At the end a callback to get returned user profile.
But I saw no way to use oauth_token to get the profile myself. Its a one time shot at the end of Oauth authentication to save data in session or db.
Is there any way I can use my oauth_token to directly access the user profile any time usingpassport methods.
In prototype I saw userProfile function which does what I need but its a private method somehow. I don't know how to use it
Update 1
I have been scavenging the git repo for passport.js , I come to know they use the "node-oauth" to manage calls to the API servers. This is available in any strategies _oauth.
But I am not aware what calls to make to get resource token. Also I have to initiate all API calls in callbacks step by step to imitate the token access calls. Is there any standard way to do this.
Without digging into the code (but having used Passport before) I'm assuming that the oauth_token is being stored with the user data in your database. You may have to access your database models directly in order to get the token, then you can use it with the provider APIs to get access to the information you need.
Visit following page to get a grasp of how to use the _oauth property.
https://github.com/ciaranj/node-oauth
It works like this:
oauth.get(
'https://api.twitter.com/1.1/trends/place.json?id=23424977',
'your user token for this app', //test user token
'your user secret for this app', //test user secret
function (e, data, res){
if (e) console.error(e);
console.log(require('util').inspect(data));
done();
});
In case you implement your own OAuth1.0 strategy you easily could overwrite
following method to implement your own logic for fetching the profile data from the remote OAuth server:
/**
* #overwritten
* #method userProfile
* #class OAuth1Strategy
* #description used by OAuth1Strategy class while authenticating
* to get the user profile from the server
*/
OAuth1Strategy.prototype.userProfile =
function(token, tokenSecret, params, done) {
this._oauth.get(
config.profileUrl,
token,
tokenSecret,
function (e, data, res){
if (e) console.error(e);
console.log(require('util').inspect(data));
done(e, data);
});
};
I am trying to return a refreshToken using the passport module of node.js.
However I am using the code below and I am unable to log any refresh token but the access token works fine.
Is there any way to specify the access-type offline for this module to hopefully return this?
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://myurl/auth/callback"
},
function(accessToken, refreshToken, profile, done) {
console.log(refreshToken);
process.nextTick(function () {
return done(null, [{token:accessToken}, {rToken:refreshToken}, {profile:profile}]);
});
}
));
This returns the refreshToken as undefined.
Any help would be greatly appreciated.
This was solved with this link:
https://github.com/jaredhanson/passport/issues/42
specifically:
passport.authenticate('google', { scope: ['https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email'],
accessType: 'offline', approvalPrompt: 'force' });
Jared Hanson - bless you.
All you need is accessType: 'offline' and you'll get the refreshToken on first login.
You don't need approvalPrompt or prompt in the request.
Note this only works on first login if you don't capture and save the refreshToken and associate it with a user account on first login you can't easily get it again.
If you didn't capture it the first time someone logs in, then you have two options:
If a user logs in and you don't have a refreshToken for them, you can immediately forceably log them out (e.g. by expiring their session in your app) and tell them to go to https://myaccount.google.com/permissions and revoke access to your application then just sign in again.
When they sign in again they will get the same prompt for access they got on first login and you will get the refreshToken on that first new login. Just be sure to have a method in your callback in Passport to save the refreshToken to their user profile in your database.
You can then use the refreshToken to request a rotating accessToken whenever you need to call a Google API.
You could also add both accessType: 'offline' and prompt: 'consent' options, but this is probably not what you want in a web based application.
Using these will prompt every user to approve access to your app every time they sign in. Despite the name, approvalPrompt does not enforce this, at least not in the current version of the API (judging by the number of people mentioning it and how often oAuth APIs change it's entirely possible this behavior used to be different).
This isn't a great approach for web based apps as it's not a great user experience but might be useful for development/debugging.
More about the second option:
This option is intended for scenarios such as mobile or desktop apps where the tokens will persist locally (not in a browser, where they are much more likely to be cleared when cache is cleared or they naturally expire).
Google limit how many refresh tokens they will issue for a user on an application (and they will invalidate the oldest tokens automatically and silently) so it's generally a bad idea to use this approach for a web app, as users might find they end up getting signed out of other browser sessions.