I have Web API with OWIN Authentication in Web MVC.
I'm using <authentication>
in Web.Config for my Web MVC so it's redirecting to login page.
<authentication mode="Forms">
<forms name="WEB.AUTH" loginUrl="~/login" domain="" protection="All"
timeout="43200" path="/" requireSSL="false" slidingExpiration="true" />
</authentication>
I'm using [System.Web.Http.Authorize]
attribute to authorize my Web API. But somehow, the API redirecting to login page same like my MVC app because of above configuration.
what I want to do is keep redirecting function for the Web MVC but returning 401 for Web API. How can I achieve this? should I create a custom authorization attribute for Web API?
--EDIT--
I found the answer from this post SuppressDefaultHostAuthentication in WebApi.Owin also suppressing authentication outside webapi
So I just add a few lines into my Startup.cs
. I had all my controllers configured with a "api" prefix route.
HttpConfiguration config = new HttpConfiguration();
//..some OWIN configuration
app.Map("/api", inner =>
{
inner.UseWebApi(config);
});
make sure you put app.Map()
after Web Api Configuration lines. Otherwise, it will give error to MVC application.
Create a custom AuthorizeAttribute
:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Unauthorized");
}
}
If you in the future skip the web.config stuff and use owin to setup your authentication, you could in your Startup.cs
do:
var provider = new CookieAuthenticationProvider();
var originalHandler = provider.OnApplyRedirect;
provider.OnApplyRedirect = context =>
{
if (!context.Request.Uri.LocalPath.StartsWith(VirtualPathUtility.ToAbsolute("~/api")))
{
context.RedirectUri = new Uri(context.RedirectUri).PathAndQuery;
originalHandler.Invoke(context);
}
};
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName = FormsAuthentication.FormsCookieName,
LoginPath = new PathString("/Account/LogOn"),
ExpireTimeSpan = TimeSpan.FromMinutes(240),
Provider = provider
});
In .NET Core I have solved it like this, Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.Name = "AuthCookie";
options.Events.OnRedirectToAccessDenied = UnAuthorizedResponse;
options.Events.OnRedirectToLogin = UnAuthorizedResponse;
})
....
}
internal static Task UnAuthorizedResponse(RedirectContext<CookieAuthenticationOptions> context)
{
context.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
return Task.CompletedTask;
}
This is what worked for me.
Creating a custom attribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class NoRedirectAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
}
}
Using the attribute in your controller:
[HttpDelete]
[NoRedirectAuthorizeAttribute(Roles = "Admin")]
[Route("api/v3/thingstodelete/{id=id}")]
public IHttpActionResult DeleteThingToDelete(Guid id)
{
//delete code
}
Here are just overriding the HandleUnauthorizedRequest method of the AuthorizeAttribute. So, instead of sending a redirect (304) to the login page, we send Forbidden(403) HTTP status code.
I struggled with this issue and I came up with a way to only do the redirect if I didn't find the token that I use in the header for my custom manual authorization of my WebApi. This is my setup (notice the Provider object and OnApplyRedirect action)
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CookieAuthenticationProvider
{
OnApplyRedirect = (ctx) => {
var token = HttpContext.Current.Request.Headers.Get("X-User-Token");
if (token == null) ctx.Response.Redirect(ctx.RedirectUri);
}
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With