Whats the best way to force a browser redirect after logout of ServiceStack - authentication

Currently when a user logs out the log out process works correctly but the user stays on the same screen and therefore can still see secure data.
What is the best practice for forcing a browser redirect after logging out of ServiceStack?

It's not possible for the server to enforce a client redirect. It's really up to the client to enforce the security for the data after you logout. If the client has been trusted with the secure data already, during the course of the session, then you need to trust that the client will secure it appropriately when the session ends.
While you can have ServiceStack send redirect headers to the client when logging out, there is nothing requiring the client to actually take that action.
If a JavaScript client makes an AJAX request to ServiceStack to logout, the redirect response doesn't affect the page displaying the secure data, because the AJAX request operates effectively in a separate scope from the page showing the data, and so that page remains unaffected by the redirect. So the redirect is useless, unless the client explicitly provides a mechanism to handle such event.
The client must take responsibility to navigate away from the secure data itself. The best practise would be:
In the success method of your call to the logout action, you should:
Dispose of any sensitive in memory data. i.e. JavaScript Variables / DOM Elements displaying the data.
Delete the session cookie
Redirect to login
If you have secured your service properly, then navigating back through the history should still trigger a session check, for which there will no longer be a valid session, and you should be redirected away.
You previously mentioned using AngularJS. If you were doing this with the $http service, then the success callback can be used, like this:
$http({method: 'POST', url: '/auth/logout', data: { provider: "logout" }}).success(
function(data, status, headers, config) {
$scope.someValue = null; // Remove sensitive values from the scope (though it should be cleared up anyway with the redirect to a different state)
$cookieStore.remove('ss-id'); // Remove the cookie
$state.transitionTo('login'); // Redirect to login state
}
);
This example assumes you have injected the $http, $cookieStore, $state providers
tl;dr
The client must enforce the security. The server redirect should be treated as nothing more than a suggestion to the client.
Use the success callback of the logout request action to delete the session cookie, dispose of any values in memory and redirect away from the data.
Hope this helps.

Related

Why do we need to use the front-channel for an OAuth authorization request?

I've been struggling with this and would love to see if any OAuth experts here have an answer.
For context, I'm trying to integrate OAuth into an existing first-party (internal) front-end client that lives on a subdomain. It's a single-page application. I have an authorization server that has an /oauth2/authorize and oauth2/token endpoint and I'm working with the OAuth 2 with PKCE authorization flow.
In all the examples I've seen externally, it seems like the recommendation is to make a top-level redirect to the authorization URL initial login . And for silently re-authenticating a user (if they were already logged in), using an invisible iFrame set to the authorization URL (and postMessaging the code back to the parent window).
I'm trying to understand what prevents me from making a front-channel request to my /authorize endpoint via Javascript. Something simple like...
const { state, code } = await fetch(authorizationUrl)
For the login case, I can handle a 403 error back from the AS and then redirect them to login from the client-side. For the re-authenticating case (i.e. client has an expired refresh token but is still logged in), this is great because I just get a 200 response and the code back directly in the JSON body and I can use it immediately. There is no top-level redirect, no hassle of saving app state, etc.
It seems like as long as the AS is willing to return the { state, code } via JSON, this should work. This means that
The AS authorize endpoint must be configured to allow CORS on select origins. This seems okay in a first-party context since I know which origins I should allow.
The AS must be sent client credentials (session cookies) with the request (otherwise the AS would have no idea how to determine if the user is logged in). In JS, this would be as simple as adding credentials: true. As long as the cookie credentials have Same-Site: None and the cookie is part of the same domain (cross-domain would not work since some browsers disable cross-site cookie sharing nowadays!)
I feel like I'm missing something crucial here. But at the same time, my prototype is working, so I'd love to get some input from experienced folks here.

How to logout all clients from Identity Server?

Identity Server and two clients (SSO): .Net Core MVC and Nodejs.
When I log in with Nodejs client, after refresh MVC (second client) I got logged MVC client. It's good.
But when I logout from Nodejs it send back-channel logout url to MVC client. Nodejs doesn't have problems with logout. But MVC client - after browser refresh it stay logged.
I read this and this posts but they didn't help.
When in MVC Startup i wrote this code:
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = (e) =>
{
e.Properties.IsPersistent = true;
e.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(2);
return Task.CompletedTask;
}
};
After two minutes I refresh browser and MVC redirect to Idrsv login page. Its good, but not safe (need to wait 2 minutes).
I read about userId claim cache but I doubt - if it will be a lot of active sessions, then cache will be very big and app will work slowly.
I can do with front-channel logout, but I read about cons, and now I doubt.
What do you prefer for logout all clients from Identity Server?
In the samples the logout is performed using iframes on the logged out page. If this page is skipped or aborted, clients may not be informed. But I don't think that's the case.
I'd rather prefer a safer backend logout that doesn't rely on iframes. Take a look at my answer here for an example.
Now about the client. Non-javascript clients do need a roundtrip to update the cookie. So the flow is: user logs out from client A. IdentityServer informs other clients (backchannel) and removes server cookie.
And now the (non-javascript) client has to take action. It also needs to remove the cookie, but that is only possible after the user performs an action.
And that's where the caching comes in. The cache only contains the alert from the server. On the first opportunity it removes the cookie and also removes the user from the cache. So the cache will in fact stay quite small. Do add some cleanup code to remove logged out users (that never returned) with cookies that are expired.

auth0 still auto-logs in seamlessly even after calling /logout url

Simple problem, I want to login and out of an app with various users to check different app functionality. App is using Auth0 for user management.
I am calling the /v2/logout url as a part of my flow.
But somehow, after logging out, when I login again the seamless SSO behavior runs and I'm immediately logged in again with no prompts -- it's as if the logout URL was never called.
Only way to get a login prompt again, is to clear my browser cache. Is there an auth0 cookie somewhere I need to delete as well? Or am I missing something? I'm reading the seamless SSO docs but don't see anything beyond calling /v2/logout.
Calling the Auth0 /v2/logout API endpoint will log the user out of Auth0 and optionally the IdP (if you specify federated parameter). It will not log out the user from your Application so you will need to implement that in your application.
Here in the Javascript SPA example, in the setSession() we are storing the Access token(along with its expiry) and the ID token in localStorage. In the logout() function we are then removing these entries. This is logging out from the Application user session. You can optionally redirect to /v2/logout to clear the Auth0 and IdP session as well in this function. That way, when you are checking if user is authenticated, the isAuthenticated() returns false and we force the user log in again.
So turns out, the issue is around redirecting the user as opposed to calling the logout url directly. I was using a separate ajax api call to the logout url. However when I use window.location.replace(logoutUrl), the logout actually happens.
From the auth0 docs:
To force a logout, redirect the user to the following URL:
https://YOUR_AUTH0_DOMAIN/v2/logout
Redirecting the user to this URL clears all single sign-on cookies set by Auth0 for the user.
So a separate call doesn't work -- have to redirect. Which I suppose makes sense -- a separate ajax call doesn't have the user session context.

JWT Refresh token and Multi-Page Application

I am going to implement JWT authentication for several independent services.
There will be auth.example.com and service1.example.com, service2.example.com etc.
My assumptions:
JWT can be kept in cookie for ".example.com"
JWT expire time should be small (like 15 mins) because there is no reliable way to logout user with JWT token (revoke token).
Refresh tokens should be used to reissue JWT tokens
Refresh token cookies should be accessible only by auth.example.com for security reasons and because https://www.rfc-editor.org/rfc/rfc6749#section-1.5 says
"Unlike access tokens, refresh tokens are intended for use only with authorization servers and are never sent to resource servers."
Next, if I have a service - multi page application (i.e. not SPA), where some URLs are called "traditional" way, not via Ajax and render HTML
based on some server side logic, which, of course, include checking of user authorization.
then, say, there will be an action service1.example.com/user/showpage
if (user.logged_in) {
render_some_html(get_some_data(user.login))
}
else {
render_anonimous_uses_page()
}
Problem is:
If site user close all site tabs and, then after hour or so, go directly to page /user/showpage (or maybe he
suspend laptop and wake it up in an hour and go to that page).
What if by that time JWT token will expire. Then to refresh it by Refresh token we need to make Ajax call to auth.example.com (because Refresh
token is stored only in auth.example.com cookie) and this is just unaccessible in server side rendering (that pseudocode that I posted above, it's server side, and it's just impossible to make client ajax call in the middle of execution of server code. it's just not applicable here). This way user will be considered logged out
on this stage.
Redirect could be one solution.. but what if site should work for anonymous out users too, and anyway looking for something better.
This problem not exists for SPA application, because before every Ajax call to internal API, it can check JWT and make call to refresh JWT token.
And question is: is this true that JWT in general should not (cannot) be used in Multi-Page (traditional) applications because of this issue? or there is good way to workaround this? or this is not a problem at all (users don't close tabs too often, or they expect site to log them out or redirect etc)?

Handle authentication in a Service Worker for a React App

I have a React app rendering client-side, in which I handle authentication the following way:
Upon loading, the app fires an AJAX request to the backend, basically asking whether the user's session is valid ;
It updates the app's state with the server's response ;
It renders the "/" route accordingly (the homepage if the session is invalid, a dashboard if it is valid).
(Maybe there are better solutions for handling this in front-end applications, I'm all ears if you have ideas)
This works pretty well, but introducing Service Workers into the mix and trying to turn the app into an offline-first progressive web app seems... complicated.
On the one hand, if I don't cache the "Am I logged in ?" request and the app is offline, the app will always render the homepage.
On the other hand, if I do cache the AJAX request, the users will eventually be shown an empty dashboard because their sessions will have expired and the server will be throwing 403s.
Is there a way to handle this effectively?
I solved my problem by taking a different approach: I now persist the state in localStorage.
This way, when the user arrives on the app, he is presented with stale data from his last visit. Meanwhile, a "Am I logged in?" request is fired in the background.
If it is succesful and returns true, the other AJAX requests get fired and fill the app with fresh data ;
If it is successful and returns false, the state is updated accordingly and the user redirected to the homepage ;
If the request is unsuccessful (i.e. the app is offline) the app keeps showing stale data from last session in the dashboard. We don't know if the user's session is still valid, but we can't retreive any data so it does not matter.
One way of doing it is adding a /verifyToken (assuming you are using some kind of token to validate the session) in your back-end api to check if the token is valid.
So you cache your session token. If the app is offline it shows the dashboard.
If the app is online, you fire a request to /verifyToken to check is the session is still valid. If it is then you continue to dashboard. If it isn't you redirect them back to homepage (or the sign in page).
Edit:
When your app is online, you can technically fire a request to any authorized route and check if the response was 403 (in case you can't modify the backend). If it is then you can send them back to sign in page.