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

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.

Related

Azure app Service authentication token not refreshing after calling .\auth\refresh endpoint

I have an angular app hosted in one azure windows app service and a asp.net core api hosted in another, both are protected using the app service authentication feature. I am using an app service plan which currently has two servers. I am using the token store and it is using the file system to store the tokens.
When the angular app calls the api it needs to pass an access token in the authorization header. The access token is retrieved by performing a GET on the \.auth\me endpoint and sending the AppServiceAuthSession cookie as the credential. The returned token is then cached in session storage and used for subsequent requests until the token expires. When the token expires I call the \.auth\refresh endpoint (and send the AppServiceAuthSession cookie) and then call the \.auth\me to get the refreshed token.
All this works well when running on one server but when the app service plan is scaled to 2 or more servers the call to \.auth\refresh succeeds but the subsequent call to the .auth\me endpoint gets a token which has already expired. I can tell the token has expired by checking the internal exp claim and also the call to the api fails with a 401 Unauthorized when it would normally succeed.
If I scale back to one server the problem goes away and if I enable ARR affinity the problem goes away but I don't want to have to enable affinity just to resolve this.
also the call to the api fails with a 401 Unauthorized when it would normally succeed.
You shouldn't be calling /.auth/refresh on the API app. You must call /.auth/refresh on the WEB app because the auth token is produced on the WEB app.
Alternative to the bearer token is the AppServiceAuthSession cookie from your client request to /.auth/refresh.
The returned token is then cached in session storage and used for subsequent requests until the token expires. When the token expires I call the .auth\refresh endpoint (and send the AppServiceAuthSession cookie) and then call the .auth\me to get the refreshed token.
We have a user-facing web application that calls an API backend, which in turn queries the MSFT graph, just to be sure we have the context correctly captured. At each phase, we anticipate a distinct AAD registration:
Web app registration (with permission to API app)
API app registration (with permission to Microsoft Graph)
Our initial assumption is that the user hasn't explicitly given their agreement to using the Microsoft Graph because this looked to be tied to adding the graph permission. Since the user isn't involved if you're merely updating the existing token, there isn't a chance for that consent to be obtained. The user would need to re-login with an explicit consent prompt, which could be done by calling /.auth/login/aad?prompt=consent. But we wouldn't anticipate that to cause the expiration problem. I'm speculating here, but it's possible that a cached, outdated value is being utilised in place of the permission error. There would need to be input from Connor.
The logs from the actual Authentication / Authorization layer itself, particularly if there is an error message, are another piece of information that would be useful to have. To obtain these logs:
From your app in the portal (the API app is what I'm most interested in): “Platform features” > “Diagnostic logs”. Change “Application Logging (Filesystem)” to “On” and set the level to “verbose.” Hit Save.
Back to “Platform features” > “Log stream” and keep that window open as you reproduce the issue (in this case, the call to the API app)
Actually, the issue is that AAD bearer tokens are incompatible with the /.auth/refresh API. Instead, you must use a session token (_x-zumo-auth_ header) or a session cookie (_AppServiceAuthSession_ cookie), which are the results of a login activity.
For more info - see here #refreshing-the-access-tokens

How safe is it to save session locally in AngularJS?

So this is my structure:
HTML form sends authentication to nodejs.
Authenticate using passportjs > res.send the userid with jwt-simple (json web token).
The received info is saved in $localStorage.user. I use that info in any of the controllers needed and include it and send my post/get requests to nodejs.
I decode the info in nodejs and query the DB.
Is this safe? Is this how it works in real world?
Many thanks.
#Somename:
The workflow which you have mentioned is slightly correct.
The ideal way to get passport authentication done is,
User log's in entering his username and passport.
Send a post request with these form data.
Authenticate the credentials using Passport. Using the passport.authenticate will invoke the serializeUser and get you the valid user if it exists. Else we return a login error response.
A Successful login will automatically create a session in back end, save it in the sessionStorage and adds it with the response.
This cookie will be saved automatically into browser's local storage once the response is fetched at client side.
Every time we send a subsequent API request we need to include this cookie in the req headers.
This cookie should be validated each time in back end. passport.authorize will again make use of the cookie and check if the session is valid.
Logout session once the User logs out.
Hope I've made things clear for you.

angularjs client and spring backend user login and session management

I have a mobile website written in angularjs, with my backend in Spring Boot. Right now, I have my own login page and can login a user without any trouble. However, if the user ever clicks "back", "refresh", etc., the client loses the user's id and login info (obtained from server on login). I need to make sure that this info is maintained and clicking "back" or "refresh" doesn't break everything.
Secondly, a user that knows the url's after login can type those url's in the browser and access them without logging in. I can stop them accessing anything on the server, but not sure what I can do on the client to redirect them to a login page in this case.
Any help would be greatly appreciated!
You should keep in mind that everything running in browser is stateless, there's no way to keep trace of the previous state.
Right now, if the user performs a refresh (or another similar action), Angular loses everything (AuthData included).
You have many way to work around that limit:
Perform an http request after the application bootstrap (have a look at the angular.module().run method
Save a cookie and use the server to print initial data layer directly on the dom via json
Save on local/session storage
Personally, I prefer cookies because that lets the server to work decoupled from the client.
In reference to your comment..."if the user ever clicks "back", "refresh", etc., the client loses the user's id and login info (obtained from server on login)."
Is there any reason you need to maintain the user id or login info after a successful authentication?
If Spring Security is setup for basic authentication, after a successful login, a Session Cookie will be sent back on the response to the client. On all subsequent requests to the server, the same Session Cookie will be sent on the request and the previously authenticated session will be re-established. You just need to ensure that your Angular client is passing cookies when issuing requests.
Take a look at this sample on how this is done.

reset user login session in angularjs

I store user auth info and token in a cookie locally for my angular/typescript SPA. Cookie expires after a certain time I have set. How do I reset expiration while user's activity? (session on the server is reset on users requests)
I mean what would be the best way, so I don't code some stupid stuff.
Thank you
I assume you do your authentication and generation of the cookie on the server and your angular side authentication is located in a service.
One way to achieve what you are looking for is to create a method in your client side service to send a request to a known end point of the server which would refresh the cookie. This server endpoint will refresh the cookie and will return a HTTP 200 code.
Once you have this code in place, you can call it when the user interact with the application (ex navigation across views via $locationChangeStart, $routeChangeStart etc).

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