I'm currently trying to build an app that needs the user to authenticate with CAS (Centrale Authentication Service) to use it. I'm using Angular 5 for the front-end and Node.js for the back-end.
To authenticate the user on the server side I'm using a module called 'r-cas-authentication' which creates an express-session when the user is authenticated. The fact is I don't know how to check on the Angular side if the user is authenticated and otherwise redirect him on the CAS authentication page.
Do you know how I could do that?
Thanks!
You don't need to deal with this on angular side. Just have the Node server redirect the unauthenticated users to a login page, always. Browser sets the session headers, and your Node server will see there's no auth and send a redirect. The r-cas-authentication module mentions the bounce and bounce_redirect options in the docs, this will further streamline the process.
Your node instance will serve the login page.:
app.get('/login', (req, res) => res.sendFile('path/to/login.html'));
Additionally, your assets (frontend scripts, css, images, etc) are (usually) free to have:
app.get('/assets', express.static(pathToStaticDirectory));
Also, you have authentication endpoint:
app.get( '/authenticate', cas.bounce_redirect );
Next, it needs to serve the Angular app. It will, however, require a cas session here. We target '/home' but it can be another path like '/' or whatever have you.
app.get('/home', cas.bounce, express.static(pathToAngularAppDirectory);
Finally, you will likely have REST API endpoints that you want protected:
app.use('/api', cas.block, appRoutes);
Now, here's what happens. Request comes infrom the browser. It hits the '/home' route. Since the browser is on it'S first visit, there's no authentication headers set. It will hit the cas.bounce() block and get redirected to /login route. Once it goes there, it gets served login.html*.
You enter your login data there and hit Login button - the page gets submitted, and cas middleware authenticates the user - sets the session data and all to browser. Browser will cary these around with it (note I say browser, not Angular). It also redirects the user back to '/home' page.
Now your browser has the session info, so it gets a pass on the route, gets served the Angular files, the app starts normally. All is cool.
There is one more case you might wanna handle from Angular.
Let's say your session expired. And the browser was left open or something. The user revisits the page - all cool, cas.bounce will redirect the user back to login form.
But what if the user simply clicks "refresh" in your app? Then Angular's HttpClient gets a 401 response (as per node module docs). You can have a simple http interceptor that does a window.location.reload() or similar (basically a full page reload, not client-side navigation), and on a page reload, your app get's caught into cas.bounce because it's trying to reload /home unauthenticated.
Related
I have a react app and a nodejs server. I set a httpOnly-cookie containing a JWT for authentication. This works. The problem is: I need some logic client-side to check if the user is logged in. When the user logs in, I could store this "state" in-memory (eg. useState), but when the browser reloads, this state is gone (while the cookie is still there).
I'm tried using js-cookie but obviously this won't work because it's a httpOnly cookie.
How can I check - without doing a (test) axios request to my server - if the user is logged in, when opening the react app in the browser?
Edit:
The answer in this question recommends to store the token in LocalStorage, but other resources (lik the discussion in the answer of this question) says cookies are the way to go.
to be clear, I don't need direct access to the token in the cookie, the cookie is send with every axios request ({withCredentials: true}) and it works like expected. But I just need to know if the cookie is set (and so the user is logged in).
There can be multiple approaches for this scenario. What I think you can do.
1 - You can send a http request to check if the JWT is valid on initial app load and whenever app is reloaded (Same thing basically) and then preserve some authentication state inside the app (Context Api or Redux) and this way you control the routes, etc.
2 - Make sure that whenever the JWT is expired you clear the cookie and whenever client receives 401 you refresh whatever authenticated state you have and redirect the user to login page or any page that does not need authentication.
Just to add to the selected answer.
a loading component and an isLoading state will help prevent the split-second showing of authenticated / protected screens. ex, isLoading ? <LoadingComponent /> : <ProtectedComponent />
You can just update the isLoading state when the request finishes, and should the request yield an unauthenticated response code, you can then perform a redirect.
I am working on the logout part of my website and I am using JWT for authentication and using cookies to send the JWT for client side.
For logging out I am passing some dummy token value with the same token name so that it over rides the previous token. But when I log out of the portal, I am still able to access my dashboard. There is some glitch in the logout functionality. I guess it is due to the browser cache.
I have few questions regarding Express.
Are there any ways to reload the current page using Express and delete the browser cache while doing so?
I need to disable the browser forward option of chrome once the user is logged out, how can I achieve this using express?
How to redirect the user to his dashboard when he tries to hit '/login' or '/signup' route when he is already logged in? I am using the JWT authentication for login
Thanks in advance
Are there any ways to reload the current page using Express and delete the browser cache while doing so?
The server can't, on its own, tell the browser what to do. The browser has to initiate communications and then act on that communications.
You could have the web page in the browser reload its own page using Javascript with window.location.reload(true) at any time. If you want the web page Javascript to be told when to do this by the server, it could either send regular Ajax calls to the server and, based on the response, decide when to reload the page. Or, it could have a webSocket connection to the server and the server could send the web page some data that, when the web page received that data, it would see that it should reload its page.
We could help you better if you told us what the real problem was here. Web pages can use Javascript and/or webSocket connections to dynamically update themselves rather than just reload all the time. That's a more modern design.
I need to disable the browser forward option of chrome once the user is logged out, how can I achieve this using express?
There's a discussion of disabling the forward button here: HTML5 history disabling forward button. You will probably find this is a brute force approach (it involves getting rid of browser history) and there is likely a much better way to solve whatever real problem you're trying to solve. It also sounds like you may also want to manage browser cache expiration too.
How to redirect the user to his dashboard when he tries to hit '/login' or '/signup' route when he is already logged in? I am using the JWT authentication for login
When you detect a request to '/login' or '/signup' in Express from a user who is already logged in, you just respond with a res.redirect("/dashboard") from your server. FYI, there are lots of questions about whether this is the proper user experience. A user going to '/login' or '/signup' when they are already signed in could have any one of these use cases:
They don't realize they are already signed in or they don't know if they are signed in as the desired user.
They want to sign in as a different user.
They want to create a new account (different from what is currently logged in).
They are trying to figure out how to log out.
You should make sure that blind redirecting (and not taking the user to the page they asked to go to) still makes all these use cases entirely clear. If not, you will just frustrate the user by not taking them where they asked to go.
Here's the sitch
I've got a handy button on my app that will eventually let you sign in seamlessly with Google. It's a link to /auth/google on my Node server, which uses passportjs to do authentication, and thus redirects you to the Google url where you authorize the application. Upon successful auth, the redirect url (auth/google/callback) is hit and it works up a user object given the user's Google info.
The quesiton
Now the server can redirect the user to wherever it wants, and however that happens, the Angular app needs to get a user object. As far as I know, I can't put the user object in a redirect. What's the best practice for telling my Angular app that the user is auth'd and getting the app a copy of the user object?
I've considered redirecting it to some url on the front end that tells it to call the server asking for a new user object, but for some reason I'm thinking there's a classier way...
I found this description of how to implement SPA and nodejs-based authentication useful:
https://www.matthewtyler.io/handling-oauth2-with-node-js-and-angular-js-passport-to-the-rescue/
Maybe combine it with JWT stored in a HTTPS cookie instead of sessions:
https://stormpath.com/blog/token-auth-spa
So far I have mainly been using a single asp.net app metaphor where the razor pages are served together with the data from the same app, so I can protect certain controller actions for the ui and data controller actions using the same security.
My new app has a completely independent api web site (using servicestack) and a different asp.net UI app that consumes the api. Right now the two apps sit on the same server, but I want to support the ability for anybody to write a UI app that consumes my data, so the app could sit anywhere and could be a php app all I care.
The new UI uses razor and MVC but it is really a fully client side app, which requests data from the api site.
So, the problem is right there. I am used to automatically redirecting a page from the server side to the login when someone hasn't logged in yet. My UI's server side has no concept of the login situation of the api web site.
The best I can do right now is, at the begging of ANY UI page's load, to do a lightweight ajax call to the api web site, to get the current user info. If it's null, I do a client side document.location.href = .
This works, but has issues, primarily it causes a lot of client side busy ui stuff. An initial version of the UI loads empty (no data), then an awkward redirect to the login page happens. The client side app becomes chatty - every page that loads does an additional call to the api site. The server side redirect is clean because the first page you see is either the UI page that you already have access to or the login page.
My first question is, what is the best practice to do this kind of stuff? My second question is, is there a client side cookie on my UI page that deterministically tells me I am logged in to the api site? I inspected the cookies on a UI page before and after the login that sets the security to the api site and the cookies seem to be the same. My third question is - is there some kind of security actionfilter I can write for my UI mvc site, which somehow magically determines from the cookies of the request, whether the UI is currently logged in to the api site and if it is, it lets the request serve the page, if not, it does the sever side redirect to the login page.
Thanks
Edit:
Scott - thanks so much for the detailed explanation. One clarification - i use mvc only to really serve my client side html. I use all knockoutjs and Ajax calls to servicestack to render the data. My question is really about how to react to Ajax calls that return security exceptions and how to avoid presenting an empty html ui because the user is not logged in. My login page authenticates directly from html to ss bypassing the mvc part. It's not an spa where I can keep a single client side login state that applies to all views of the spa. There are multiple cshtml pages that all need to probe for login in order to not load empty and redirect to the login page...
So the MVC just serves the blank template, that includes the knockout js that will call the API to populate it? I believe this flow shows how your current pages are testing for a session using a lightweight ajax call to the api.
The problem with that approach as you have noted is that it has overhead, and a noticeable delay if there isn't a session.
Solution:
You can't test for the ss-id cookie in your JavaScript client application because of the origin difference. Knowing this cookie exists would give you an indication of whether a user might have a valid session. But seeing you can't access it, you have to work around this. When your login page successfully creates a session by calling the API, you should have the success method create a cookie that denotes that you have a session. Such as a hasSession cookie.
You can check for this existence of this cookie on each page load. It doesn't involve a server trip to verify it. If that cookie has the same expiration policy as the ServiceStack API cookie, then it should stay in sync.
The initial cshtml page state should hide the unpopulated page form contact using CSS, and show a loading indicator until the data is loaded from the API.
When the page first loads it should check if the hasSession cookie exists? If it doesn't then it shouldn't make any API calls, and should redirect immediately login.
How would I know that I can invoke ajax calls and succeed without making a test call?
You should just assume you have a session ss-id cookie if you have the hasSession cookie as you must have logged in successfully to get it. So make you call for the page data. If you get data back from the call and not a 401 exception then populate the form, and display it by altering the CSS.
If you got a 401 redirect to the login screen, and delete the hasSession cookie. The user won't have seen a blank unpopulated form because the CSS prevented this. They get a loading indicator while waiting, a perfectly reasonable state.
The 401 Authorization error should only occur once, and redirect to login, and that shouldn't even happen if your hasSession and the ss-id cookie expiration remain in sync.
I am just confused why you are trying to change the ServiceStack attributes now, subclassing [Authorize]. You shouldn't need to change the behaviour of the API.
I'm using node/express to make a pure backend api. My front-end (angular.js) is hosted on a separate server. I have a few lines of middleware for every request to allow CORS.
If I start chrome with -args --disable-web-security flags, everything works great!
However if I start it normally, cookies seem to not be getting set in the browser, and therefore sessions on the node side aren't kicking in. This is the same for safari/mobile safari/etc.
I've tried browser options such as "accept all cookies"/"never block cookies". I thought maybe browsers don't like localhost but this is the same behavior on localhost and on actual hosted domains.
The flow is:
I login and the session is set with an id, on success the frontend is directed to the next page. This works, and I console logged req.session.id and it's correct.
On the next page a request is sent, the node server is configured to use the id in the session for this request. With safari/mobile safari/chrome the req.session.id is suddenly empty. With chrome -security disabled, the req.session.id is still correct and behaves just like it should.
please refer to this answer that covers cross-domain cookies and session: Using Express and Node, how to maintain a Session across subdomains/hostheaders