I have a Web Api and Mvc 5 on same project.
That Web Api is protected with bearer token (but I commented the SuppressDefaultHostAuthentication line, so I can access the api from browser when I am authenticated with cookie mvc)
Now I´m trying to access the api from a mvc controller without sending the token, is that possible with SuppressDefaultHostAuthentication off?
Tried that without success (401 error):
HttpClientHandler handler = new HttpClientHandler()
{
PreAuthenticate = true,
UseDefaultCredentials = true
};
using (var client = new HttpClient(handler))
{
client.BaseAddress = new Uri("http://localhost:11374/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync("api/MyApi").Result;
if (response.IsSuccessStatusCode)
{ }
}
If its not possible, how is the best way to handle that problem?
WebApi adheres to REST, which among other things, dictates that requests are stateless. That means with WebApi, or any REST-compatible API, there's no concept of anything such as cookies, sessions, etc. Each request to the API must contain all information needed to service the request. Therefore, if you have an endpoint that requires authentication, you must authenticate the request to access it. Period. If you're doing auth via bearer tokens, then you must pass the bearer token.
Since the WebAPI and the MVC app are in the same project you don't need to go through HTTP and make a request in order to access a method of each one - they're neighbors :)
You can treat the WebAPI as an ordinary class and instantiate it in the MVC controller. Afterwards you call the methods on the instance as you do with any other object in your application.
However it isn't possible to avoid tokens and/or other security mechanisms the WebAPI is designed with IF you leverage a request through HTTP to access it.
Related
I have searched lots of posts here regarding my problem but have not found anything related to what I am trying to achieve.
I have created a Web API micro service for handling user authentication. The service uses identity framework and issues bearer tokens. I have another Web API which is secured using the [Authroize] attribute on the controllers. Both services talk to the same database via Entity Framework.
One hurdle I had to overcome whilst setting up this infrastructure was both services require the same machine key in their web.configs. When everything is deployed it works great.
However, I am trying to write some integration tests for my main API using the Microsoft Owin TestServer. This allows me to write integration tests against an in memory copy of the API which also uses an in memory copy of the database using the entity framework migrations.
I have successfully created integration tests for my user service. However, I am struggling to get integration tests working for my main service. I have created a solution with a new test project in it. I have also linked in my user service and main service. In the bootstrap for the integration tests, I spin up an in memory copy of each service by using each services Startup.cs.
Everything works ok until I try to hit a controller that is secured with the [Authorize] attribute. I have successfully hit the user service to register a user, activate that user and obtain the bearer token. I am then passing this over in the request to the main service in the auth header. However, I always an Unauthorized response from the service.
I suspect this is related to machine keys again and have tried putting the same machine key into an App.config within the integration test project. This did not make any difference.
If anyone has managed to get this scenario working I would very much appreciate any advice on what could be causing the problem. I haven't pasted any code as there is quite a lot involved but would be happy to paste any specifics.
Testing in this way is super fast and really powerful. However, there seems to be a lack of info out there on getting this working.
EDIT
Here is some code as requested. The two in memory APIs are created in a OneTimeSetUp as follows:
_userApi = TestServer.Create<UserApi.Startup>();
_webApi = TestServer.Create<WebApi.Startup>();
The Authorized HttpClient is then taken from the API as follows:
var client = _webApi.HttpClient;
var authorizationValue = new AuthenticationHeaderValue("Bearer", token);
client.DefaultRequestHeaders.Authorization = authorizationValue;
"token" is obtained from the user service.
The GET is then performed using the client as follows:
var result = await client.GetAsync(uri);
EDIT 2
I should also point out that I have authenticated integration tests working within the User Service. The problem is only when I try to use both services together. E.g. Obtain token from User Service and use that token to authenticate against my main service. The in memory entity framework database is created as follows:
AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(TestContext.CurrentContext.TestDirectory, string.Empty));
using (new ContractManagerDatabase())
{
System.Data.Entity.Database.SetInitializer(new DropCreateDatabaseAlways<ContractManagerDatabase>());
}
So after days of battling with this I have finally found a solution. The first thing I did was to move the integration tests back into the main Web API. Having taken a step back and thinking more about the problem I realised that by creating a separate solution and spinning up my user service and Web API in memory all I was really testing was Identity Framework and not the API itself.
I have integration tests working just fine in my user service for both unauthenticated and authenticated calls. It seemed to me that I should be able to do the same with the Web API and not rely on the user service.
I thought that I must be able to "bypass" the [Authorize] attribute to test the API. After some google searching, I found the following article which was most valuable in getting the solution working:
https://blogs.taiga.nl/martijn/2016/03/10/asp-net-web-api-owin-authenticated-integration-tests-without-authorization-server/
The key things for me were as follows:
The http client needed to be constructed differently for the test.
var client = new HttpClient(_webApp.Handler) {BaseAddress = new Uri("http://localhost")};
The call to GET had to be constructed differently for the test.
token = token == string.Empty ? GenerateToken(userName, userId) : token;
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Authorization", "Bearer " + token);
var client = GetAuthenticatedClient(token);
return await client.SendAsync(request);
The token needs to be generated as follows. I also needed to add a claim for the User Id as this is taken from the request within the controller.
private static string GenerateToken(string userName, int userId)
{
var claims = new[]
{
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.NameIdentifier, userId.ToString())
};
var identity = new ClaimsIdentity(claims, "Test");
var properties = new AuthenticationProperties { ExpiresUtc = DateTime.UtcNow.AddHours(1) };
var ticket = new AuthenticationTicket(identity, properties);
var format = new TicketDataFormat(_dataProtector);
var token = format.Protect(ticket);
return token;
}
I hope this post helps others with similar issues and thanks to everyone who contributed.
I have MVC front end application using WebApi 2 application for authentication and authorization. I am using JWT tokens for the same. So far I have been able to successfully authenticate and receive back a token… I can further access the restricted resource ([Authorize] attribute) by adding an Authorization token to the header using POSTMAN tool.
Authorization: “Bearer <jwt.token.string>”
Issue is, I am not able to intercept the call in MVC pipeline to add the token to the httpHeader. It always routes me back to the login page. Not the case when I use the POSTMAN tool. I have unsuccessfully tried injecting the token at following points:
Extending Authorize attribute with custom implementation
Adding a custom ActionFilterAttribute
Adding custom DelegatingHandler
Owin pipeline using StageMarker (PipelineStageAuthenticate) in Startup.cs
In all above cases I am hitting the event because I can debug. I have strong suspicion that I am hitting the authorization point before I set my header but I can’t figure out sequence of flow to properly intercept the HttpContext object and inject the Authorization header.
After successful authentication, add
var ctx = Request.GetOwinContext();
var authenticateResult = await ctx.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalBearer);
ctx.Authentication.SignOut(DefaultAuthenticationTypes.ExternalBearer);
var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
ctx.Authentication.SignIn(applicationCookieIdentity);
This will create a signed cookie and your Authorize attribute will automatically read the cookie. All your requests will become authorized subsequently.
In my client side (controller of MVC application) I am using the code below to set cookie value:
HttpCookie cookie = new HttpCookie("TestCookie");
cookie.value = 'Test';
HttpContext.Request.Cookies.Add(cookie);
I am also setting the cookie value in request header. This is done when I am configuring breeze entitymanager. I use breeze queries to get data from web api.
'Cookie': UserProfileID = config.getCookies('UserProfileID')
But in Web API, I always find that there are no cookies present in request header.
request.Headers.GetCookies("UserProfileID").FirstOrDefault()
To set a cookie, you need to add it to the Response object, not the Request.
var cookie = new HttpCookie("TestCookie");
cookie.Value = "Test";
HttpContext.Response.Cookies.Add(cookie);
Upon more research, I found this question. The answer provides some insight about the nature of Web API:
There's not a whole lot to work with here, but generally speaking, Web API diverges from MVC mostly in that it's fully REST-compliant, whereas MVC is not. REST-compliant applications are stateless (in other words: no session, no cookies, etc.). Everything the API endpoint needs must be sent along with the request either in the URL, the request headers or the request body. That means you could send the value of the cookie (not the cookie, itself) in the query string of a GET request or the body of a POST, or as is typical with REST API auth, as an Authorization HTTP header.
So to get your desired result, you would need to extract the value of the cookie on the client in your MVC application, then send it along as part of the data of your API request, or use an Authorization HTTP header as suggested.
I have a Mvc 5 client that have a Ownin cookie authentication.
I also have a Web Api that is protected with Owin Bearer token (I used the VS2013 Web Api template, that create the Token endpoint)
Ok, now my Mvc 5 client need to use my WebApi.
I created a method to get the bearer token:
internal async Task<string> GetBearerToken(string siteUrl, string Username, string Password)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(siteUrl);
client.DefaultRequestHeaders.Accept.Clear();
HttpContent requestContent = new StringContent("grant_type=password&username=" + Username + "&password=" + Password, Encoding.UTF8, "application/x-www-form-urlencoded");
HttpResponseMessage responseMessage = await client.PostAsync("Token", requestContent);
if (responseMessage.IsSuccessStatusCode)
{
TokenResponseModel response = await responseMessage.Content.ReadAsAsync<TokenResponseModel>();
return response.AccessToken;
}
return "";
}
And in my Mvc action I called that:
public async Task<ActionResult> Index()
{
var token = await GetBearerToken("http://localhost:6144/", "teste", "123456");
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer "+ token);
var response = await client.GetAsync("http://localhost:6144/api/values");
if (response.IsSuccessStatusCode)
{
var data = response.Content.ReadAsAsync<IEnumerable<string>>();
return Json(data.Result, JsonRequestBehavior.AllowGet);
}
}
}
That all works fine... But I need to use the Web Api in all my actions...
So how can I keep that token (despite getting a new token every request) and how verify if it expired ... Is it possible to keep that together with authentication cookie someway?
Any best pratices to deal with that scenario?
Thanks
If I get it right your MVC 5 client app is accessing a WebAPI of a different app.
The MVC 5 Client uses a cookie to authenticate the user. To access the WebAPI you get a Bearer tokeen from the /Token endpoint and send it in the Authorization header.
You do not call the WebAPI from your client side Javascript code, you just call it from within MVC Actions running on the server of the MVC5 application.
Getting a new Token before each service call sounds wrong. This would mean 2 roundtrips each time. This can't be performant.
If I got that right, you could:
Store the token in the Session object. For as long as your user of the MVC App is authenticated and his session is alive you would then always have the same Token.
If its expired you would get a 401 unauthorized access back from the WebAPI.
To keep your MVC Action Unit Testable you could wrap the session access into a Service that you inject into the Action (Dependency Injection).
you could store the Token in a cookie similar to the Authentication cookie already existing. This way you would not need a Session on the server side. Here again I would wrap the access to get the Token from the Cookie in a service that all your actions use.
I would use the Session storage. Simple. Straight forward.
But maybe I am missing something
Hope this helps you.
Feedback appreciated :-)
Bearer token is not a good way to authorize your web application. If you store services' token in cookie it will be available to the application's clients, so service layer will be vulnerable to application's clients. The only solution seems to be keep token in a session but you will lose stateless nature of your application.
Here is describied what/how bearer token should be used: "A bearer token is just a big, random string that a client must present on every API call. Bearer tokens are simple because there's no special signature or validation code required on either end. The client is responsible for storing the token in a safe place and sending it with every request. The server is responsible for looking up the token in a database and making sure it's a valid one -- that's it.".
Here is good example of using bearer token in single page application where client directly talks to the service.
Anyway I would suggest you to use HMAC authentication, BCrypt or ClientCertificates. Even amazon uses it for authenticating REST requests.
If you want to manage the tokens across all of your actions, you should change the code to use a custom authorization filter. That filter can be added to all Web API requests, all actions for a controller, or an individual action. To do that, derive from the AuthorizeAttribute and issue the GetBearerToken call from the filter. Stick the token into the HTTP context for usage during request processing. Instead of directly calling creating HttpClient instances, you could use a factory to generate them and add the appropriate tokens for authentication.
As for determining if the tokens are expired, you could add an additional filter that checks for specific errors coming back or alternative issue a check in the authorization filter. I don't know all of your requirements so it's difficult to determine the appropriate solution there.
(This question can be seen as follow ups to these two StackOverflow posts about OpenAuth with DotNetOpenAuth in a ServiceStack scenario: first and second)
From what I understand, ServiceStack uses an IAuthSession to know which user is authenticated, but this seems to rely on the HTTP session cookie. With OAuth request, no such cookie exist.
Question: I want my ServiceStack requests to be considered authenticated if 1) a the browser cookie is present or 2) if the OAuth Authentication Header Bearer is present. How should I do this?
I tried the following to set the thread's authentication, but it relies on ASP.NET's HttpContext.Current.User.
I'd also like it to work on both IIS hosted and Self-Hosted scenarios...
var analyzer = new StandardAccessTokenAnalyzer((RSACryptoServiceProvider)signCert.PublicKey.Key, (RSACryptoServiceProvider)encryptCert.PrivateKey);
var resourceServer = new ResourceServer(analyzer);
var requestWrapper = new HttpRequestWrapper((HttpRequest)request.OriginalRequest);
var principal = resourceServer.GetPrincipal(requestWrapper, requiredScopes);
HttpContext.Current.User = principal;
Any help is appreciated.