Architecture for login system on MEAN stack? - javascript

I'm developing a web app on the MEAN stack (MongoDB, Express, AngularJS, and node.js). I'm developing a login system, and will also have some of the Angular routes protected so that only logged-in users can access them. I'm trying to think of the best way to approach the architecture of this.
I'm thinking of the current workflow:
User logs in via AngularJS form, which sends an http POST to an Express endpoint. The endpoint validates the user against the database, and responds with an OAuth token as well as a cookie. Both are stored in the mongo database for later validation.
Once AngularJS receives the login response, it stores the received cookie using ng-cookies, and stores the OAuth token in a User service.
Every time the route changes in AngularJS now, the User service is used to make sure that the cookie is still legitimate by comparing it to cookies in the mongo database (this would be an API call using Angular's resolve... would this create a noticeable lag?)
When a user clicks "log out" or the cookie expires, the cookie and OAuth token are both deleted from the database and will no longer be valid.
Does this approach make sense? Is it secure, and will it be relatively efficient/quick in execution?

I ended up combining my original workflow with Express's auth example, seen here. It is as follows:
When user initially loads the app, an http call is made to an Express endpoint that checks if a session exists already for the user. If so, the user is stored in $rootScope and considered logged in.
Any time the AngularJS route changes, the same endpoint is accessed. Route protection was specified in a way similar to that described here. If the endpoint ever returns that no session exists, $rootScope.user is unset (if it needs to be), and the user is redirected to the login page.
When the login form is processed, it posts to an Express endpoint. The endpoint retrieves the user from the mongoDB (if it exists), and attempts to hash the password. If it's a match, the user's session is set, stored in the mongo DB, and the endpoint returns the user object (used to store in the $rootScope as previously mentioned).
Any time any further endpoints are accessed, the functions are first passed through the restrict function which ensures that a session exists before sending any data to the client. It returns a 401 if no session exists, which is then handled on the Angular side using this HTTP interceptor to unset $rootScope.user and redirect to the login screen.
When the user clicks "log out" on the Angular side, the session is unset and deleted from the mongo DB, $rootScope.user is set to null, and the user is redirected back to the front page.

Related

How can I provide a custom second factor of authentication using Firebase?

I want to provide my application's users the ability to add a second factor of Authentication and I'm currently using Firebase for logging and signing in new users. Firebase already lets you use a second factor but your only possibility is to send an SMS to a verified phone number. I want to replace this second factor with Authy OneTouch so I was wondering what's the best practice in this case.
Right now here's how I authenticate my users:
An user logs in using a form in my client. Under the hood I'm using signInWithEmailAndPassword, retrieving the IDToken from Firebase and setting persistence to NONE.
The client sends a post request to my backend attaching the IdToken and singOut immediately after.
If the token is valid, the server will provide an HttpOnly session cookie storing that token that will be sent along with future requests to my backend to keep track of the user's authentication state.
How I plan to change my workflow:
Through the profile page, the user may or may not opt-in for the second auth factor.
On user opting in I'll send a request to my server and it will call setCustomUserClaims to attach a custom mfa claim and do the necessary steps to register a new user on Authy.
On logging in I access the claim calling getIdTokenResult and access tokenResult.claims.mfa. If it doesn't exist then the process goes as the previous list. Otherwise I log the user out, ask him/her to go through the second factor, and regularly poll a specific URL that Authy provides to get updates on the status of the second factor challenge.
When I detect that the second factor challenge was successfully completed I send a post request to my backend attaching the idToken I got from Firebase and relevant infos I got from Authy's response.
The backend verifies if the idToken is valid (to make sure the email-password challenge was previously performed), then proceeds to generate a custom token with Firebase user's uuid, email, password, mfa-status using createCustomToken, then performs a signInWithCustomToken and sets a session cookie in the response.
Is my idea fundamentally correct? In case does Firebase provide a way to customize its default second factor and I just missed it? Thanks.
While Firebase Authentications's paid brethren Google Cloud Identity Platform does offer 2FA with SMS, neither of them currently provides an option to use/require a custom second factor for authenticating a user.
It's a common request though, so I recommend filing a feature request for it.
Until the feature is added to Firebase itself, the only way you can do something like that is through a custom provider, which allows you (but also requires you) to take control of the auth flow yourself.

How to store user data in browser more securely?

I'm working on a project where I'm verifying admin and user from the backend. I have a database and there is a collection called admins. I stored my admin's email there and when a user will log in if the user's email is available in the database it will return isAdmin true or else it will return false. so that's how I can give the admin special access. But the problem is I'm storing my user information in the local storage so every time when the user opens the browser I can get his info without login him. so I have to also store isAdmin true or false in the local storage. If anyone edits the local storage he can get access to the admin panel. I want to know how can I make it more secure or how can I store isAdmin more securely so that no one can edit it from the client-side?
so ideally you shouldn't really solve these issues on frontend side of application but on API (backend, db, whatever you use). Only this that should be somehow saved on frontend (client) is user's token and with every request to API you should include this token and backend should be responsible for authorisation of request.
Simple example:
user logs in to api
api responds with auth token - 'abcd1234'
client saves this token to local storage / cookie
in next calls you include this token to request
GET /all-users { headers: { Authorisation: 'abcd1234' } }
API reads this token, decides if owner of this token is authorised to access these data
It's not this simple in real life, but you should understand a little.
And now solution to your problem - i suggest you to do none of this, but for school project its okay i guess
store your role in some hash so user doesn't know what is under the hash and can't simply guess admin hash
use some UUID instead of role - basically same as 1.
store your role in some global variable - so this way users can't see them in localStorage or cookies -- but its on client so its accessible to everyone in source code

Consume Saml Response from external identity provider

I am having trouble configuring my application to consume the SAML response from an external identity provider (in this case, OneLogin). My application has a javascript (angular5) front-end and .NET back-end.
Here is our current work-flow when using our own identity provider and service provider.
User navigates to our application in the browser and receives a 401 unauthorized when requesting resources from the API.
User is redirected to the login page.
User enters credentials which are posted to our server-side identity provider.
If the user is authenticated, the identity provider returns a SAML response to the client.
Client posts the SAML response to the service provider.
Service provider returns the tokens needed to access the rest of the API.
Now a customer has requested we integrate their external identity provider using OneLogin. Here is what I understand to be the new workflow.
User navigates to our application in the browser and receives a 401 unauthorized when requesting resources from the API.
User is redirected to the OneLogin (external identity provider) page.
User enters credentials which are authenticated by OneLogin.
OneLogin will post to our provided service provider endpoint with the SAML response as Form Data in the HTTP Post.
If post is successful the user will be redirected to any url provided when configuring OneLogin.
?????? (Our front-end still has no idea who the user is or whether or not they are authenticated).
Currently, all communication between our angular front-end (client) and .NET back-end (server) is initiated by the client. What I really need is for the client to receive that SAML response form data that is posted by OneLogin, so that I can initiate authorization with our service provider and receive the appropriate response in the client, but from what I understand I won't be able to consume the Form Data client side. I was hoping I could have the external identity provider redirect to a page on our front end and encode that saml response as a query parameter, but I am not seeing any way to do that.
I feel like there is something I am missing, but all the OneLogin examples seem to be the OneLogin identity provider communicating with a server side service provider and no mention of the client side. I could use some help better understanding what I need to do to accomplish my goal of informing the client that the user is authenticated and authorized.
What you describe with OneLogin is the SAML2 Web Browser SSO profile. As you see, it's all done through the browser. The user gets redirected from the client to the IdP, where they login. The IdP then POSTs a SAMLResponse to your Attribute Consumer Service (ACS) URL at your back end. It's the ACS's job to decode the SAMLResponse and parse the SAML2 attributes contained within it. It can create a new session at that point if required.
At this point the back end now knows who the user is. So you can initiate another redirect to send the browser to another URL with a parameter that lets the client know it has to retrieve user details from the back end. Perhaps some sort of /sso?token=something URL. The client side can then ask the back end to return JSON perhaps that contains the user information referenced by token, i.e. a session of some sort the back end has created once the attributes have been verfied.

Facebook Javascript SDK Autologin & Tokens

I am using the Facebook login as an authentication for my PhoneGap application - once a user logs in, their data is retrieved from my database to display information. I am not using the SDK for any other purpose.
I have the Facebook auto login working fine - it retrieves an authResponse and my Facebook information. Since the access token changes with each login, what can I use to store locally and in my database to authenticate the user on my server for future logins?
Here is a flow that I think could work...
User sees logs in screen and enters Facebook credentials
Facebook securely validates and returns user information & access token
The app uses localStorage to store user email and access token
For future autologin, the localStorage values are used as email/password
I feel like this cannot be the correct answer, however.
I figured out a solution - I was confused about storing passwords on my database to fetch user information. Rather, these are the correct steps:
Use Facebook SDK to handle the login and retrieve the authResponse
Update the user table in my database with the temporary access token and retrieve user's information
For every POST or GET the user wishes to perform, I will match the FB.getLoginStatus() results from the database's access token (the check will be done server side)
If the tokens match, perform requests. Otherwise, force the user to login again.

Maintaining client side page state on page refresh in angular.js applications

I am building an Single Page app using angular.js and I am facing this issue for which I am not able to find the right answer.
When we do a full page refresh in an angular app, how should we check if the user still has a valid session ?? State Provider or UI router merely routes the url to the requested page, but what if the session of the user has expired ?
One thing that comes to my mind is to use a service and store a Boolean value there once the user logs in and on every page refresh or state change, we check this Boolean and redirect the user to login page, if this value is false. But, if we do a refresh, this Boolean value is reset.
Thought of storing this key value in a cookie or html local storage, but how safe are these values getting stored here. Some one can reset the value of this Boolean to gain access to a page.
Please let me know.
To make a client-side app secure you will need to involve a server of some sort. Storing values in cookies or local storage will not do any good as these can be manipulated by a user (as can everything else on the browser).
Not sure what options you have available to you but I would recommend looking into Nodejs/Expressjs/Passportjs - this is a pretty awesome combo and very good support here on SO.
Once you make progress in this area you will then be in a position to ask a more focused question.
I think you're conflating a few concepts here.
One - is the user authentication to the server. It must be the server, otherwise the concept of a user session in a client-side-only app is useless. This is facilitated (typically) by an authentication cookie. The cookie is a security token given to the user and signed by something secret on the server. The cookie contains things like login name and expiration. The cookie is validated by the server on every request the browser makes.
Two - is the nice user experience maintained in the client-side app. What I mean by that, is that if you didn't check whether the cookie has expired, your ajax calls to the server would (and should) fail with HTTP 401 - Unauthorized. You likely would want to prevent that, and have your app preemptively redirect to a login page, or if applicable, request to refresh the security token.
So what does all of that mean?
Enforce authentication on the server
Create a loginService that checks session expiration from a cookie, or whatever else you use fo your user authentication.
Use resolve parameter of $routeProvider or $stateProvider to have the loginInfo available to controllers. Here's my answer on SO to a question you might find useful.

Categories

Resources