Net Core 6 - can post Multi Params with FormUrlEncodedContent to web api - post

The Web API controller:
[AllowAnonymous]
[HttpPost("validateUser")]
public async Task<IActionResult> validateUser(string email, string password)
{
var identityUsr = await _userMgr.FindByEmailAsync(email);
if (await _userMgr.CheckPasswordAsync(identityUsr, password))
{
var issuer = _config["Jwt:Issuer"];
var audience = _config["Jwt:Audience"];
int addMinutes = (int)Convert.ChangeType(_config["Jwt:addMinutes"], typeof(int));
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
var token = handler.CreateJwtSecurityToken(
audience: audience,
issuer: issuer,
//notBefore: DateTime.UtcNow,
//expires: DateTime.UtcNow.AddMinutes(addMinutes),
signingCredentials: credentials
);
var tokenHandler = new JwtSecurityTokenHandler();
var stringToken = tokenHandler.WriteToken(token);
return Ok(stringToken);
}
else
{
return Unauthorized();
}
return null;
}
Sender information:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,UserName,NormalizedUserName,Email,NormalizedEmail,EmailConfirmed,PasswordHash,SecurityStamp,ConcurrencyStamp,PhoneNumber,PhoneNumberConfirmed,TwoFactorEnabled,LockoutEnd,LockoutEnabled,AccessFailedCount")] AspNetUser aspNetUser)
{
if (ModelState.IsValid)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://localhost:44327");
var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("email", "administrator#rapsosuites.com"));
values.Add(new KeyValuePair<string, string>("password", "Pn.14024284.Fm"));
var content = new FormUrlEncodedContent(values);
var result = await client.PostAsJsonAsync("/userApi/validateUser", content);
string resultContent = await result.Content.ReadAsStringAsync();
resultContent = resultContent + "//";
}
}
return View(aspNetUser);
}
I test with Postman and it works fine.
I test with the browser and it fails.
I make a change to this line and sending the parameters in the URL works fine !
var result = await client.PostAsync("/userApi/validateUser?email=administrator#rapsosuites.com&password=Pn.14024284.Fm", content);
For any reason the content object is not working sending the parameters

you can not use PostAsJsonAsync for FormUrlEncodedContent, try to use PostAsync
var content = new FormUrlEncodedContent(values);
var result = await client.PostAsync("/userApi/validateUser", content);
if you want to use PostAsJsonAsync you need much more code
fix the code
var values = new
{
email= "administrator#rapsosuites.com",
password="Pn.14024284.Fm"
};
var result = await client.PostAsJsonAsync("/userApi/validateUser", values);
and fix action
public async Task<IActionResult> validateUser([FromBody] ViewModel model)
and create a class
public class ViewModel
{
public string email {get; set;}
public string password {get; set;}
}

Related

Call Webapi with Dictionary<String, object> as parameter from ASP .NET MVC Application

I have a WebApi defined as below
public ActionResult DoSomeAction([FromForm(Name = "file")] IFormFile dataFile,
Dictionary<string,object> collection)
{
//do something
}
I am trying to call this from my client as shown below,
using (var client = new HttpClient())
{
var api_Uri = Environment.GetEnvironmentVariable("API_URL");
client.BaseAddress = new Uri(api_Uri);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Dictionary<string, object> data = new Dictionary<string, object>();
data.Add("a", "sasas");
data.Add("b", "sasas");
data.Add("", "sasas");
var inputSerialized = JsonSerializer.Serialize(data);
var stringContent = new StringContent(inputSerialized , Encoding.UTF8, "application/json");
var requestString = string.Format("api/DoSomeAction?selectedRule={0}", stringContent);
HttpResponseMessage Res = await client.PostAsync(requestString, multipartContent);
}
multipartContent is MultipartFormDataContent which contains File information.
The above code is not working. Some guidance would be appreciated.
I was able to solve this my implementing a custom IModelBinder.
I moved IFormFile and Dictionary to a class.
While creating the request as below ,
internal MultipartFormDataContent GetRequestParams(IFormFile FilePath)
{
MultipartFormDataContent multipartContent = GetFileContent(FilePath);
var dataExtractor = new DataExtractor();
var dictionaryData = dataExtractor.GetDictionary(); //This return Dictionary<string,object>
var serialisedData = JsonSerializer.Serialize(dictionaryData);
var stringContent = new StringContent(serialisedData, Encoding.UTF8, "application/json");
multipartContent.Add(stringContent, "MyCollection");
return multipartContent;
}
private MultipartFormDataContent GetFileContent(IFormFile FilePath)
{
byte[] data;
using (var br = new BinaryReader(FilePath.OpenReadStream()))
{
data = br.ReadBytes((int) FilePath.OpenReadStream().Length);
}
ByteArrayContent bytes = new ByteArrayContent(data);
MultipartFormDataContent multiContent = new MultipartFormDataContent();
multiContent.Add(bytes, "File", FilePath.FileName);
//Key is "File", bcos my property name in class is File. This should match
return multiContent;
}
Custom class containing the data
public class Input
{
public IFormFile File { get; set; }
[ModelBinder(BinderType = typeof(FormDataJsonBinder))]
public Dictionary<string,object> MyCollection{ get; set; }
}
Custom IModelBinder implementation
public class FormDataJsonBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var modelName = bindingContext.ModelName;
var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
if (valueProviderResult == ValueProviderResult.None)
{
return Task.CompletedTask;
}
bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
var value = valueProviderResult.FirstValue;
if (string.IsNullOrEmpty(value))
{
return Task.CompletedTask;
}
try
{
var result = JsonSerializer.Deserialize(value, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(result);
}
catch (Exception ex)
{
bindingContext.Result = ModelBindingResult.Failed();
}
return Task.CompletedTask;
}
}
}
Web Api Signature
public IActionResult ExecuteRule([FromForm] Input inputdata)
{
// Do something
}

.Net Core JWT + cookie auth on Web API and MVC Web Project invalid signature

My application first uses cookie auth to login. I then have it calling an API which I would like to use JWT auth which opens a websocket stream (Im debating and putting this stream on the UI) when the API gets an update it calls the UI and I want this one call to be a JWT Auth, I tried a bunch of things this past weekend but I keep getting invalid signature or SSL cert is invalid.
Both projects are .Net Core. One is a web api and the other is a MVC project
Its actually 2 APIS and MVC (userAPI, TraderAPI and the UI)
User API
public string GenerateJWTToken(UserRoleVM user)
{
if (user != null)
{
var secretKey = Base64UrlEncoder.DecodeBytes(_config.GetSection("Tokens:Key").ToString());
var securityKey = new SymmetricSecurityKey(secretKey);
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
var currentTime = DateTime.Now;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName)
,new Claim(JwtRegisteredClaimNames.Sid, user.UserGUID)
,new Claim(JwtRegisteredClaimNames.NameId, user.UserID.ToString())
,new Claim(JwtRegisteredClaimNames.Email, user.Email)
,new Claim(JwtRegisteredClaimNames.AuthTime, currentTime.ToString())
,new Claim(ClaimTypes.Role, user.Role)
//,new Claim(JwtRegisteredClaimNames.)
,new Claim(type: "Subscribed", value: user.IsSubscribed.ToString())
,new Claim(type: "SubscribedDate", value: user.DateSubscribed.ToString())
};
var jwtSecurityToken = new JwtSecurityToken
(
signingCredentials: signingCredentials,
audience: _config.GetValue<string>("Tokens:Audience"),
issuer: _config.GetValue<string>("Tokens:Issuer"),
expires: currentTime.AddMinutes(_config.GetValue<int>("Tokens:LifeTime")),
//notBefore: currentTime,
claims: claims
);
return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
}
return null;
}
//public Task<LoginResponseVM> Login(string username, string password)
public async Task<string> Login(string username, string password)
{
string jwtToken = null;
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
{
try
{
var result = await _signInManager.PasswordSignInAsync(username, password, false, false);
if (result.Succeeded)
{
//var savedUser = await _userService.GetUserByNameAsync(username);
//var role = await _userService.GetUserRolesAsync(savedUser);
var userVM = await _userService.GetUserNRole2Async(username);
//jwtToken = Convert.ToBase64String(Encoding.ASCII.GetBytes(GenerateJWTToken(userVM)));
jwtToken = GenerateJWTToken(userVM);
}
}
catch (Exception ex)
{
}
}
return jwtToken;
}
TraderAPI
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpPost]
//public async Task<IActionResult> GetTradeStatusUpdate([FromBody] TradeResultVM model)
public async Task<IActionResult> SendTradeStatusUpdate([FromBody] TradeResultVM model)
{
_logger.LogInformation("Inside GetTradeStatus");
var lstTradeStatuses = await GetLstTradeStatuses();
if (lstTradeStatuses != null)
{
await _semaphoreSlimS.WaitAsync();
try
{
foreach (var activeTrade in lstTradeStatuses)
{
if (activeTrade.TradeID == model.TradeID)
{
_logger.LogInformation("Trade found in lstTradeStatuses");
...
}
[HttpPost("SendUITradeStatusV4")]
public async Task<IActionResult> SendUITradeStatusV4(BinanceStreamOrderUpdate orderUpdate, string token)
{
if (orderUpdate != null)
{
TradeResultVM tradeVM = new TradeResultVM
{
//TradeName = "Test",
TradeID = orderUpdate.OrderId,
TradeSymbol = orderUpdate.Symbol,
Price = orderUpdate.Price,
Quantity = orderUpdate.Quantity
};
string trade = JsonSerializer.Serialize(tradeVM);//
using var client = _httpClient.CreateClient("TraderUI");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
//client.BaseAddress = new Uri("https://localhost:50111");
//using var response = await client.PostAsync($"/HarkinsTrader/GetTradeStatusUpdate/",
_logger.LogInformation("Sending Key");
using var response = await client.PostAsync($"/HarkinsTrader/SendTradeStatusUpdate/",
new StringContent(trade, Encoding.UTF8, "application/json"));
if (response.IsSuccessStatusCode)
{
_logger.LogInformation("Success StreamStatus Update");
return Ok();
}
}
return BadRequest();
}

StatusCode: 405, ReasonPhrase: 'Method Not Allowed'

i'm building a Web Application to consume A REST FULL API ASP.Net Core Web Service, i have Problem when i update a records.
i'm trying to Call Put Method from API ASP.Net core by ASP.Net Core application
I have got an NullReferenceException: Object reference not set to an instance of an object.
public async Task<IActionResult> UpdateEmployee(int id)
{
Employee employee = new Employee();
using(var httpClient=new HttpClient())
{
using(var response = await httpClient.GetAsync("myURI/employee/" + id))
{
string apiResponse = await
response.Content.ReadAsStringAsync();
employee = JsonConvert.DeserializeObject<Employee>apiResponse);
}
}
return View(employee);
}
[HttpPost]
public async Task<IActionResult> UpdateEmployee(Employee employee)
{
Employee receivedemployee = new Employee();
using(var httpClient=new HttpClient())
{
var content = new MultipartFormDataContent();
content.Add(new
StringContent(employee.EmployeeId.ToString(),Encoding.UTF8,
"application/json"), "id");
content.Add(new
StringContent(employee.FirstName,Encoding.UTF8,
"application/json"),"FirstName");
content.Add(new StringContent(employee.LastName,
Encoding.UTF8, "application/json"), "LastName");
content.Add(new StringContent(employee.DateOfBirth.ToString(),
Encoding.UTF8, "application/json"), "Email");
content.Add(new StringContent(employee.PhoneNumber,
Encoding.UTF8, "application/json"), "DateOfBirth");
content.Add(new StringContent(employee.Email, Encoding.UTF8,
"application/json"), "Email");
using (var response = await httpClient.PutAsync("myURI/api/employee", content))
{
string apiResponse = await response.Content.ReadAsStringAsync();
ViewBag.Result = "Success";
receivedemployee = JsonConvert.DeserializeObject<Employee>(apiResponse);
}
return View(receivedemployee);
}
}
}
i expected updating a record
I cleaned up your code a bit. try it this way
I removed unnecessary using blocks and serialized your employee class with a single "application/json" encoding.
public async Task<IActionResult> UpdateEmployee(int id)
{
Employee employee = new Employee();
var httpClient = new HttpClient();
var request = new HttpRequestMessage
(HttpMethod.Get, $"myURI/employee/{id}");
var response = await httpClient.SendAsync(request);
string apiResponse = await response.Content.ReadAsStringAsync();
employee = JsonConvert.DeserializeObject<Employee>(apiResponse);
return View(employee);
}
[HttpPost]
public async Task<IActionResult> UpdateEmployee(Employee employee)
{
Employee receivedEmployee = new Employee();
var httpClient = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Put, $"myURI/employee/{employee.EmployeeId}")
{
Content = new StringContent(new JavaScriptSerializer().Serialize(employee), Encoding.UTF8, "application/json")
};
var response = await httpClient.SendAsync(request);
string apiResponse = await response.Content.ReadAsStringAsync();
ViewBag.Result = "Success";
receivedEmployee = JsonConvert.DeserializeObject<Employee>(apiResponse);
return View(receivedEmployee);
}

Not receiving access_token in three-legged oauth 2.0 flow in asp.net mvc (Blackboard Learn)

I have to implement three-legged authentication in ASP.NET MVC. I have followed the steps according to the Blackboard documentation, especially the link https://community.blackboard.com/docs/DOC-3976-three-legged-oauth
I have received authorization code by calling the REST API /learn/api/public/v1/oauth2/authorizationcode.After that according to the documentation (I followed the documentation exactly but I don't know what am I have been missing ), I built a POST request to /learn/api/public/v1/oauth2/token to get access_token but I am unable to get access_token.
Instead, access_token, I have been receiving a BadRequest. This means I am making a mistake to build my second request but I am unable to fix the problem. I haven't found any code sample in .NET to implement three legged authentication for Blackboard Learn. Could you please help me to resolve the issue?
This is my code to call both APIs to receive access_token.
public class HomeController : Controller
{
public ActionResult Index()
{
// GET /learn/api/public/v1/oauth2/authorizationcode
Guid stateId = Guid.NewGuid();
string applicationKey = "Application key goes here";
string redirectUrl = string.Format("https://Blackboard Learn URL goes here/learn/api/public/v1/oauth2/authorizationcode" +
"?redirect_uri=https://localhost:44300/Home/OAuth2Response&response_type=code&client_id={0}&scope=read&state={1}",
applicationKey, stateId);
Response.Redirect(redirectUrl, true);
return View();
}
public async Task<bool> OAuth2Response(string code = null, string state = null, string error = null, string error_description = null)
{
bool success = true;
string json = string.Empty;
string urlCommand = string.Format("/learn/api/public/v1/oauth2/token?code={0}&redirect_url=https://localhost:44300/Home/OAuth2Response", code);
try
{
using (HttpClient client = new HttpClient())
{
var endpoint = new Uri("Blackboard Learn URL goes here" + urlCommand);
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
HttpContent body = new FormUrlEncodedContent(postData);
// POST /learn/api/public/v1/oauth2/token
using (HttpResponseMessage response = await client.PostAsync(endpoint, body)) // Problem is here
{
if (response.IsSuccessStatusCode)
{
json = await response.Content.ReadAsStringAsync();
}
else
{
success = false;
}
}
}
}
catch (Exception err)
{
//hopefully we never end up here, log this exception for forensics
success = false;
}
return success;
}
}
NOTE: I can successfully receive an access_token in Postman tool.
Finally, the below code works perfectly for 3 legged authentications in ASP.NET MVC.
public class HomeController : Controller
{
//https://blackboard.jiveon.com/docs/DOC-3976-three-legged-oauth
public ActionResult Index()
{
// GET /learn/api/public/v1/oauth2/authorizationcode
Guid stateId = Guid.NewGuid();
string applicationKey = "Application key goes here";
string redirectUrl = string.Format("Blackboard Learn URL goes here/learn/api/public/v1/oauth2/authorizationcode" +
"?redirect_uri=https://localhost:44300/Home/OAuth2Response&response_type=code&client_id={0}&scope=read&state={1}",
applicationKey, stateId);
Response.Redirect(redirectUrl, true);
return View();
}
public async Task<bool> OAuth2Response(string code = null, string state = null, string error = null, string error_description = null)
{
bool success = true;
string json = string.Empty;
string urlCommand = string.Format("/learn/api/public/v1/oauth2/token?code={0}&redirect_uri=https://localhost:44300/Home/OAuth2Response", code);
try
{
using (HttpClient client = new HttpClient())
{
var endpoint = new Uri("Blackboard Learn URL goes here" + urlCommand);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes("client_id:client_secret")));
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
HttpContent body = new FormUrlEncodedContent(postData);
using (HttpResponseMessage response = await client.PostAsync(endpoint, body))
{
if (response.IsSuccessStatusCode)
{
json = await response.Content.ReadAsStringAsync();
dynamic oauth2Result = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
string access_token = oauth2Result.access_token;
string refresh_token = oauth2Result.refresh_token; }
else
{
success = false;
}
}
}
}
catch (Exception err) {
//hopefully we never end up here, log this exception for forensics
success = false;
}
return success;
}
}

Token generated outside controller is too long and it's rejected by ConfirmEmail on Controller in MVC C#

I am updating my question as I have made some progress.
Thanks in advance for your support.
Question:
I am using GenerateEmailConfirmationTokenAsync to create a token outside the Controller (it's working fine), but somehow my token is longer than the ones created within the Controller using the GenerateEmailConfirmationTokenAsync and therefore the ConfirmEmail action rejects the token. (Error: Invalid Token).
I have tried Machinekey on webconfig, HttpUtility.UrlEncode, but I am still stuck. How to sort out the Invalid Token error on Controller ConfirmEmail?
Guys, can you help me please!
Thanks.
Here is my Code:
RegisterUser (outside Controller)
public async Task RegisterUserAsync()
{
var store = new UserStore<ApplicationUser>(db);
var UserManager = new ApplicationUserManager(store);
var query = from c in db.Customer
where !(from o in db.Users
select o.customer_pk)
.Contains(c.customer_pk)
select c;
var model = query.ToList();
if (query != null)
{
foreach (var item in model)
{
var user = new ApplicationUser { UserName = item.email, Email = item.email, customerId = item.customerId};
var result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id);
SmtpClient client = new SmtpClient();
MailMessage message = new MailMessage
{
IsBodyHtml = true
};
message.Subject = "Confirm Email";
message.To.Add(item.email1);
message.Body = "Please confirm your account by clicking here";
client.SendAsync(message, "userToken");
//Assign Role User Here
await UserManager.AddToRoleAsync(user.Id, "Client");
}
}
}
}
SendEmailConfirmation method (outside Controller)
public async Task<string> SendEmailConfirmationTokenAsync(string userID)
{
var store = new UserStore<ApplicationUser>(db);
var UserManager = new ApplicationUserManager(store);
var url = new UrlHelper();
var provider = new DpapiDataProtectionProvider("MyApp");
UserManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(
provider.Create("EmailConfirmation"));
string code = await UserManager.GenerateEmailConfirmationTokenAsync(userID);
string encodedCode = HttpUtility.UrlEncode(code);
string callbackUrl = "http://localhost/Accounts/ConfirmEmail?userId=" + userID + "&code=" + encodedCode;
return callbackUrl;
}
where db is
ApplicationdDbContext db = new ApplicationdDbContext();
ConfirmEmail within the Identity Controller (Accounts Controller) - I've created Accounts instead of Account controller but it's working fine.
//
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var confirmed = await UserManager.IsEmailConfirmedAsync(userId);
if (confirmed)
{
return RedirectToLocal(userId);
}
var result = await UserManager.ConfirmEmailAsync(userId, code); //Here I get the error (Token Invlaid, despite the token and userId being displayed)
if (result.Succeeded)
{
ViewBag.userId = userId;
ViewBag.code = code;
}
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
[HttpPost]
[ValidateAntiForgeryToken]
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(SetPasswordViewModel model, string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
if (!ModelState.IsValid)
{
return View(model);
}
var result = await UserManager.AddPasswordAsync(userId, model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(userId);
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToLocal(userId);
}
ViewBag.userId = userId;
ViewBag.code = code;
AddErrors(result);
return View(model);
}
I have worked for hours in this code but until now I can't sort it out.
Thanks for any comments or solution. The reason for this approach is that I have to use task scheduler(I'm using fluentscheduler, which is working fine).

Resources