Sails JS: How to store and access current user data? - session

I already checked a lot of references and found good sources like this one: Get current user from inside the model in Sails. So I'm asking for best practices and your experiences.
As I've developed a quite complex platform based on JWT-Authentication I have to fix the major mistake to store the current user data (while user requests something) on my sails instance. I know that this leads to major security leaks (for more than one user).
The question is: How can I store and access current user data without passing the session object through almost all methods I've created?
Is passing the session object around through all helpers, utilities etc. the only way to solve this? Instead of using a centralized Service like: UserService.getCurrentUser();
Any help is highly appreciated. Thanks!

If you're asking if there's a way to globalize the user data so that it's magically available to all your methods, the short answer is that there's no safe way to do this in Node (let alone in Sails.js). Node's single-threaded nature makes it impossible to maintain state in that way.
Some folks have solved this in Sails by using a globally-applied policy that looks up the user and adds it to the request:
// api/policies/fetch-user.js
module.exports = function fetchUserPolicy (req, res, next) {
// Get the user ID out of the session.
var userId = req.session.userId;
// If there's no user logged in, just continue.
if (!userId) { return next(); }
// Look up the user by ID.
User.findOne({id: userId}).exec(function(err, user) {
if (err) { return res.serverError(err); }
if (!user) { return res.serverError(new Error('Could not find user in session!')); }
// Add the user info to the request.
req.user = user;
// Continue the request.
return next();
});
};
There's nothing wrong with this code, but we don't recommend it because best practice is to use policies purely for access control. Instead, you can do pretty much the same exact thing in a custom hook:
// api/hooks/fetch-user.js
module.exports = function fetchUserHook(sails) {
return {
// Add some routes to the app.
routes: {
// Add these routes _before_ anything defined in `config/routes.js`.
before: {
// Add a route that will match everything (using skipAssets to...skip assets!)
'/*': {
fn: function(req, res, next) {
// Get the user ID out of the session.
var userId = req.session.userId;
// If there's no user logged in, just continue.
if (!userId) { return next(); }
// Look up the user by ID.
User.findOne({id: userId}).exec(function(err, user) {
if (err) { return res.serverError(err); }
if (!user) { return res.serverError(new Error('Could not find user in session!')); }
// Add the user info to the request.
req.user = user;
// Continue the request.
return next();
});
},
skipAssets: true
}
}
}
};
};
Either way, you'll still need to pass req around to any methods that want to use the user info that was fetched.

Related

What to implement when "msal:acquireTokenFailure" is thrown?

I know this should be simple, but a little help would be appreciated; we're all a bit new to using industrial strength typescript packages.
We're building an Angular App and using the #azure/msal-angular library, which for most part works OK; following the tutorials and examples and it generally all make sense.
Apart from what to implement when the "msal:acquireTokenFailure" event is broadcast?
In the ngOnInit() method of our AppComponent we have this line
// Subscriptions and redirects are for jwtTokens
this.subscription = this.broadcastService.subscribe("msal:acquireTokenFailure", () => {
// ToDo: What should be implemented here?
});
In a few posts to the GitHub page for the project contributors have suggested something along the lines of
// Subscriptions and redirects are for jwtTokens
this.subscription = this.broadcastService.subscribe("msal:acquireTokenFailure", () => {
this.subscription.unsubscribe();
this.authService.loginRedirect();
});
Which, as far as I can, will redirect to an AzuerAD login screen but will lose the underlying call details that we trying to get a Token for.
What looks more useful (in pseudo-code) would be something like
// Subscriptions and redirects are for jwtTokens
this.subscription = this.broadcastService.subscribe("msal:acquireTokenFailure", () => {
this.subscription.unsubscribe();
if (isIE) {
this.authService.acquireTokenRedirect(userRequest);
} else {
this.authService.acquireTokenPopup(userRequest);
}
});
The question being, is this a valid approach; and where would we recover the userRequest parameters from?
Please, please, don't redirect me to the Microsoft docs; I've spent hours going round in circles following the same links...

rxjs can manual subscription handling be replaced with switchmap

I have a service to manage logged in user. I have a second service that provides datasource for logged-in user's list items. Both services are singletons and (possibly) live longer than one users login.
I have this pattern reoccurring a lot:
this._loggedInUserService.loggedInUserObservable.subscribe(loggedInUser: User => {
// Remove old subscription
if (this._subscription) {
this._subscription.unsubscribe()
this._subscription = null
}
if (loggedInUser) {
this._subscription = this._otherService.getUserSpecificObservable(loggedInUser).subscribe(...)
}
})
Now that I have read a bit about switchMap, is the following functionally equal with the code above? Is the subscription correctly ended if the user changes?
this._loggedInUserService.loggedInUserObservable.pipe(
switchMap(user => {
if (user) {
return this._otherService.getUserSpecificObservable(loggedInUser)
} else {
// What to return here?
}
})
).subscribe(...)
Also, what should I return in the else? I don't need the subscription to work at all in that case, so is it safe just to return null or undefined? Or should I return empty Observable (import { EMPTY } from 'rxjs')? (The code in subscribe does not need to be run if there is no active user.)
As long as you aren't interested in values that are not defined, you can just filter them out before calling the second service:
this._loggedInUserService.loggedInUserObservable.pipe(
filter(user => !!user),
switchMap(user => this._otherService.getUserSpecificObservable(user))
).subscribe(...)
You can also see the principial idea in action here: https://stackblitz.com/edit/typescript-p75oku

Where to filter Identity 2.0 claim ticket in a WebAPI app?

ASP.NET apps using OWIN permit multiple Identity sources (Facebook, Google, etc.). Most of the provider-specifc information those sources provide is irrelevant to my app, potentially even large, and I don't want it in my cookies all session. My app is primarily WebAPI, but I suspect the question applies equally to MVC and WebForms.
For now, all I need is an integer account ID. Where/when should I reconstruct the identity, after external authentication?
For example, here is one way I could filter claims:
public ReplaceExistingClaims(ClaimsIdentity identity) {
{
Claim customClaim = GetCustomClaimFromDbForIdentity(identity);
foreach (Claim claim in ClaimsIdentity.Claims) ClaimsIdentity.RemoveClaim(claim);
ClaimsIdentity.AddClaim(customClaim);
}
And following are two different places I could inject those claims changes:
var facebookAuthenticationOptions = new FacebookAuthenticationOptions
{
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = context =>
{
ReplaceExistingClaims(context.Identity);
return Task.FromResult(0);
}
}
};
Above, I know I can hook an individual provider from Startup IF it provides an Authenticated event. I have two conceptual problems with this. One: it requires me to write and wire up my code separately for each provider I plug in. Two: there is no requirement for providers to provide this event. Both of these make me feel like there must be a different intended insertion point for my code.
public ActionResult ExternalLoginCallback(string returnUrl)
{
ReplaceExistingClaims((ClaimsIdentity)User.Identity);
new RedirectResult(returnUrl);
}
Above, I know I can put code in ExternalLoginCallback. But this happens too late for two reasons. One: The user has already been issued a ticket I consider invalid, but the default [Authorized] considers valid because it's signed by me, and now they are making requests to my site with it. There could even be race conditions here. Two: There is no guarantee the browser will visit this redirect, and I'd prefer from a design perspective if it didn't have to, e.g. to simplify my WebAPI client code.
To the best of my knowledge, the best solution will meet these requirements:
same code applies to all providers
client receives my custom ticket from my server (e.g. without image claims)
client never receives another ticket format from my server
the authentication process requires the minimum possible HTTP round-trips
token-refresh and other core identity features are still available
once a user is [Authorize]d, no further account transformation is necessary
database/repository access is feasible during ticket generation
Some pages I'm researching, for my own notes:
How do I access Microsoft.Owin.Security.xyz OnAuthenticated context AddClaims values?
https://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.Facebook/FacebookAuthenticationHandler.cs
https://katanaproject.codeplex.com/workitem/82
https://www.simple-talk.com/dotnet/.net-framework/creating-custom-oauth-middleware-for-mvc-5/
You have to implement DelegationHandler and put all your authentication routines in it.
Register at Application start (DI usage is enabled):
private static void RegisterHandlers(HttpConfiguration config)
{
var authHandler = new MyFacebookAuthHandler();
config.MessageHandlers.Add(authHandler);
}
And this is an example of implementation:
public class MyFacebookAuthHandler : DelegationHandler
{
public override sealed Task<HttpResponseMessage> OnSendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
try
{
// Process credentials
// Probably you have to save some auth information to HttpContext.Current
// Or throw NotAuthorizedException
}
catch(NotAuthorizedException ex)
{
return request.CreateErrorResponse(HttpStatusCode.Unauthorized, ex).ToCompletedTask();
}
catch (Exception ex)
{
return request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex).ToCompletedTask();
}
return base.OnSendAsync(request, cancellationToken);
}
}
The ClaimsAuthenticationManager class is specifically for this.
https://msdn.microsoft.com/en-us/library/system.security.claims.claimsauthenticationmanager(v=vs.110).aspx
Code sample from that reference:
class SimpleClaimsAuthenticatonManager : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (incomingPrincipal != null && incomingPrincipal.Identity.IsAuthenticated == true)
{
((ClaimsIdentity)incomingPrincipal.Identity).AddClaim(new Claim(ClaimTypes.Role, "User"));
}
return incomingPrincipal;
}
}

Sequelize correctly executing multiple creates + updates

I have a cron job that scrapes a list of items on a website and then inserts or updates records in a database. When I scrape the page, I want to create records for new ones that haven't been created yet, otherwise update any existing ones. Currently I'm doing something like this:
// pretend there is a "Widget" model defined
function createOrUpdateWidget(widgetConfig) {
return Widget.find(widgetConfig.id)
.then(function(widget) {
if (widget === null) {
return Widget.create(widgetConfig);
}
else {
widget.updateAttributes(widgetConfig);
}
});
}
function createOrUpdateWidgets(widgetConfigObjects) {
var promises = [];
widgetConfigObjects.forEach(function(widgetConfig) {
promises.push(createOrUpdateWidget(widgetConfig));
});
return Sequelize.Promise.all(promises);
}
createOrUpdateWidgets([...])
.done(function() {
console.log('Done!');
});
This seems to work fine, but I'm not sure if I'm doing this "correctly" or not. Do all promises that perform DB interactions need to run serially, or is how I have them defined ok? Is there a better way to do this kind of thing?
What you're doing is pretty idiomatic and perfectly fine, the only room for improvement is to utilize the fact Sequelize uses Bluebird for promises so you get .map for free, which lets you convert:
function createOrUpdateWidgets(widgetConfigObjects) {
var promises = [];
widgetConfigObjects.forEach(function(widgetConfig) {
promises.push(createOrUpdateWidget(widgetConfig));
});
return Sequelize.Promise.all(promises);
}
Into:
function createOrUpdateWidgets(widgetConfigObjects) {
return Sequelize.Promise.map(widgetConfig, createOrUpdateWidget)
}
Other than that minor improvement - you're chaining promises correctly and seem to have the correct hang of it.

Passport and Passport Local req.isAuthenticated always returns false

I haven't been able to track this down, but for my set up, isAuthenticated always returns false even after a successful login. Here's the passport code:
req.isAuthenticated = function() {
var property = 'user';
if (this._passport && this._passport.instance._userProperty) {
property = this._passport.instance._userProperty;
}
return (this[property]) ? true : false;
};
But in a quick look around I don't see the _userProperty proeprty anywhere in the local strategy (sorry if I didn't look hard enough), so I suppose that might be why it's always returning false?
I'd leave a code sample of my application code, but I feel it's probably easier to have a quick look at the repo for my work in progress:
passport api token sessionless
Ultimately, my goal is to have logout work properly for that boilerplate project (which it currently it doesn't).
I guess you forgot to put: req.login(...) inside passport.authenticate('local', function(...){}).
See here (at the end of the page)
Apologies if my original question is not that useful in the first place, but...
I found that my combination of passport, passport-local, and passport-local-mongoose, a solution was to simply create an invalidation method on my mongoose Schema (that has the passportLocalMongoose "plugged in", and when my /logout route gets hit I essentially remove that user's token. Here's that method:
Account.statics.invalidateUserToken = function(email, cb) {
var self = this;
this.findOne({email: email}, function(err, usr) {
if(err || !usr) {
console.log('err');
}
usr.token = null;
usr.save(function(err, usr) {
if (err) {
cb(err, null);
} else {
cb(false, 'removed');
}
});
});
};
I presume it's more interesting to see this in context so again please feel free to refer to the repo listed in question...hope this helps someone.
Also, if a core from one of the aformentioned libs wants to suggest a better way I'd of course love to refactor my code to make it idiomatic; if not, this approach seemed to work.

Resources