I'm using Owin to host WebAPI Controllers. I have Owin middleware which performs authentication and sets the following if authentication fails:
context.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
When this happens I want to display a HTML page with some instructions to the user. (Like, "You need to log on.")
At the moment I'm just redirecting the user to a accessdenied.html-page, but I would prefer if the access denied was shown directly without the user being redirected (I don't want the Location field in the web browser to change).
I assume I could just generate the HTML on the fly and adding it to the response, for example by reading the HTML content from a resource.
My question is: Is it possible to do display a custom access-denied error page automatically using configuration? In "traditioinal" ASP.NET, it was possible to set up customErrors in web.config, but this does not appear to work with Owin selfhost:
<customErrors>
<error statusCode="401" redirect="~/accessdenied.html"/>
</customErrors>
In a previous project of mine I had to use an Owin middleware like this:
app.Use((owinContext, next) =>
{
return next().ContinueWith(x =>
{
if (owinContext.Response.StatusCode == 500 /*or 401 , etc*/)
{
//owinContext.Response.Redirect(VirtualPathUtility.ToAbsolute("~/Home/Error"));
//this should work for self-host as well
owinContext.Response.Redirect(owinContext.Request.Uri.AbsoluteUri.Replace(request.Uri.PathAndQuery, request.PathBase + "/Home/Error"));
}
});
});
you have to register the middleware before all the others.
In this case I'm redirecting the user to an Error view, but as general practice I would say it's better to have an HTML static page.
Actually I think there's an extension request for handling global exceptions.
Have a look at this link...
I came across the same issue. I tried setting StatusCode and then doing Redirect to 401 page. But Redirect changes StatusCode to 302.
I came up with solution of reading 401.html writing it to Response. It worked for me.
context.Response.StatusCode = 401;
var path = HttpContext.Current.Server.MapPath("/401.html");
var html = System.IO.File.ReadAllText(path, Encoding.UTF8);
context.Response.Write(html);
owin provides you an option to redirect to your error page
context.Response.Redirect(errUrl); //context is the owinContext
You don't need to have any special RedirectResult or RedirectToAction Method.
Related
This is essentially the same question as this one:
ASP.NET MVC - Preserve POST data after Authorize attribute login redirect except it isn't asked 7 years ago, and it's about ASP.NET Core, which is pretty different. I am using the [Authorize] attribute to do my most basic access authentication, really just to check to see if there is a user logged in at all. If not, then it kicks them back to the login page. Here's the services setup for that.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "loginId";
});
services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/Logout");
Here is my Logout action.
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Logout(string returnUrl = "/cgi-s/admin.cgi")
{
await HttpContext.SignOutAsync();
if (HttpContext.Request.Cookies.TryGetValue("loginId", out string loginId))
{
User auth = _context.Users.Where(a => a.LoginId.Equals(loginId)).FirstOrDefault();
if (auth != null)
{
auth.LoginId = string.Empty;
await _context.SaveChangesAsync();
}
}
return Redirect("/Account/Login?returnUrl="+returnUrl);
}
Right now I am just using the default behavior with a return url string to get back to the attempted page after a successful login. Is there a way to set this up so that POST data is also preserved?
I've tried a couple different things, like a custom middleware that stores post data which then gets retrieved on login, but I haven't come up with anything that I haven't found security holes in afterward.
Is there an easy way to do this that I'm just missing?
Thanks.
PS. Please ignore the weirdness going on in the Logout action. We are a two man team working on a 20 year old Perl CGI site, slowly transitioning over to .NET while trying to also keep up with new features and bug fixes, so everything is weird while we run Perl CGI alongside some .NET code on IIS with Postgres. Hopefully we will eventually get everything transitioned over.
Have you tried httpContext.Request.EnableBuffering?
See this question here and be sure to look at the comments: Read the body of a request as string inside middleware
httpContext.Request.EnableBuffering();
using(StreamReader streamReader = new StreamReader(httpContext.Request.Body,
...,leaveOpen: true))
{
//Do something here:
httpContext.Request.Body.Position = 0;
}
I have an Angular app that integrates with IdentityServer4 with implicit flow and the angular-oauth2-oidc library.
Everything seems to work fine, I can log in; and access token is available.
If I click the logout button, I handle it like:
logout() {
this.oauthService.logOut();
}
...and I'm redirected to Identity Server, where it asks me if I really want to log out.
My question is whether I can bypass that prompt? I mean, I want to log out completely if the button is clicked and redirected back to the Angular site, without the need to confirm it?
How can this be achieved?
EDIT: as mentioned in the other answers, it should work if you pass id_token_hint. So I did:
logout() {
this.oauthService.customQueryParams = {
'id_token_hint': this.oauthService.getIdToken()
};
this.oauthService.logOut();
}
But it doesn't make any difference.
There were two issues I needed to fix in order to make this work.
In IdentityServer, AccountOptions class, I had to set this property to true instead of false:
public static bool AutomaticRedirectAfterSignOut = true;
Next, In IdentityServer client configuration, I had to define the post logout redirect uri:
PostLogoutRedirectUris = new List<string> {"http://...."}
That did it. I did not have to change anything in the Angular client.
I registered a MessageHandler (with config.MessageHandlers.Add(new ValidationHandler()) which inherits from DelegatingHandler. It checks each Request for a security token and checks if it is valid.
I got 2 or 3 actionmethods in my Controller which should be accessabel without any authorization.
My Problem:
The MessageHandler is called first. So the actionmethod which should be accessabel from everywhere will be handled as a unauthorized request.
I'm not abel to change the code of the MessageHandler.
I tried to add the allowanonymous attribute, but i still get an unauthorized response.
I found this post Redirecting unauthorized controller in ASP.NET MVC . So my current idea would be to forward the user on the HandleUnauthorizedRequest to the proper action method. But I think it's not the best way.
Is there a better way for this? Is there a way to tell the web.config that actionmethod1 and actionmethod2 are allowed to be accessed as Unauthorizeded user?
[Edit]
Creating an UnAuthorizeAttribute with the AuthorizeAttribute which forwards the user still to the action methods doesn't work. The messagehandler "kills" the request with
statusCode = HttpStatusCode.Unauthorized;
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode));
So the UnAuthorizeAttribute will not be invoked. I'm using asp.net mvc webapi
You can try creating actionfilter to handle this as you know the request you are getting into the controller / action. This is just and idea.
http://www.codeproject.com/Articles/650240/A-Simple-Action-Filter-Overview
Hope this helps.
We have a "webapi" site built using servicestack and everything works great
Now browsing to the site brings up the ~/metadata page
Is there an option to show a custom stub page as in - pls visit the main site at www.abc.com
or better yet, redirect the browser to www.abc.com
In the EndpointHostConfig, you can set MetadataRedirectPath and DefaultRedirectPath. However, looking at the source code MetadataRedirectPath and DefaultRedirectPath both expect a relative url.
If you need more control or want to redirect to an outside site then you can intercept the entire request. So in your specific case you can do the following:
In AppHost configuration
SetConfig(new EndpointHostConfig
{
RawHttpHandlers =
{
httpReq => httpReq.PathInfo == "/metadata" ?
new RedirectHttpHandler { AbsoluteUrl = "http://www.abc.com" }
: null
},
});
Edit: Changed code to reflect mythz suggestion.
I'm having problem with getting ServiceStack [Authentication] attribute to work in ASP.Net MVC4 controller, pages / action methods with the attribute keep redirecting Users to the login page even after the login details are submitted correctly.
I've followed the SocialBootstrapApi example, with the difference being that all the authentication web service calls are made from the controllers:
this.CreateRestClient().Post<RegistrationResponse>("/register", model);
Other things that I've done so far:
Use my own user session implementation subclassing AuthUserSession (not too different from the example, but using my own implementation of User table)
Inherit ServiceStackController on my BaseController, overriding the default login URL
Enable Auth feature in AppHost with my user session implementation
Registration does work, user auth logic works (even though the session does not persist), and I can see the ss-id and ss-pid cookies in the request.
So my complete list of questions:
How do I make the [Authenticate] attribute work (or, what did I do wrong)?
How do I save and reuse the user session in an MVC controller? At the moment this.UserSession is always null.
How do I logout a user? this.CreateRestClient().Get<AuthResponse>("/auth/logout"); does not seem to work.
Update 1:
The session cookies (ss-id and ss-pid) gets created when I attempt to load the secured page (ones with [Authenticate] attribute), before any credentials get submitted. Is this the expected behaviour?
Update 2:
I can see that the session is saved in MemoryCacheClient, however trying to retrieve it in the base controller via this.Cache.Get<CustomUserSession>(SessionKey) returns null (where SessionKey is like: urn:iauthsession:1)
After much fiddling around, apparently the way to hook ServiceStack authentication is to call the AuthService via:
try {
authResponse = AuthService.Authenticate(new Auth{ UserName = model.UserName, Continue = returnUrl, Password = model.Password });
} catch (Exception ex) {
// Cut for brevity...
}
and NOT authResponse = this.CreateRestClient().Post<AuthResponse>("/auth/credentials", model);!
Where AuthService is defined in the base controller as:
public AuthService AuthService
{
get
{
var authService = ServiceStack.WebHost.Endpoints.AppHostBase.Instance.Container.Resolve<AuthService>();
authService.RequestContext = new HttpRequestContext(
System.Web.HttpContext.Current.Request.ToRequest(),
System.Web.HttpContext.Current.Response.ToResponse(),
null);
return authService;
}
}
Everything else (incl. session) works correctly now.
You can find how it could be done in the ServiceStack Use Cases repository. The following example is based on MVC4 but works perfectly for MVC3 either: CustomAuthenticationMvc.