Answered by:
OWIN Mvc5 + Web Api

Question
-
User280723817 posted
Hi
I have a question about getting both mvc & web api working correctly with ADFS. Looks like I have post the question in the wrong place.
http://forums.asp.net/t/2079851.aspx?OWIN+Mvc5+WebApi
Hopefully more security experts are watching this category.
Any help would be appreciated.
Thanks
Ras
Thursday, December 10, 2015 9:50 PM
Answers
-
User280723817 posted
Ok. I got further. I was able to check the "/api" path before redirecting to the identity provider like this:
app.UseWsFederationAuthentication( new WsFederationAuthenticationOptions { Wtrealm = realm, MetadataAddress = adfsMetadata, Notifications = new WsFederationAuthenticationNotifications { RedirectToIdentityProvider = notification => { if (notification.OwinContext.Request.Path.StartsWithSegments(new PathString("/api"))) { notification.HandleResponse(); } return Task.FromResult(0); } } });
Is this an acceptable solution, security-wise? Seems a bit hacky with the hard-coded path but I'm more concerned about the implications of .HandleResponse() if that could do something bad in terms of security.
Thanks!
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, December 11, 2015 6:24 PM
All replies
-
User-2057865890 posted
Hi,Ras
Welcome to asp.net forum.
An authentication filter validates access tokens, and the [Authorize] attribute is used to protect a resource.
When a controller or action has the [Authorize] attribute, all requests to that controller or action must be authenticated.
Otherwise, authorization is denied, and Web API returns a 401 (Unauthorized) error.
// If we already have a bearer token, set the Authorization header. var token = sessionStorage.getItem(tokenKey); var headers = {}; if (token) { headers.Authorization = 'Bearer ' + token; } $.ajax({ type: 'GET', url: 'api/values/1', headers: headers }).done(function (data) { self.result(data); }).fail(showError);
More information,please see
Secure a Web API with Individual Accounts and Local Login in ASP.NET Web API 2.2
Best regards,
Chris Zhao
Friday, December 11, 2015 1:50 AM -
User280723817 posted
Hi Chris, Thanks for the response. That part is all good for me. To explain my scenario better, I created a sample app:
1. In VS2015 I created a new ASP.NET Web Application (4.5.2) and chose MVC with Web API
2. For Authentication, I picked Work And School Accounts and used On-Premises option with ADFS metadata and App ID.
3. In Startup.Auth it already had app.UseWsFederationAuthentication and I added app.UseActiveDirectoryFederationServicesBearerAuthentication() for the API. So my code looks like this:
public partial class Startup { private static string realm = ConfigurationManager.AppSettings["ida:Wtrealm"]; private static string adfsMetadata = ConfigurationManager.AppSettings["ida:ADFSMetadata"]; public void ConfigureAuth(IAppBuilder app) { app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); app.UseCookieAuthentication(new CookieAuthenticationOptions()); app.UseWsFederationAuthentication( new WsFederationAuthenticationOptions { Wtrealm = realm, MetadataAddress = adfsMetadata }); var options = new ActiveDirectoryFederationServicesBearerAuthenticationOptions { MetadataEndpoint = adfsMetadata, TokenValidationParameters = new TokenValidationParameters() { ValidAudience = realm }, }; app.UseActiveDirectoryFederationServicesBearerAuthentication(options); } }
4. My MVC HomeController has MVC AuthorizeAttribute
[Authorize] public class HomeController : Controller { public ActionResult Index() { return View(); } }
5. Then I added an API Controller and added protected it with the Http Authorize attribute.
[Authorize] public class ValuesController : ApiController { // GET: api/Values public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } }
6. I run my MVC home page and I get authenticated correctly by ADFS and that's good.
7. Then I generate a Bearer token and call the API and I can get values back. That's also good. I get ["value1","value2"] back.
HttpClient client = new HttpClient(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44329/api/values"); request.Headers.TryAddWithoutValidation("Authorization", access.Token); HttpResponseMessage response = client.SendAsync(request).Result; string responseString = response.Content.ReadAsStringAsync().Result; Console.WriteLine(responseString);
8. Now I add a role to the API Authorize attribute for a scenario where caller does not have access:
[Authorize(Roles = "user-does-not-have-this-role")] public class ValuesController : ApiController { // GET: api/Values public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } }
9. This is where the issue is. Now the client code (Step #7) is returning an HTML content back which seems like an error page from the ADFS saying you don't have JavaScript. The issue is that when the Web API threw a 401, the MVC part of the OWIN Auth logic kicked in and redirected the call to ADFS instead of returning a 401 like it should.
10. Now if I comment out the 'app.UseWsFederationAuthentication' part, I correctly get the 401 Unauthorized message for the API client (step 7)
{"Message":"Authorization has been denied for this request."}
I want to use both authentication options but I want to make it so that the passive authentication for MVC does not mess with the API 401s.
How can I do that?
Friday, December 11, 2015 4:21 AM -
User280723817 posted
I guess another way to look at this, I want the Federation authentication middleware for everything EXCEPT /api/ path. And the Bearer token middleware ONLY for /api/ path. Maybe there's a different way to handle this? Anyone else had to handle this same situation?
Thanks,
Ras
Friday, December 11, 2015 5:30 PM -
User280723817 posted
Ok. I got further. I was able to check the "/api" path before redirecting to the identity provider like this:
app.UseWsFederationAuthentication( new WsFederationAuthenticationOptions { Wtrealm = realm, MetadataAddress = adfsMetadata, Notifications = new WsFederationAuthenticationNotifications { RedirectToIdentityProvider = notification => { if (notification.OwinContext.Request.Path.StartsWithSegments(new PathString("/api"))) { notification.HandleResponse(); } return Task.FromResult(0); } } });
Is this an acceptable solution, security-wise? Seems a bit hacky with the hard-coded path but I'm more concerned about the implications of .HandleResponse() if that could do something bad in terms of security.
Thanks!
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, December 11, 2015 6:24 PM