How to do ASP.NET Web API integration tests with authorize attribute - asp.net-mvc

I do have authorize attribute applied on my Web API.
I am calling Web API from MVC4 application in which I am using standard cookie based authentication.
I need to call Web API method on controllers from integration tests but because authorize attribute is applied I will always receive unauthorized exception.
What is the best way to solve this problem ?
PS. I don't want (need) to use other methods of authentication such as APIKey,Token in Auth Header and similar...

First of all, one key element in order to answer this question is to know what kind of authentication mechanism you use. For example, if you use basic auth, you can send the credentials when you are integration testing:
[Fact]
public async Task FooTest() {
var username = "user";
var password = "supersecret";
// construct your config here as I do below.
// RouteConfig and WebAPIConfig are my own classes
var config = new HttpConfiguration();
RouteConfig.RegisterRoutes(config);
WebAPIConfig.Configure(config);
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/cars");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Authorization = new AuthenticationHeaderValue(
"Basic", EncodeToBase64(string.Format("{0}:{1}", username, password)));
using (var httpServer = new HttpServer(config))
using (var client = new HttpClient(httpServer)) {
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsAsync<Car>();
// do you test now...
}
}
private static string EncodeToBase64(string value) {
byte[] toEncodeAsBytes = Encoding.UTF8.GetBytes(value);
return Convert.ToBase64String(toEncodeAsBytes);
}
Of course, your handler which handles the authentication should be able to authenticate you with those credentials.
On the other hand, as you will be hosting the application in memory, setting an authenticated principal to the Thread.CurrentPrincipal would be another option but wouldn't be my favorite option here.

Related

User Principal in Entity Framework Core DbContext

I am upgrading a project from .NET 4.6 to .NET Core. It is an ASP.NET MVC website with a WebAPI that uses EntityFramework. When the a (MVC or WebAPI) Controller fires up the DbContext, there is code that needs to identity the user as a ClaimsIdentity to inspect their claims. In previous .NET, this was most reliably available on Thread.CurrentPrincipal like this:
ClaimsIdentity identity = System.Threading.Thread.CurrentPrincipal.Identity as ClaimsIdentity;
IIRC, this was the safest way to do it since you could be coming from different contexts - WebAPI or ASP.NET MVC.
In the .NET core solution, I have tried to Dependency Inject an IHttpContextAccessor into the constructor, but the User on HttpContext is not authorized and has no claims
ClaimsIdentity identity = httpContext.HttpContext.User.Identity;
// identity.IsAuthenticated == false. identity.Claims is empty.
Security is wired up in Startup.cs:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).
AddCookie(options =>
{
options.LoginPath = "/Login";
options.Cookie.HttpOnly = true;
}).
AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
var key = Configuration["Tokens:Key"];
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = Configuration["Tokens:Issuer"],
ValidAudience = Configuration["Tokens:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key))
};
});
The user logins on a /Login MVC view page, which logs in via Cookies and also generates a Bearer token in another request that is saved on the client. After all this the user is redirected to the homepage.
Cookie Login:
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = bIsPersistent });
Token Generation (called from ajax, saved to localstorage before redirection)
var secretKey = Configuration["Tokens:Key"];
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
var creds = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256 );
var expires = DateTime.Now.AddHours(8);
var token = new JwtSecurityToken(
_config["Tokens:Issuer"],
_config["Tokens:Issuer"],
oAuthIdentity.Claims,
expires: expires,
signingCredentials: creds
);
ret = Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
After landing on the homepage, an ajax call is made to the WebApi with the bearer token (I pulled the bearer token out of the http request and verified the signature on jwt.io), and the webapi causes the DbContext to be instantiated, and this is where the identity is not valid.
It's as if the Identity is not properly marshalled over to the DbContext -
How to I get the correct User or Identity in the DbContext?
Additionally, at the point I need it is in the DbContext construction, which I don't have alot of control over with the Dependency Injection. But I need to get this info basically from a default constructor or lazy load it somehow.
With your setup, you have two authentications setup. So, in your ConfigureServices function in Startup class, you need to use something like the following:
services.AddAuthentication().AddCookie().AddJwtBearer();
Don't forget to specify a default authentication. For instance, if you want the authentication to be cookies by default, you can use this:
services.AddAuthentication("Cookies").AddCookie().AddJwtBearer();
Or to keep the code safer,
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie().AddJwtBearer();
In your startup class, in the Configure function, don't forget to add
app.UseAuthentication();
When authenticating within a controller, you will need to use the scheme name along with the [Authorize] if you are not using the default scheme.
[Authorize(AuthenticationSchemes = "")]

MVC with REST and client authentication

I've got a working model of an MVC application working with Authentication (using Azure B2C). In order to authenticate my client, I need to add a 'Bearer' token to the header like so:
public async Task<string> GetValuesAsync()
{
var client = new HttpClient { BaseAddress = new Uri(this.serviceOptions.BaseUrl, UriKind.Absolute) };
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer",
await this.GetAccessTokenAsync());
return await client.GetStringAsync("api/products");
}
I don't want to create a proxy for every CRUD operation I have and I want to use the Kendo UI products which have a pretty slick RESTful API. Is there some way to automatically add the bearer token to the header in every HTTP operation so I don't have to make proxies and can use a proper RESTful API?

A simple ASP .NET MVC API controller using roles

I wrote a web application using ASP .NET MVC and authorization system by default. I configured IdentityRole and input through external providers. Using the current database I have created my data context. Now I want to write a Xamarin.Android app and connect to my database, I want a simple API. But the feature that you want to access this API was only available to user with a certain role. The API is really very simple and therefore do not want to add to the draft WCF or WebAPI project. How to do it best?
First, you don't need a separate project to use Web Api; you can use both MVC and Web Api in the same project. For one off endpoints for things like in-site AJAX requests, just creating MVC actions that return JSON or XML would be fine, but if you're talking about a true API, even if it's fairly simplistic, I'd say go Web Api.
You'd protect your Web Api actions much the same as you would your MVC actions, using the [Authorize] attribute. If you need to restrict by role, you just pass a role(s) to that. However, the big difference here, especially if you're serving a mobile app, is that you'll need pass the authorization along with the request. That's generally accomplished using the Authorization header along with a bearer token. Basically, you would need to set up an endpoint that signs a user in and returns a token. Then, each subsequent request that needs authorization includes that token in the header.
I want to finish and to fully answer this question and close this topic. I've been searching for how to add the ability for a mobile client to connect to an existing site on ASP.NET MVC. In my search, I came across a great article Justin Hyland on March 2, 2014
In principle, everything in this article is well and clearly written, but I want to make a tiny contribution for clarity.
Under Setup WebAPIConfig stated that the need
added in the following code to the WebApiConfig Register method
But if we consider the case ASP.NET MVC we don't have such file. It's all very simple, you just need such a file to create the folder App_Start. The contents of the file can be left exactly as it is in the article.
To get rid of the bugs which will inevitably appear we need to install two nuget package: Microsoft.AspNet.WebApi and Microsoft.AspNet.WebApi.Owin.
Excellent! Now we can turn to the method to obtain the token and then adding the token to the query we can get the needed data closed by the attribute [Authorize].
A small remark. If You need to access a method which is closed for a specific role that to the Authenticate method from the article should add a few lines of code. Immediately after the line:
identity.AddClaim(new Claim(ClaimTypes.Name, user));
add the line:
identity.AddClaim(new Claim(ClaimTypes.Role, role));
where role you can get the following, for example:
var userIdentity = UserManager.FindAsync(user, password).Result;
var role = RoleManager.FindById(userIdentity.Roles.First().RoleId).Name;
User and password you have to send a request.
I also want to give an example of code which will send request and receive response. To not have to look for and immediately start coding.
async Task<string> GetToken(string userName, string password)
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>( "user", userName ),
new KeyValuePair<string, string> ( "password", password )
}
);
using (var client = new HttpClient())
{
HttpResponseMessage response = await client.PostAsync(APP_PATH + "/Authenticate", content);
var result = await response.Content.ReadAsStringAsync();
return result;
}
}
async Task<string> GetUserInfo(string token)
{
using (var client = CreateClient(token))
{
var response = await client.GetAsync(APP_PATH + "/ValidateToken");
return await response.Content.ReadAsStringAsync();
}
}
HttpClient CreateClient(string accessToken = "")
{
var client = new HttpClient();
if (!string.IsNullOrWhiteSpace(accessToken))
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
}
return client;
}
All have only to call the appropriate methods in the correct order. I hope that is useful to someone.
P.S.
If You create a new project in Visual Studio to get this functionality you just need to tick:

GetClientAccessToken having clientIdentifier overwritten to null by NetworkCredential

I've been trying to get the GetClientAccessToken flow to work with the latest release 4.1.0 (via nuget), where I'm in control of all three parties: client, authorization server and resource server.
The situation I have started to prototype is that of a Windows client app (my client - eventually it will be WinRT but its just a seperate MVC 4 app right now to keep it simple), and a set of resources in a WebAPI project. I'm exposing a partial authorization server as a controller in the same WebAPI project right now.
Every time (and it seems regardless of the client type e.g. UserAgentClient or WebServerClient) I try GetClientAccessToken, by the time the request makes it to the auth server there is no clientIdentifier as part of the request, and so the request fails with:
2012-10-15 13:40:16,333 [41 ] INFO {Channel} Prepared outgoing AccessTokenFailedResponse (2.0) message for <response>:
error: invalid_client
error_description: The client secret was incorrect.
I've debugged through the source into DNOA and essentially the credentials I'm establishing on the client are getting wiped out by NetworkCredential.ApplyClientCredential inside ClientBase.RequestAccessToken. If I modify clientIdentifier to something reasonable, I can track through the rest of my code and see the correct lookups/checks being made, so I'm fairly confident the auth server code is ok.
My test client currently looks like this:
public class AuthTestController : Controller
{
public static AuthorizationServerDescription AuthenticationServerDescription
{
get
{
return new AuthorizationServerDescription()
{
TokenEndpoint = new Uri("http://api.leave-now.com/OAuth/Token"),
AuthorizationEndpoint = new Uri("http://api.leave-now.com/OAuth/Authorise")
};
}
}
public async Task<ActionResult> Index()
{
var wsclient = new WebServerClient(AuthenticationServerDescription, "KieranBenton.LeaveNow.Metro", "testsecret");
var appclient = new DotNetOpenAuth.OAuth2.UserAgentClient(AuthenticationServerDescription, "KieranBenton.LeaveNow.Metro", "testsecret");
var cat = appclient.GetClientAccessToken(new[] { "https://api.leave-now.com/journeys/" });
// Acting as the Leave Now client we have access to the users credentials anyway
// TODO: CANNOT do this without SSL (turn off the bits in web.config on BOTH sides)
/*var state = client.ExchangeUserCredentialForToken("kieranbenton", "password", new[] { "https://api.leave-now.com/journeys/" });
// Attempt to talk to the APIs WITH the access token
var resourceclient = new OAuthHttpClient(state.AccessToken);
var response = await resourceclient.GetAsync("https://api.leave-now.com/journeys/");
string sresponse = await response.Content.ReadAsStringAsync();*/
// A wrong one
/*var wresourceclient = new OAuthHttpClient("blah blah");
var wresponse = await wresourceclient.GetAsync("https://api.leave-now.com/journeys/");
string wsresponse = await wresponse.Content.ReadAsStringAsync();
// And none
var nresourceclient = new HttpClient();
var nresponse = await nresourceclient.GetAsync("https://api.leave-now.com/journeys/");
string nsresponse = await nresponse.Content.ReadAsStringAsync();*/
return Content("");
}
}
I can't figure out how to prevent this or if its by design what I'm doing incorrectly.
Any help appreciated.
The NetworkCredentialApplicator clears the client_id and secret from the outgoing message as you see, but it applies it as an HTTP Authorization header. However, HttpWebRequest clears that header on the way out, and only restores its value if the server responds with an HTTP error and a WWW-Authenticate header. It's quite bizarre behavior on .NET's part, if you ask me, to suppress the credential on the first outbound request.
So if the response from the auth server is correct (at least, what the .NET client is expecting) then the request will go out twice, and work the second time. Otherwise, you might try using the PostParameterApplicator instead.

Trying to decrypt a FormsAuthentication ticket always unable to validate data

I am using the new webapi.
Now I don't know if I am doing this correctly but I am trying to setup my api to return an authentication cookie within the HttpResponseMessages header to use on another an mvc application.
I am using the FormsAuthenticationTicket as I think its what I need to use like
public HttpResponseMessage Get(LoginModel model)
{
if (model.UserName == "bob")
{
// if (Membership.ValidateUser(model.UserName, model.Password))
// {
var msg = new HttpResponseMessage(HttpStatusCode.OK);
var expires = DateTime.Now.AddMinutes(30);
var auth = new FormsAuthenticationTicket(1, model.UserName, DateTime.Now, expires,
model.RememberMe,"password",
FormsAuthentication.FormsCookiePath);
var cookie = new HttpCookie("user");
cookie.Value = FormsAuthentication.Encrypt(auth);
cookie.Domain = "localhost";
cookie.Expires = expires;
msg.Headers.Add("result",cookie.Value);
return msg;
// }
}
return new HttpResponseMessage(HttpStatusCode.Forbidden);
//else
//{
// return "The user name or password provided is incorrect.";
//}
}
now within my login controller on my mvc application I call the service and get the data value from the header I set in the api controller.
string data = response.Headers["result"].ToString();
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(data);
Everytime I try running the FormsAuthentication.Decrypt I keep getting an error
Unable to validate data.
I assume its due to when the api encrypts the data it uses some kind of key that the website doesn't know about. Am I right?
Can someone help out?
Thank you
I assume its due to when the api encrypts the data it uses some kind
of key that the website doesn't know about. Am I right?
FormsAuthentication.Encrypt and Decrypt methods use the machine key. So make sure you have configured the same key for both your Web API web application and the consuming ASP.NET MVC application.
You could also take a look at the following article which illustrates how you could use OAuth 2.0 with the Web API.

Resources