JSON, getting information using HttpPost - c#

I'm recreating an old Cordova app in Xamarin forms PCL and I need to access this method that's on the server, providing it with the username and password and storing the information that comes back:
[HttpGet]
public JsonResult LoginUser(string userName, string password)
{
bool responseResult = false;
IEBUser user = null;
string errMsg = String.Empty;
try
{
if (String.IsNullOrEmpty(userName))
{
throw new Exception("userName is Empty");
}
else if (String.IsNullOrEmpty(password))
{
throw new Exception("userName is Password");
}
// connect to DB and find the user if it can
user = SelfServiceMembership.GetUserByUserName(userName);
// if no suer then user wasn't found or DB Errored
if (user == null)
{
throw new Exception("userName was not found");
}
// decrypt pw and see if they match with username's account
PasswordHash PH = new PasswordHash();
password = PH.GenerateHash(password);
if (user.HashWord != password)
{
throw new Exception("Password does not match the one on our records");
}
responseResult = true;
}
catch (Exception ex)
{
errMsg = ex.Message;
}
if (responseResult)
{
return Json(new
{
result = responseResult,
user = new
{
userId = user.UserID,
userName = user.UserName,
firstName = user.FirstName,
lastNmae = user.LastName,
email = user.Email
}
},
JsonRequestBehavior.AllowGet);
}
return Json(new
{
result = responseResult,
errorMessage = errMsg
},
JsonRequestBehavior.AllowGet);
}
The old Javascript code that called this method looks like this:
// gets user info from web service
loginUser: function (userName, password){
responseData = null;
$.ajax({
type: "GET",
url : app.CurrentCompanyDetails.WebServiceURL + this.apiRoot + this.loginUserCall,
dataType: "json",
data: {
userName: userName,
password: password
},
async: false,
crossDomain: true,
success: function(response) {
responseData = response;
},
error: function (xhr, status, msg) {
alert(msg);
}
});
return responseData;
},
I can't seem to find a definitive answer anywhere on how to accomplish this in C#

Here is an example showing how I make a Post call. I guess if you change to HttpMethod.GET it should work too.
My example sends userName and password to a REST API and if everything is ok, it returns the Cookie from the response. You can change it to adapt to your needs.
private static Cookie ConfigurarAcessoGED() {
Uri uri = new Uri("URL_FROM_API");
var req = new HttpRequestMessage(HttpMethod.Post, uri);
var reqBody = #"{
'UserName':'USERNAME',
'Password':'PASSWORD'
}";
req.Content = new StringContent(reqBody, Encoding.UTF8, "application/json");
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
HttpClient client = new HttpClient(handler);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
HttpResponseMessage resp = null;
try {
resp = client.SendAsync(req).Result;
} catch (Exception ex) {
throw new Exception("Something went wrong");
}
if (!resp.IsSuccessStatusCode) {
string message;
if (resp.StatusCode == HttpStatusCode.Unauthorized || resp.StatusCode == HttpStatusCode.Forbidden || resp.StatusCode == HttpStatusCode.Redirect)
message = "Permission denied.";
else
message = resp.ReasonPhrase;
throw new Exception(message);
}
IEnumerable<Cookie> responseCookies = cookies.GetCookies(uri).Cast<Cookie>();
if (responseCookies.FirstOrDefault() != null)
return responseCookies.FirstOrDefault();
return null;
}
Here is my method from my API:
[HttpPost, Route("LogIn")]
public IActionResult LogIn([FromServices] UserService userService, [FromBody]LoginModel login) {
using (var session = SessionFactory.OpenSession())
using (var transaction = session.BeginTransaction()) {
User user = userService.Login(session, login.UserName, login.PassWord);
if (user == null)
return BadRequest("Invalid user");
var identity = new ClaimsIdentity("FileSystem");
identity.AddClaim(new Claim(ClaimTypes.Name, login.UserName));
foreach (Role r in user.Roles) {
identity.AddClaim(new Claim(ClaimTypes.Role, r.Nome));
}
var principal = new ClaimsPrincipal(identity);
HttpContext.Authentication.SignInAsync("FileSystem", principal).Wait();
}
return Ok();
}

Related

Decoding JWT in unity

I have an issue that I'm now completely at a loss over.
I have an authentication server that creates and sends JWT's to a unity application (android/VR).
The auth server creates a token as such
private string GenerateJwtToken(User user)
{
var symmetricKey = Convert.FromBase64String(_tokenSettings.Secret);
var expires = DateTime.Now.AddMinutes(Convert.ToInt32(15));
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.PrimarySid, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim("company", user.Company),
new Claim(ClaimTypes.Role, "api_user"),
new Claim(ClaimTypes.Expiration, $"{expires.ToShortDateString() + " " + expires.ToShortTimeString()}"),
}),
Expires = expires,
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(symmetricKey),
SecurityAlgorithms.HmacSha256Signature)
};
var securityToken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(securityToken);
return token;
}
I have no issues decoding this and getting out the claims in a console application. This is where the confusion comes in. When I try and perform the same task in Unity and build the application to the VR headset, I'm met with issues, namely this
"IDX10729: Unable to decode the header 'header' as Base64Url encoded string. jwtEncodedString: 'Token here'."
The unity auth service looks like this
public async Task<string> GetAccessToken(string username, string password)
{
using (var client = new HttpClient())
{
using (var request = new HttpRequestMessage())
{
//TODO: Replace this hardcoded URL value here.
request.RequestUri = new Uri("{{URL Emmited}}");
request.Method = HttpMethod.Post;
var body = new AuthRequest()
{
Username = username,
Password = password
};
var content = new StringContent(JsonConvert.SerializeObject(body), encoding: Encoding.UTF8, "application/json");
request.Content = content;
var response = await client.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();
var tokenContent = JsonConvert.DeserializeObject<Response>(responseContent);
var token = tokenContent.Token;
return token;
}
}
}
and retrieving the claims, I've tried two different ways
public string GetUserName(string token)
{
try
{
var securityToken = new JwtSecurityToken(token);
return securityToken?.Claims.First(claim => claim.Type == "given_name").Value;
}
catch (Exception exception)
{
//log exception here
return string.Empty;
}
}
and
public string GetUserName(string token)
{
try
{
var handler = new JwtSecurityTokenHandler();
var securityToken = handler.ReadJwtToken(token);
return securityToken?.Claims.First(claim => claim.Type == "given_name").Value;
}
catch (Exception exception)
{
//log exception here
return string.Empty;
}
}
ReadJwtToken internally just creates a new JwtSecurityToken object so I assume there's something in that I'm missing.
Both of these throw the same "Unable to decode the header 'header' as Base64Url encoded string" error, however, if I do handler.CanReadToken(token); this returns true, which baffles me even more.
Anyone have any ideas as to why this is happening and any clues on how to fix it.
some further information; the token I retrieve can be decoded in JWT.io and in a console app, I'm using the dotnet standard 2.0 assemblies for System.IdentityModel, System.IdentityModel.Tokens.JWT, System.IdentityModel.Tokens, System.IdentityModel.Logging and Newtonsoft.Json

Api Call returning an exception

I am developing a game using Monogame and I want to access my Api so that I can make a login but it always returns me an exception. If I use port 80 I get the following one No connection could be made because the target machine actively refused it and if I use port 5000 I get a 401: Not authorized .
By printing in the console I could come to the conclusion that my try is interrupted at the line response = await client.GetStringAsync(builder.Uri.AbsoluteUri);
Is there something wrong with my code?
Communication class
public class Communication
{
private readonly HttpClient client = new HttpClient();
private const string Uri = "http://localhost:5000/";
private const int Port = 5000;
public Communication()
{
}
public async Task<User> Login(string username, string password)
{
string response;
User user = null;
try
{
var builder = new UriBuilder(Uri + "/Api/Account/Login/")
{
Port = Port
};
builder.Query = $"Username={username}&Password={password}";
Debug.WriteLine("Chegou Aqui!!!!")
response = await client.GetStringAsync(builder.Uri.AbsoluteUri);
if (response == "OK")
{
user = JsonConvert.DeserializeObject<User>(response);
}
}
catch (Exception ex)
{
Debug.WriteLine("\tERROR {0}", ex.Message);
}
return user;
}
}
My Api Login Method
[AllowAnonymous]
[HttpPost("Login")]
public IActionResult Authenticate([FromBody]AuthenticateModel userModel)
{
var user = _userService.Authenticate(userModel.Username, userModel.Password);
if(user == null)
{
return BadRequest(new { message = "Username or Password invalid" });
}
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return Ok(new
{
Id = user.Id,
UserName = user.Username,
Token = tokenString
}) ;
}
This article possibly can help you
You can check your firewall settings or if any antivirus installed in your system, make sure to check the settings of it.
You get error 401: Not authorized because HTTP requests by default configured on port 80, not any other ports.
If the problem persists, check your router or switch configuration, if any is in your route to server.

Pass Image to WebAPI from MVC contoller so that it's available in HttpContext.Current.Request.Files

I have created a webApi which expect form-data. I am passing both image and data to the WebAPI. When I pass the parameters using PostMan i can recieve data inside model and file is avialable inside HttpContext.Current.Request.Files.
Now i want to pass same data and image to webapi though MVC contoller. Data am able to get in my api method but getting file proving to be a difficult task.
This is my APi method
[Route("Profile/EditUserProfileAsync")]
[HttpPost]
public async Task<HttpResponseMessage> EditUserProfile(UserProfile userProfile)
{
try
{
var Identity = (ClaimsPrincipal)Thread.CurrentPrincipal;
int UserId = Convert.ToInt32(Identity.Claims.Where(c => c.Type == ClaimTypes.Sid).Select(c => c.Value).SingleOrDefault());
if (UserId == 0)
{
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "User Not Found!");
}
userProfile.UserId = UserId;
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var Result = await _userprofile.EditUserProfile(userProfile);
return Request.CreateResponse(HttpStatusCode.Created, Result);
}
catch (Exception ex)
{
await _log.logError(new ErrorLog() { CreatedDate = DateTime.Now, CustomMessage = "Error in EditUserProfile API", ErrorMessage = ex.Message, ModuleName = "UserProfile", StackTrace = ex.StackTrace });
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.Message);
}
}
This is the method that is being called by the api to save data
public async Task<MethodResult> EditUserProfile(UserProfile userProfile)
{
MethodResult methodResult = new MethodResult();
bool profileComplete = true;
try
{
if (userProfile.ImagePath == null && HttpContext.Current.Request.Files.Count == 0)
{
return new MethodResult() { Success = false, Message = "Profile Image is required !!" };
}
// some code here
}
catch(Exception ex)
{
// log error
}
}
here files count is always comming as zero in the EditUserProfile method in webapi
This the MVC controller code
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<JsonResult> SaveUserProfileDetails(UserProfile userProfile)
{
string Result = "true";
try
{
var identity = (ClaimsPrincipal)Thread.CurrentPrincipal;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Remove("Authorization");
client.DefaultRequestHeaders.Add("Authorization", identity.FindFirst("AcessToken").Value);
MultipartFormDataContent form = new MultipartFormDataContent();
form.Add(new StringContent(userProfile.FirstName), "FirstName");
form.Add(new StringContent(userProfile.UserId.ToString()), "UserId");
form.Add(new StringContent(userProfile.LastName), "LastName");
form.Add(new StringContent(userProfile.Designation), "Designation");
form.Add(new StringContent(userProfile.Gender.ToString()), "Gender");
form.Add(new StringContent(userProfile.City), "City");
form.Add(new StringContent(userProfile.Country), "Country");
form.Add(new StringContent(userProfile.DateOfBirth), "DateOfBirth");
form.Add(new StringContent(userProfile.Specialties), "Specialties");
if (Request.Files.Count > 0)
{
MemoryStream ms = new MemoryStream();
Request.InputStream.CopyTo(ms);
byte[] data = ms.ToArray();
ByteArrayContent fileContent = new ByteArrayContent(data);
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { Name = "file"};
form.Add(fileContent);
}
else
{
form.Add(new StringContent(userProfile.ImagePath), "ImagePath");
}
var resp = await client.PostAsync(Constant.ApiBaseUri + "Profile/EditUserProfileAsync", form);
if (resp.IsSuccessStatusCode)
{
var result = await resp.Content.ReadAsStringAsync();
MethodResultVM<string> methodResult = JsonConvert.DeserializeObject<MethodResultVM<string>>(result);
Result = methodResult.Message;
}
else
{
Result = "false";
}
}
catch (Exception ex)
{
Result = "false";
}
return Json(Result, JsonRequestBehavior.AllowGet);
}
with this i am relieving Input stream in header. But i don't want that as android and ios applications are already using the same api and i don't want to modify the api.
Please guide me what can be done to recieve IMAGE in httpcontext in web api controller from httpclient object in mvc controller.

How to prevent passing id and password in url for a get call in web api C#?

I have written a code to fetch details from active directory as a GET call based on user id and password. I am passing user id and password in the url like -http://localhost:1234/api/User/IsAuthorized/UserID=1234;password=qwerty
but this is an unsafe technique. Can anybody give me a solution to pass these values in the body and use it as a POST call instead of a get call
my code goes like-
[Route("IsAuthorized/UserID={userName};password={password}")]
[AllowAnonymous]
public IHttpActionResult GetIsAuthorized(string userName,string password)
{
HttpResponseMessage response = null;
string errorMessage = null;
bool hasError = false;
bool isValid;
UserDetails detail = null;
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "ABC"))
{
isValid = pc.ValidateCredentials(userName, password);
string token = null;
if (isValid)
{
detail = IsAuthenticated("abc", userName, password, out errorMessage, out hasError);
}
if (hasError)
{
detail = new UserDetails(isValid, userName, null, null, null, errorMessage, null);
}
else
{
if (detail != null)
{
token = CreateToken(userName);
detail = new UserDetails(isValid, userName, detail.AssociateName, detail.Mobile, detail.Email, null, token);
}
else
detail = new UserDetails(isValid, userName, null, null, null, "unknown username or bad password", null);
}
return Ok(detail);
}
}
Why are you even doing it like this? You must be calling you web api from a client. Why are you not using the HttpClient to pass your credentials to your api. Something like this:
public async Task<TResult> PostAsync<TResult, TInput>(string uriString, TInput payload = null) where TInput : class
{
var uri = new Uri(uriString);
using (var client = GetHttpClient())
{
var jsonContent = JsonConvert.SerializeObject(payload, Formatting.Indented, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
HttpResponseMessage response = await client.PostAsync(uri, new StringContent(jsonContent, Encoding.UTF8, "application/json"));
if (response.StatusCode != HttpStatusCode.OK)
{
//Log.Error(response.ReasonPhrase);
return default(TResult);
}
var json = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<TResult>(json);
}
}
private HttpClient GetHttpClient()
{
var client = new HttpClient();
var username = //get username
var password = // get password
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}

HttpClient & WebAPI Unpredictable Redirect 302

I have a WPF app that uses HttpClient to call a Web API method on another server. Everything works fine, however, sometimes, especially when the client app wakes up (from power suspend), the call to the same server method returns Found: 302 instead of OK: 200.
The Web API method doesn't really do anything, and it just has an [Authorize] attribute to make sure the user is authenticated. Normally, if the user is not authenticated, it would return 404.
BTW, I'm not using ASP.NET forms authentication explicitly, but the redirect is sending the client to login.aspx, which doesn't exist.
Here's the code:
Web API Method (.NET 4.5):
[HttpGet]
public HttpStatusCodeResult IsAuthenticated()
{
return new HttpStatusCodeResult(System.Net.HttpStatusCode.OK);
}
WPF App client code (.NET 4.0):
public async Task<bool> IsAuthenticated()
{
try
{
Uri address = new Uri(AuthUrl);
var cookieJar = ReadCookiesFromDisk(COOKIE_FILE);
if (cookieJar.Count == 0)
return false;
var handler = new HttpClientHandler
{
CookieContainer = cookieJar,
UseCookies = true,
UseDefaultCredentials = false,
AllowAutoRedirect = false
};
var client = new HttpClient(handler)
{
BaseAddress = address
};
int timeout = 15000;
var task = client.GetAsync(address.ToString());
Task[] tasks = new Task[1];
tasks[0] = task;
if (Task.WaitAny(tasks, timeout) != -1)
{
// task completed within timeout
// checking for redirect, but this should not happen!!!
HttpResponseMessage response = task.Result;
if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Redirect)
return true;
else
return false;
}
else
{
// timeout logic
return false;
}
}
catch (Exception e)
{
EventLogger.Log(e);
return false;
}
}
Client Authentication Code:
public async Task<string> Login(string data)
{
try
{
Uri address = new Uri(LoginUrl);
HttpContent content = new StringContent(data, Encoding.UTF8);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
var cookieJar = ReadCookiesFromDisk(COOKIE_FILE);
var handler = new HttpClientHandler
{
CookieContainer = cookieJar,
UseCookies = true,
UseDefaultCredentials = false,
AllowAutoRedirect = false
};
var client = new HttpClient(handler)
{
BaseAddress = address
};
HttpResponseMessage response = await client.PostAsync(address.ToString(), content);
response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync();
Uri uri = new Uri(UrlBase);
var responseCookies = cookieJar.GetCookies(uri);
if (responseCookies[".ASPXAUTH"] != null)
WriteCookiesToDisk(COOKIE_FILE, cookieJar);
return body;
}
catch (Exception e)
{
return e.ToString();
}
}
Server:
public JsonResult LogOn(string userInfo)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
LogOnModel model = serializer.Deserialize<LogOnModel>(userInfo);
JsonMessage error = null;
if (ModelState.IsValid)
{
try
{
if (WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
if (WebSecurity.IsConfirmed(model.UserName))
{
if (model.RememberMe)
Response.Cookies[0].Expires = DateTime.Now.AddDays(30);
else
Response.Cookies[0].Expires = DateTime.Now.AddDays(7);
var person = Dc.People.FirstOrDefault(x => x.UserName == model.UserName);
string fullName = string.Empty;
string email = string.Empty;
if (person != null)
{
fullName = string.Format("{0} {1}", person.FirstName, person.LastName);
email = person.Email;
//fill the session to create the session cookie
Session["1"] = 1;
}
var message = new { FailCount = 0, Message = WebSecurity.GetUserId(model.UserName).ToString(), Success = true, SuccessCount = 0, RedirectUrl = "#/conversations/priority", Name = fullName, UserEmail = email, UserHandle = model.UserName, UserAvatar = person.Avatar };
return Json(message);
}
else
{
error = new JsonMessage { FailCount = 0, Message = "Your aren't authorised to login", Success = false, SuccessCount = 0 };
TempData["Error"] = "Your are not authorised to login";
return Json(error);
}
}
else
{
TempData["Error"] = "The user name or password provided is incorrect.";
error = new JsonMessage { FailCount = 0, Message = "The user name or password provided is incorrect.", Success = false, SuccessCount = 0 };
return Json(error);
}
}
catch (Exception ex)
{
TempData["Error"] = ex.Message;
error = new JsonMessage { FailCount = 0, Message = ex.Message, Success = false, SuccessCount = 0 };
}
}
return Json(error);
}
Change following code
HttpResponseMessage response = await client.PostAsync(address.ToString(), content);
to
HttpResponseMessage response = await client.PostAsync(address.ToString(), content).ConfigureAwait(false);
above statement sometime show deadlock issues when using ASync. so when you configure await to false for Async Task, it will work in correct manner.

Categories

Resources