How to clear apache shiro session? - grails

I used apache shiro session for the authentication and authorization.
Am able to login with different user and permission and roles but actual problem is whenever i call a signOut function looks like shiro session is not getting wiped off.
The evident for this is whenever i clicked logout it comes main screen and if i use browser back button i can go back for the last screen.
My signOut function looks like this
// Log the user out of the application.
SecurityUtils.subject?.logout()
webRequest.getCurrentRequest().session = null
session.invalidate()
// For now, redirect back to the home page.
redirect(uri: "/")
Any help on this really appreciated struggling for this from past 2 days

This works for me with version version 1.1.4 of the shiro plugin.
def logOut() {
SecurityUtils.subject?.logout()
redirect(uri: "/")
}

This is due to browser cache. You can configure to reset your browser cache in ShiroSecurityFilters file.
class ShiroSecurityFilters {
def filters = {
requestHeadersFilter(controller: '*', action: '*') {
after = {
response.setHeader("Pragma", "no-cache")
response.setDateHeader("Expires", 0)
response.setHeader("Cache-Control", "no-cache")
response.addHeader("Cache-Control", "no-store")
}
}

Related

OWIN authentication middleware: logging off

OWIN beginner here. Please be patient...
I'm trying to build an OWIN authentication middleware which uses form posts to communicate with my external authentication provider. I've had some success with getting the authentication bits working. In other words, I can:
communicate with the remote provider through form post;
process the response returned by the remove provider
If everything is ok, I'm able to signal the default authentication provider
THis in turn gets picked up by the cookie middleware which ends up generating the authentication cookie
So far, so good. Now, what I'd like to know is how to handle a log off request. Currently, the controller will simply get the default authentication manager from the owin context and call its SingOut method. This does in fact end my current session (by removing the cookie), but it really does nothing to the existing "external" session.
So, here are my questions:
1. Is the authentication middleware also responsible for performing log off requests?
2. If that is the case, then can someone point me to some docs/examples of how it's done? I've found some links online which describe the logging in part, but haven't found anything about the log off process...
Thanks.
Luis
After some digging, I've managed to get everything working. I'll write a few tips that might help someone with similar problems in the future...
Regarding the first question, the answer is yes, you can delegate the logoff to the middleware. If you decide to do that, then your middleware handler should override the ApplyResponseGrantAsync method and check if there's a current revoke request. Here's some code that helps to illustrate the principle:
protected override async Task ApplyResponseGrantAsync() {
var revoke = Helper.LookupSignOut(Options.AuthenticationType,
Options.AuthenticationMode);
var shouldEndExternalSession = revoke != null;
if (!shouldEndExternalSession) {
return;
}
//more code here...
}
After checking if there's a revoke request, and if your external authentication provider is able to end the response through a redirect, then you can simply call the Response.Redirect method (don't forget to check for the existance of redirect - ex.: if you're using asp.net identity and MVC's automatically generated code, then the sign out will redirect you to the home page of your site).
In my scenario, things were a little more complicated because communication with my authentication provider was based of form posts (SAML2 messages with HTTP Post binding). I've started by trying to use Response.Write to inject the HTML with the autopostback form into the output buffer:
protected override async Task ApplyResponseGrantAsync() {
//previous code + setup removed
var htmlForm = BuildAndSubmitFormWithLogoutData(url,
Options.UrlInicioSessaoAutenticacaoGov);
Response.StatusCode = 200;
Response.ContentType = "text/html";
await Response.WriteAsync(htmlForm);
}
Unfortunately, it simply didn't work out. Not sure on why, but the browser insisted in redirecting the page to the URL defined by the Logoff's controller method (which was redirecting the page to its home page or '/'). I've even tried to remove the location HTTP header from within the ApplyResponseGrantAsync method, but it still ended up redirecting the user to the home page (instead of loading the predefined HTML I was writing).
I've ended up changing the redirect so that it gets handled by my middleware. Here's the final code I've ended up with in the ApplyResponseGrant method:
protected override async Task ApplyResponseGrantAsync() {
//previous code + setup removed
//setup urls for callbabk and correlation ids
var url = ...; //internal cb url that gets handled by this middleware
Response.Redirect(url);
}
This redirect forced me to change the InvokeAsync implementation so that it is now responsible for:
Checking for a new authentication session
Checking for the end of an existing authentication session (handle the logoff response from the external provider)
Checking if it should generate a new form post html message that ends the current session controlled by the external provider
Here's some pseudo code that tries to illustrate this:
public override async Task<bool> InvokeAsync() {
if (Options.InternalUrlForNewSession.HasValue &&
Options.InternalUrlForNewSession == Request.Path) {
return await HandleLoginReply(); /login response
}
if (Options.InternalUrlExternalSessionEnded.HasValue &&
Options.InternalUrlExternalSessionEnded == Request.Path) {
return await HandleLogoffReply();//logoff response
}
if (Options.InternalUrlForEndingSession.HasValue &&
Options.InternalUrlForEndingSession == Request.Path) {
return await HandleStartLogoutRequest(); //start logoff request
}
return false;
}
Yes, in the end, I've ended with an extra request, which IMO shouldn't be needed. Again, I might have missed something. If someone manages to get the ApplyResponseGrantAsync to return the auto submit post (instead of the redirect), please let me know how.
Thanks.

Redirect to login page after session timeout grails

I am using grails 2.3.4 and spring security core 2.0 RC2 and spring security ui 1.0 RC1. Every thing is working fine but when ever there is session time out I get following error "Error in grail layout main" because I have called session variable in my layout's main.gsp file.Now I want to redirect to login page after every session timeout and dont show the error page.To redirect after session time out i have done it in bootstrap.groovy file as
def structureMap1 = Requestmap.findByUrl("/institution/index") ?: new Requestmap(url: "/institution/index",configAttribute: "ROLE_INSTITUTION").save(failOnError:true)
but there are so many pages so it is difficult to write for every page . Is there any other method to do it please help.
How about using SecurityFilters you can place it in your conf folder:
class SecurityFilters {
def filters = {
catchRememberMeCookie(url: "/**") {
before = {
if (!session.user) {
def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()
def confirmurl= g.createLink(controller: controllerName, action: actionName, params:params, absolute: 'true' )
session.lastURL=request.getHeader('referer') ?: confirmurl
redirect(controller:'auth',action:'denied')
return false
}
}
}
This segment is an example and will not answer your question at hand since there is insufficient information to give a detailed answer but with a security filter - this sits above all your requests and if you need it to redirect to another location based on a specific value etc then you can so if no session.user do something else which will then kick in for all your actions

URL encoding # sharp to Durandal SPA

I know it is not necessary to specify a login URL to a Durandal login page. But I wonder how to fix the following problem I'm facing to redirect a Authentication to a specific Durandal Page that has a Sharp sign (i.e. #).
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
//LoginPath = new PathString("/" + HttpUtility.UrlDecode("#") + "/login")
LoginPath = new PathString("/#/login")
});
...
}
When I paste:
http://localhost/#/login
to a browser URL I can navigate to the login page without any problems. I can login and it is working fine.
Because I'm mixing MVC with SPA in some scenarios, when I add [Authorize] attribute to an MVC controller, then I will be redirected as expected to
http://localhost/%23/login?ReturnUrl=%2Fe
and I get the error:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /#/login
how to use the # sign instead of %23 char encoding? or maybe I'm messing something else!!!
FYI:
My question is related to character encoding not something line this: Login page on different domain because I may face the same problem in other situations in the future.
As far as I know it isn't possible to use a # from the server. A possible solution could be to redirect the user from the shell in durandal.js when the user isn't authenticated.
I don't know if it is too late in the life cycle but you could try to catch the redirect in the global asax file in the Application_EndRequest event and fix the response. I have code like this to change the actionresult to a jsonresult that my js can work with so that when a user attempts to reach an MVC controller resource that I have protected via java script ajax I can handle it properly.
protected void Application_EndRequest()
{
var context = new HttpContextWrapper(this.Context);
// If we're an ajax request and forms authentication caused a 302,
// then we actually need to do a 401
if (FormsAuthentication.IsEnabled && context.Response.StatusCode == 302
&& context.Request.IsAjaxRequest())
{
var jsRet = new JSONFunctionResult<Boolean>();
if (this.Request.IsAuthenticated)
{
jsRet.Messages.Add(new JSONFunctionResultMessage()
{
Text = string.Format("You must have one of these roles to perform the operation: {0}", context.Response.Headers["RolesRequired"]),
Title = "Security Violation",
Type = (int)JSONFunctionResultMessageTypes.authorization
});
}
else
{
jsRet.Messages.Add(new JSONFunctionResultMessage()
{
Text = "You must be logged into to access this resource",
Title = "Security Violation",
Type = (int)JSONFunctionResultMessageTypes.authentication
});
}
jsRet.OperationStatus = JSONFunctionResultOperationStatus.error.ToString();
string jresponse = JsonConvert.SerializeObject(jsRet);
context.Response.Clear();
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
context.Response.Write(jresponse);
}
}
In my code I check for a redirect and if forms auth is enabled and if the user is Authenticated and change the response accordingly. For your code you could maybe change the response to go to your correct login page?

How can I ask for credentials after Spring Security ACL has thrown an AccessDeniedException?

I'm using Grails 2.2.3 and the Spring Security ACL plugin 1.1.1, and I'd like to have a URL that is open to the public and the service layer using the #PostAuthorize annotation secures the resource. We're doing it this way because to determine whether a user has access to a particular object we need to look at the object first.
What I'd like to be able to do is in the controller layer catch the AccessDeniedException, then have the browser ask for credentials and try again. I've tried the naive approach of setting the response status to 401 and redirecting back to itself to try again. The problem I ran into is that the browser never asked for credentials.
To put this into code what I'd like to do is in the controller layer:
#Secured(['IS_AUTHENTICATED_ANONYMOUSLY'])
def controllerAction() {
try {
someService.action()
} catch (AccessDeniedException ade) {
// if logged in show FORBIDDEN, if not ask for credentials and try again
}
}
And the service layer would simply have:
#PostAuthorize("""returnObject.availability == 'ALL'""")
def action() {
PersistedObject.findById(1)
}
Thanks for any help!
I ended up solving the problem, and it turns out I was missing a header that I needed to send along with the 401 status code.
To correct what I have above what you want to do is:
#Secured(['IS_AUTHENTICATED_ANONYMOUSLY'])
def controllerAction() {
try {
someService.action()
} catch (AccessDeniedException ade) {
if (!springSecurityService.isLoggedIn()) {
response.setHeader 'WWW-Authenticate', 'Basic realm="Grails Realm"' // the missing line
response.sendError HttpServletResponse.SC_UNAUTHORIZED
}
}
The "redirect" I was looking for happens automatically within the browser after credentials are submitted. I was mistaken about needing something specific for the redirect.

Grails Spring Security plugin: logout not working

I just add the spring-security-plugin to my grails project. everything looks working fine. but when I try to logout the app shows me the logout message, however the application is still logged-in!
My Config files is the following:
// Added by the Spring Security Core plugin:
grails.plugins.springsecurity.useBasicAuth = true
grails.plugins.springsecurity.userLookup.userDomainClassName = 'malibu.server.User'
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'malibu.server.UserRole'
grails.plugins.springsecurity.authority.className = 'malibu.server.Role'
cheers
Since you're using Basic auth, your browser must be caching your credentials and logging you back in.
Just session.invalidate() before redirect.
class LogoutController {
/**
* Index action. Redirects to the Spring security logout uri.
*/
def index = {
session.invalidate()
redirect [whatever]
}
}

Resources