Login from other Domain using MeanJS API - angularjs

In MeanJS, after a user loggedin and we refresh the page, in core.server.controller.js will send a user credentials to the index.html
exports.renderIndex = function (req, res) {
res.render('modules/core/server/views/index', {
user: req.user || null
});
};
Ques 1: Why the server knows who is the loggedin user? even we have closed the browser and reopen it.
Ques 2
Lets't call the domain of above app is localhost:8000.
I have a separate website with just front-end code (angular) no backend.
Let's call it localhost:3000. I can call the API from localhost:8000 and display the data.
I can also login by calling localhost:8000/api/auth/signin API, but after I refresh it, it does not recognize me as signedin user because I have no server serve me the index.html like localhost:3000.
Any trick to make localhost:8000 login works?

Regarding Question 1, that happens because of the way express-session is configured. If you close the browser the cookie session will persist, unless you change the options defined in config/env/default.js, namely:
// Session Cookie settings
sessionCookie: {
// session expiration is set by default to 24 hours
maxAge: 24 * (60 * 60 * 1000),
// httpOnly flag makes sure the cookie is only accessed
// through the HTTP protocol and not JS/browser
httpOnly: true,
// secure cookie should be turned to true to provide additional
// layer of security so that the cookie is set only when working
// in HTTPS mode.
secure: false
},
According to express-session docs you can use expire and maxAge to control that behaviour. If both maxAge and expire are unset most clients will consider this a "non-persistent cookie" and will delete it on a condition like exiting a web browser application. In MEAN.js maxAge is set and that's why the user keeps logged in even if the browser is closed. However after 24 hours the user will need to login again.
Regarding Question 2, I never tried anything like that but I think the answer may be in the domain, path and sameSite properties of express-session docs. Take a look and see if something works out according to your needs.

Related

Application page reloads on auto token refresh in private browsing, with okta-react sdk

I am using #okta/okta-react v4.1.0 and #okta/okta-auth-js v4.5.1 in my ReactJS application.
My OktaAuth instance looks like this:
new OktaAuth({
issuer: "issuer",
clientId: "clientId",
pkce: false,
redirectUri: "http://localhost:3000/implicit/callback",
tokenManager: {
autoRenew: true,
expireEarlySeconds: 5 * 60 // expire 5 mins early
}
});
It is expected that 5 mins before expiry of access token, the okta-react SDKshould make a GET call to /authorize?clientId= endpoint and fetch a new valid access token and should replace that in browser's application storage.
This transition appear smooth when I am not using a private browsing session (Incognito Mode).
But, when I am accessing this application in a private browsing session, while renewing this token ,my application page reloads and hence clears out any unsaved data available on the screen.
The different thing I noticed in private browser session network tab, is that,
3 calls were made to /authroize?clientId= endpoint, while in normal browser session, it was just 1.
the 3 api calls have their Status Codes as 200, 302, 200, while in normal session it was just 200.
It appears the second API Getcall, is forcing a redirect to redirectUrl and hence a page load, but why only in case of private session.
As you have observed, browsers started blocking third party cookies, so browser doesn't recognise that there is already an Okta session.
If you are using a SPA app, you can start using refresh token with rotation: https://developer.okta.com/docs/guides/refresh-tokens/refresh-token-rotation/
Thank you,
Raj
After some discussions , When I allow the third party cookies in my incognito browser session, this behavior cease to exist. So yes, for timebeing, using this as a solution.

Frontend/Backend separation: Safari not storing cookies from API which is hosted on a separate domain than its Frontend SPA client

I have a setup which - as far as I can tell - is fairly common nowadays: a backend REST API that lives on its own domain, say myapi.com, and a single page frontend application that is served somewhere else, say myapp.com.
The SPA is a client to the API and the API requires users to authenticate before they can do things.
The backend API is using cookies to store session data for some allowed origins among which myapp.com. This is in order to have a safe bus to transmit and store auth data without having to worry about it client-side.
In Chrome, Opera and Firefox, this works just fine: an API call is made to authenticate the user, Cookies are returned and stored in the browser in order to then be pushed together with the next call.
Safari, on the other hand, does receive the cookies but refuses to store them:
I suspect Safari sees the API domain as a 3rd party cookie domain and therefore blocks the cookies from being stored.
Is this the expected behaviour in Safari? If so, what are some best practices to get around it?
Perpetuating a tradition of answering your own question on this one.
TL;DR this is desired behaviour in Safari. The only way to get around it is to bring the user to a webpage hosted on the API's domain (myapi.com in the question) and set a cookie from there - anything really, you can write a small poem in the cookie if you like.
After this is done, the domain will be "whitelisted" and Safari will be nice to you and set your cookies in any subsequent call, even coming from clients on different domains.
This implies you can keep your authentication logic untouched and just introduce a dumb endpoint that would set a "seed" cookie for you. In my Ruby app this looks as follows:
class ServiceController < ActionController::Base
def seed_cookie
cookies[:s] = {value: 42, expires: 1.week, httponly: true} # value can be anything at all
render plain: "Checking your browser"
end
end
Client side, you might want to check if the browser making the request is Safari and defer your login logic after that ugly popup has been opened:
const doLogin = () => {
if(/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
const seedCookie = window.open(`http://myapi.com/seed_cookie`, "s", "width=1, height=1, bottom=0, left=0, toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no")
setTimeout(() => {
seedCookie.close();
// your login logic;
}, 500);
} else {
// your login logic;
}
}
UPDATE: The solution above works fine for logging a user in, i.e. it correctly "whitelists" the API domain for the current browser session.
Unfortunately, though, it appears that a user refreshing the page will make the browser reset to the original state where 3rd party cookies for the API domain are blocked.
I found a good way to handle the case of a window refresh is to detect it in javascript upon page load and redirect the user to an API endpoint that does the same as the one above, just to then redirect the user to the original URL they were navigating to (the page being refreshed):
if(performance.navigation.type == 1 && /^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
window.location.replace(`http://myapi.com/redirect_me`);
}
To complicate things, it turns out Safari won't store cookies if the response's HTTP status is a 30X (redirect). Thereby, a Safari-friendly solution involves setting the cookies and returning a 200 response together with a JS snippet that will handle the redirect within the browser.
In my case, being the backend a Rails app, this is how this endpoint looks like:
def redirect_me
cookies[:s] = {value: 42, expires: 1.week, httponly: true}
render body: "<html><head><script>window.location.replace('#{request.referer}');</script></head></html>", status: 200, content_type: 'text/html'
end
What worked for me (as mentioned by others / in other similar questions too) is to put my Frontend and Backend under same domain, e.g:
frontend.myapp.com
backend.myapp.com
Then both Safari on Mac Monterey and Safari on iOS 15 started to allow set-cookie from backend.myapp.com (with Secure, HttpOnly, SameSite=none) and access them from frontend.myapp.com

iframe for token renew times out and we end up with path appended to redirect url

We are using adal-angular to protect our site. On load, we have some expensive queries, so to avoid having this reload (and repeat the queries) in the iframe, we have a custom redirect page:
adalAuthenticationServiceProvider.init(
{
anonymousEndpoints: accessDeniedPages, // Pages that are accessible when the user is not authenticated
instance: 'https://[redacted]/', // This is to differentiate Prod from Int environments
tenant: '[redacted]', // This is the name of our AAD tenant
clientId: clientId, // This is our AAD app client id (not the Web App)
endpoints: endpoints, // These are the other AAD apps we can talk to (see above)
redirectUri: frameRedirectUrl,
cacheLocation: 'localStorage' // Specifies Local instead of Session storage
}, $httpProvider);
Recently we have noticed a problem where, when the user goes to the site with expired tokens, the token renewal kicks in, but they are left either:
A) On our redirect page with their route parameter appended:
https://localhost:44301/app/frameRedirect/frameRedirect.html#/reports/ instead of https://localhost:44301/#/reports/
B) On the redirect url with the token appended
https://localhost:44301/app/frameRedirect/frameRedirect.html#id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6...
I enabled verbose logging, and the first thing I noticed was that there were timeouts creating the iframe:
Thu, 08 Mar 2018 23:05:35 GMT:1.0.17-VERBOSE: Loading frame has timed out after: 6 seconds for resource https://[redacted]
When I bumped up the timeout (from 6 seconds), things seem to be working (though getting into the repro state is always iffy).
I can't bump this up in production, however, as we are getting adal from CDN and not a local copy.
Questions:
What would make this start timing out now (has been working for 2 years)
Why are we ending on the redirect page with the path appended instead of being taken to the correct location?
Per the second item, I debugged and see that it is trying to go to that location with $window.location.href, but never seems to get there.
thanks,
~john

How to stop Web API getting 401 when using OWIN Cookie authentication

I have a web app which is MVC 5 using OWIN cookie authentication. It also uses AngularJS from which web api calls are made. The Angular code is only invoked after the user has successfully logged in and been authenticated and everything works as you would expect.
However, can someone please explain what component (Chrome, OWIN, IIS, ...) is responsible for eventually issuing a 401 if the user has been idle for 20 or so minutes. In MVC this is not a problem because the redirect automatically re-authenticates but with web api my only option is to get the user to log back in again.
Ok so thats the first thing, who is responsible for timing the session out and giving the 401 (and can i alter it) but ALSO is there any way to get web api calls to keep the session alive so that even if the user is idle the api calls stop it timing out?
Specifically, I have implemented SignalR in a way that the signal to the client results in the client issuing an api call to refresh its data. I realise i could push the refresh but at the moment thats a bit tricky. So whats happening is the updates are occuring nicely but eventually this results in a 401 which i want to avoid.
below is an ideal flow
server logs in => creates cookie sets expiration time, passes it to client
client saves cookies & passes it with each request
server checks the cookies for validity including expiration time
if invalid or expired, server responds with 401
the cookie expiration time can be configured in the CookieAuthenticationOptions. See file ~/App_Start/Startup.Auth.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
ExpireTimeSpan=TimeSpan.FromDays(365),
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
Set your ExpireTimeSpan to the desired TimeSpan.
For web api calls that fail authentication you could catch 401's and send back a response that tells the browser it's logged out - you could pop up a login dialog or redirect to login url.

GWT and AppEngine User Service

I am using GAE User Service to Authrnicate my GWT Application.
Depending on whether the User is logged in the User is presented with LoginPage/Dashboard.
The GWT Application calls a Auth Servlet (Window.Location.assign("/googleauth"); causing application to unload which then transfers control to Google Authentication Page, after authentication we are redirected to CallBack servlet.
I can check whether user is loggedin successfully in Callback Servlet. However if I simply redirect back to my application the session login is lost.
The Application loads from scratch.
If I set up a cookie-->
HttpSession session =
request.getSession();
String sessionid = session.getId(); //Get sessionID from
server's response to your login
request
Cookie cookie=new Cookie("sid",sessionid);
response.addCookie(cookie);
response.sendRedirect(AppURL.getApplicationBaseURL());
In my client code check -->
String sessionID =
Cookies.getCookie("sid");
if(sessionID!=null) { //show
dashboard }
Is the way I am using secure? How long are the cookies valid for?
You said:
I simply redirect back to my application the session login is lost.
This should not happen. Once you login the session should be there until you logout or session timeouts (you can set this in GAE settings).
You can simply make a GWT-RPC call to server and check if user is logged in: UserServiceFactory.getUserService().isUserLoggedIn().
Note: if you are looking for session cookies, AppEngine uses different cookie names in production and development servers. It uses ACSID cookie in production and dev_appserver_login.

Resources