Trying to decrypt a FormsAuthentication ticket always unable to validate data - asp.net-mvc

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.

Related

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:

allow mvc5 c# webapi so that only my app can access it

I am making the api calls through the controller action method as below. The following is the working code of it.
But I want to secure the webapi so that only my application can access it. I have seen sources with login credentials
but in my case it is a public facing website with no login users.
Only the calls from my application should access it. Could anyone please suggest what can be done. or Is my current code with ValidateReferrer is suffice to handle?
[HttpGet]
[ValidateReferrer]
[ActionName("GetFind")]
[CacheOutput(ClientTimeSpan = 300, ServerTimeSpan = 300)]
public ApiQueryResponse GetFind(string query)
{
return _Worker.GetFind(query);
}
Validate Referrer in the controller has the following implementation:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext == null)
{
throw new System.Web.HttpException("No Http context, request not allowed.");
}
else
{
if (filterContext.HttpContext.Request.UrlReferrer == null)
{
throw new System.Web.HttpException("Referrer information missing, request not allowed.");
}
else if (filterContext.HttpContext.Request.UrlReferrer.Host != filterContext.HttpContext.Request.Url.Host)
{
throw new System.Web.HttpException(string.Format("Possible cross site request forgery attack, request sent from another site: {0}", filterContext.HttpContext.Request.UrlReferrer.Host));
}
}
}
In the worker class,
public ApiQueryResponse GetFind(string query)
{
var results = GetResults(Settings.ApiKey, SetFindParameters(query), Resource);
return results;
}
private ApiQueryResponse GetResults(string apiKey, string parameterQuery, string Resource)
{
var results = new ApiQueryResponse();
if (apiKey != null && !String.IsNullOrWhiteSpace(apiKey))
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync(string.Format("{0}/{1}?{2}&key={3}", WebApiUrl, Resource, parameterQuery, apiKey)).Result;
if (response.IsSuccessStatusCode)
{
var responseBodyAsText = response.Content.ReadAsStringAsync().Result;
results = JsonConvert.DeserializeObject<ApiQueryResponse>(responseBodyAsText);
}
}
}
return results;
}
Again this is the case where you have to authenticate your "application" but not users. If you check facebook/twitter/gmail api's, they have a client secret and client id to authenticate the application. But still there will be an "Authorize" call made with this id and secret for which the api returns a token and this token is used henceforth to authorize the other requests. This token will also have an expiry and there are methods to get refresh tokens.
Thus said, you have to take a call on how much security you have to implement for your api's. You could have a similar approach where your client first asks for a security token by providing the client id and secret (which should really be a secret). You can check this id and secret against your store (may be database) and if that passes the validation, you can send back a token which you could authroize using [Authroize] attribute or by custom validation.
How to create tokens should be another discussion IMO. Simple approach is mentioned here for eg- how to generate a unique token which expires after 24 hours?. There are other standard ways of generating tokens JWT/OAuth tokens.
EDIT
As a simple approach (not taking much security aspects into consideration) would be:
Create an app secret (may be a Guid value)
While sending request, take current timestamp and encrypt (have your
own encrypt and decrypt logic) the timestamp with the app secret. Lets call that encrypted value as 'token'
Pass the token in your request header (may be a custom header,say,
x-my-auth)
In the api, have a custom authorize filter
In the custom filter, overridden OnAuthroizeCore method, get the
token from request header
Decrypt the token using the same app secret and you will get the
timestamp sent from the client
If decryption is fine, then we are through the first step/ or the
token passed the first step
Additionaly, check whether the difference between the current time
and the time decrypted from token is more than 5(*you can have your
own expiry value)
If the difference is more than your expiry limit, return false which
would throw unauthorized exception back to the client (do the same if the token fails to decrypt)
The expiry check is to handle the scenario where someone hacking your
token from the request and then using it afterwards. In case if he
uses the token after your expiry, this would throw unauthorized
Consider the above logic and entire description just as a "food for thought" and DO NOT use it without proper research and understanding. My idea was to give some basic idea about the application authentication until someone really good at this writes up a really nice article in this post

problems with DotNetOpenAuth OAuth2 Client for Google

I have an ASP.NET MVC 4 application.
Yesterday my users started to complain they cannot login using their Google accounts. After lots of googling I found this: DotNetOpenAuth.GoogleOAuth2. I followed the instructions.
I created Client ID for web applications in Google console.
In AuthConfig.RegisterAuth() I have:
var client = new DotNetOpenAuth.GoogleOAuth2.GoogleOAuth2Client(googleClientID, googleClientSecret);
var extraData = new Dictionary<string, object>();
OAuthWebSecurity.RegisterClient(client, "Google", extraData);
In AccountController, I have something like this:
public ActionResult ExternalLoginCallback(string returnUrl)
{
DotNetOpenAuth.GoogleOAuth2.GoogleOAuth2Client.RewriteRequest();
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
if (!result.IsSuccessful)
{
return RedirectToAction("ExternalLoginFailure");
}
if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
{
// here I have some logic where is user sent when login was successfull
return RedirectToLocal(returnUrl);
}
if (User.Identity.IsAuthenticated)
{
// If the current user is logged in add the new account
OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
return RedirectToLocal(returnUrl);
}
else
{
// User is new, ask for their desired membership name
string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
// some logic
return View("ExternalLoginConfirmation", new RegisterExternalLoginModel { UserName = username, ExternalLoginData = loginData, EncryptedEmail = encryptedEmail });
}
}
I have two problems:
Before the change, the result.UserName contained the users email. Now it contains name. But I need email. Except for this, registration works fine.
My biggest problem - existing users cannot log in using their google account. The code goes to "// User is new, ask for their desired membership name" for them. The ProviderUserId I get now is different for the same email address.
Thanks a lot for any advice.
Can you configure this library to pass additional parameters to the Google authorization service? If so, you should pass 'openid.realm=$your_app_openid2_realm' (if your app was configured for OpenID2 before, it most likely asserted a 'realm' value in its requests, you should use the same value).
In this case, you'll receive two identifiers from Google. The new one (which is compatible with profile URLs and overall more Google APIs) and the old one (returned as openid_id).

Authentication mobile apps function call with WCF Service

I have just started to create WCF Service for my mobile apps. Currently I have Multiple project in Single Solution.
Main Web-Site Project
Edmx project
WCF lib project
Authentication function
public List<AuthenticateModel> Authenticate(string UserName, string Password)
{
SimpleMembershipInitializer _initialized = new SimpleMembershipInitializer();
bool validate = System.Web.Security.Membership.ValidateUser(UserName, Password);
AuthenticateModel model = new AuthenticateModel();
if (validate)
{
model.userID = IDUser
}
Return model;
}
Now how can I know same logged in user is calling the another function. If here any way to maintain session as web? Or pass any certificate or token for verification (if yes how to pass token and implement it)?
[OperationContract]
[WebInvoke(UriTemplate = "GetAllrecords/?paramId={paramId}", Method = "GET", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
List<SomeModel> GetAllrecords(int paramId)
{
Return something; // Return requested data
}
Any help will be greatly appreciated.
If you have the time you should read
http://www.codeproject.com/Articles/630986/Cross-Platform-Authentication-With-ASP-NET-Web-API
It focus on web api, but discusses the use of tokens.
In our authentication-method we return a generated token that is persisted on the server with user and role.
The webclient (in our case built with angularjs) composes requests with the token in the request header. We then inspect each request in our web api, extract the token and compares it to the persisted token.
There are many solutions, our is not very secure, but it's sufficient for us.

How to do ASP.NET Web API integration tests with authorize attribute

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.

Resources