Server Not Finding Callback Url - c#

I'm trying to implement OAuth2 with Fitbit in my WebAPI applications. I'm able to make the initial request to the fitbit api. But when it comes back the the server I'm getting a error saying that I cannot find the callback url
OAuthController
[HttpPost]
public async Task<HttpResponseMessage> Authorize(UserAuthRequestDTO request)
{
if (string.IsNullOrEmpty(request.PatientID) || string.IsNullOrEmpty(request.Provider))
Request.CreateResponse(HttpStatusCode.BadRequest, ErrorLookup.GetErrorMessage("invalid_input"), Configuration.Formatters.JsonFormatter);
var userId = User.Identity.GetUserId();
if (userId == null)
return Request.CreateResponse(HttpStatusCode.BadRequest, ErrorLookup.GetErrorMessage("invalid_token"), Configuration.Formatters.JsonFormatter);
var accUser = await GetUserById(userId);
_currentUser = AccountUtils.GetOrgAndUserInfo(accUser);
var callbackUrl = $"{Request.RequestUri.GetLeftPart(UriPartial.Authority)}/oauth2/callback";
IOAuthHandler handler;
switch (request.Provider)
{
case "Fitbit":
handler = new FitbitHandler(callbackUrl);
break;
case "Withings":
handler = new WithingsHandler(callbackUrl);
break;
default:
return Request.CreateResponse(HttpStatusCode.BadRequest, ErrorLookup.GetErrorMessage("invalid_input"), Configuration.Formatters.JsonFormatter);
}
var authorizationUrl = handler.RequestUserAuthorizationUrl(request.PatientID,_currentUser.Org);
return Request.CreateResponse(HttpStatusCode.OK, authorizationUrl);
}
[HttpPost]
public async Task<HttpResponseMessage> Callback(UserAuthDTO request)
{
if (string.IsNullOrEmpty(request.PatientID))
Request.CreateResponse(HttpStatusCode.BadRequest, ErrorLookup.GetErrorMessage("invalid_input"), Configuration.Formatters.JsonFormatter);
var userId = User.Identity.GetUserId();
if (userId == null)

You are setting the callback url to
...oauth2/callback?code=3aa6e9e....
But in your action, your route is
...api/oauth2/callback
You are missing "api" in your definition

Related

How to pass Data from action to action using mvc and not in router

i have an action inside of users and i want that action to return the user to another action in another controller but not in the router parameter, Here is a sample
public IActionResult LoginCheck(UserForm user)
{
AuthUser auth = new AuthUser(_context);
var result = auth.IsLoggedIn(user.Email, user.Password);
if(result.isfound==false)
{
return NotFound();
}
result.User.IsAuth = true;
return RedirectToAction("Home","Index",result.User);
}
public async Task<IActionResult> Index(User user)
{
if(user.IsAuth == false)
{
return Unauthorized();
}
just part of the code
Home index did not use the incoming user as it was sent as router parameters i think
Welcome to stackoverflow!
You can use TempData to achieve that.
public IActionResult LoginCheck(UserForm user)
{
AuthUser auth = new AuthUser(_context);
var result = auth.IsLoggedIn(user.Email, user.Password);
if(result.isfound==false)
{
return NotFound();
}
result.User.IsAuth = true;
TempData["user"] = result.User;
return RedirectToAction("Home","Index");
}
Then you can get it in the other action
public async Task<IActionResult> Index()
{
if(TempData["user"] == null)
{
return Unauthorized();
}else{
var someUser= (User)TempData["user"];
}
}
But I do not recommend using TempData for sensitive data.
You can use second action as method:
public async Task<IActionResult> LoginCheck(UserForm user)
{
AuthUser auth = new AuthUser(_context);
var result = auth.IsLoggedIn(user.Email, user.Password);
if(result.isfound==false)
{
return NotFound();
}
result.User.IsAuth = true;
return await Index(result.User);
}
second action
[NonAction] // Indicates that a controller method is not an action method.
public async Task<IActionResult> Index(User user)
{
if(user == null)
{
return Unauthorized();
}
else{
var someUser= user;
}
}
Use redirection make browser to handle this transfer and it is slower.

Why is my HttpResponseExceptionFilter not working with synchronized action on NET.Core?

I've implemented a HttpResponseExceptionFilter in Asp.Net Core Web Api to handle errors and format them in a fixed format json string.Here is my code:
public class HttpResponseExceptionFilter : IAsyncExceptionFilter
{
private readonly ILogger<HttpResponseExceptionFilter> _logger;
public HttpResponseExceptionFilter(ILogger<HttpResponseExceptionFilter> logger)
{
_logger = logger;
}
public Task OnExceptionAsync(ExceptionContext context)
{
if (context.Exception == null)
return Task.CompletedTask;
switch (context.Exception)
{
case BadHttpRequestException ex:
context.Result = new JsonResult(new BaseResponse<object?>()
{
StatusCode = ex.StatusCode,
Message = ex.Message,
Data = null
})
{
StatusCode = ex.StatusCode
};
break;
default:
_logger.LogError(context.Exception, "error");
context.Result = new JsonResult(new BaseResponse<object?>()
{
StatusCode = 500,
Message = context.Exception.Message,
Data = null
})
{
StatusCode = 500
};
break;
}
context.ExceptionHandled = true;
return Task.CompletedTask;
}
}
and add it in program.cs:
builder.Services.AddControllers(options =>
{
options.Filters.Add<HttpResponseExceptionFilter>();
});
then I throw an exception in controller action(for example):
[HttpPost]
[Route("Register")]
public async Task<IActionResult> Register(UserRegisterRequest registerRequest)
{
throw new BadHttpRequestException("error details", 500);
User user = await _userService.Register(registerRequest);
return JsonResult(user);
}
[HttpPost]
[Route("Login")]
public IActionResult Login(string account, string password)
{
throw new BadHttpRequestException("error details", 500);
string token = _userService.Login(account, password);
return JsonResult(new LoginResponse() { Token = token });
}
then, it will work with Register method but doesn't work with Login method and if I convert Login method into public async Task<IActionResult> Login(string account, string password) it works.
So are there reasons to explain this?And how can I make it work with synchronized action?
By the way I've tried using UseExceptionHandler and it performs as the same way.

How To Pass ErrorMessage From API to MVC In Asp.Net Core

I've created an API. When you face an error, It shows you the type of error with it's message. But When I try to use that API in my MVC project, It just shows the type of error. I want to see the message in Modelstate.AddModelError
Here is API controller for Login
[HttpPost("login")]
public async Task<IActionResult> LoginUser([FromBody] UserDtoLogin user)
{
var userToRetrieve = await _applicationDbContext.Users.FirstOrDefaultAsync(u => u.UserName == user.UserName);
if (userToRetrieve == null)
{
ModelState.AddModelError("username", "Such a user doesn't exists! Enter the correct username please");
return NotFound(ModelState);
}
if (!_userRepository.VerifyPasswordHash(user.Password, userToRetrieve.PasswordHash, userToRetrieve.PasswordSalt))
{
ModelState.AddModelError("password", "Wrong Password!");
return BadRequest(ModelState);
}
await _userRepository.Login(userToRetrieve);
return Ok(user);
}
Here is MVC Controller for Login
[HttpPost]
public async Task<IActionResult> Login(User user)
{
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:42045/api/user/login");
if (user != null)
{
request.Content = new StringContent(JsonConvert.SerializeObject(user),
System.Text.Encoding.UTF8, "application/json");
}
var client = _clientFactory.CreateClient();
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
ViewBag.StatusCode = System.Net.HttpStatusCode.OK;
var apiString = await response.Content.ReadAsStringAsync();
user = JsonConvert.DeserializeObject<User>(apiString);
}
else
{
ViewBag.StatusCode = response.StatusCode;
}
return View(user);
}
I write a simple demo to show how to pass ErrorMessage From API to MVC In Asp.Net Core, you can reference to it.
API
[Route("api/[controller]")]
[ApiController]
public class LoginController : ControllerBase
{
//For testing convenience, I use hard code here
List<UserDtoLogin> context = new List<UserDtoLogin>
{
new UserDtoLogin
{
UserName = "Mike"
},
new UserDtoLogin
{
UserName = "Jack"
},
new UserDtoLogin
{
UserName = "Lily"
}
};
[HttpPost("login")]
public IActionResult LoginUser([FromBody] UserDtoLogin user)
{
var userToRetrieve = context.FirstOrDefault(u => u.UserName == user.UserName);
if (userToRetrieve == null)
{
return BadRequest("Such a user doesn't exists! Enter the correct username please");
}
//your logic.....
return Ok();
}
}
MVC/Controller
[HttpPost]
public async Task<IActionResult> Privacy(UserDtoLogin todoItem)
{
var todoItemJson = new StringContent(JsonSerializer.Serialize(todoItem),Encoding.UTF8,Application.Json);
using var httpResponseMessage = await _httpClient.PostAsync("your api url", todoItemJson);
var errormessage = httpResponseMessage.Content.ReadAsStringAsync().Result;
return View(todoItem);
}
Then you can see it can receive the errormessage successfully.

Controller refuses to return 404

How do I force my controller to return a 404?
[HttpGet]
[Route("account({accountid})/printgroup", Name = "Get")]
public HttpResponseMessage Get(int accountid)
{
var query = Request.RequestUri.Query;
var uri = new Uri(Client.Instance.BaseAddress.ToString().Replace("[accountid]", accountid.ToString()) + query);
var request = new HttpRequestMessage {RequestUri = uri, Method = HttpMethod.Get};
var clientResponse = Client.Instance.SendAsync(request).Result;
return clientResponse;
}
When the clientResponse is 404, the method does not return a response. It just times out.
What am I doing wrong? How do I force it to return a 404 if the client response is 404?
EDIT:
Per macceturra's helpful comments, I've created my own object:
Yet the behavior is the same! The method will go out of scope, yet the client does not see anything. It just times out.
My latest update is:
[HttpGet]
[Route("account({accountid})/bill({billingRunId})", Name = "GetInvoiceSummary")]
public async Task<IHttpActionResult> GetInvoiceSummary(int accountid, int billingRunId)
{
var query = Request.RequestUri.Query;
var uri = new Uri(Client.Instance.BaseAddress.ToString() + accountid + "/" + billingRunId + query);
var request = new HttpRequestMessage { RequestUri = uri, Method = HttpMethod.Get };
var response = await Client.Instance.SendAsync(request);
if (response.StatusCode == HttpStatusCode.NotFound)
return NotFound();
return ResponseMessage(response);
}
The behavior is once again the same.
Update
Based on discussion in comments it was advised that you review the response pipeline as it was determined that the code in the action was handling/executing the request as intended. The issue was being encountered by the response on its way out the pipeline which cause the client to time out. Check your message handlers and middle-ware in the pipeline that may be causing the response to block on its way out the pipeline based on how it was handled by your controllers.
Original answer
Consider the following, using the preferred Web API 2.* syntax along with proper use of async/await.
[HttpGet]
[Route("account({accountid})/printgroup", Name = "Get")]
public async Task<IHttpActionResult> Get(int accountid) {
var query = Request.RequestUri.Query;
var uri = new Uri(Client.Instance.BaseAddress.ToString().Replace("[accountid]", accountid.ToString()) + query);
var request = new HttpRequestMessage { RequestUri = uri, Method = HttpMethod.Get };
var clientResponse = await Client.Instance.SendAsync(request);
if (clientResponse.StatusCode == System.Net.HttpStatusCode.NotFound)
return NotFound();
return ResponseMessage(clientResponse);
}
After adding this piece of middleware, the problem has been resolved:
public class NotFoundMiddleware : OwinMiddleware
{
public NotFoundMiddleware(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
await Next.Invoke(context);
if (context.Response.StatusCode == (int)HttpStatusCode.NotFound
&& !context.Response.Headers.ContainsKey("X-ServiceFabric")
)
context.Response.Headers.Add("X-ServiceFabric", new[] { "ResourceNotFound" });
}
}

return errors from web api 2 account register

I am trying to use the account controller in the MVC project to call the register account method in a WebApi 2 account controller. All works fine but I cant figure out how to return errors back to the MVC project such as: "password must contain Upper case and lower case" etc.
ASP.NET MVC account controller register:
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var response =
await
ApiRequest.PostAsync(String.Format("{0}/api/v1/account/register", "http://localhost:12345"), model);
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index", "Home");
}
// Add errors
}
// If we got this far, something failed, redisplay form
return View(model);
}
ApiRequest class:
public static class ApiRequest
{
public static async Task<HttpResponseMessage> PostAsync(string uri, object item)
{
StringContent content = new StringContent(await Json.SerializeAsync(item));
content.Headers.ContentType = new MediaTypeHeaderValue("text/json");
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await client.PostAsync(new Uri(uri), content);
}
}
public static async Task<HttpResponseMessage> GetAsync(string uri)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await client.GetAsync(new Uri(uri));
}
}
public static async Task<HttpResponseMessage> PutAsync(string uri, object item)
{
StringContent content = new StringContent(await Json.SerializeAsync(item));
content.Headers.ContentType = new MediaTypeHeaderValue("text/json");
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await client.PutAsync(new Uri(uri), content);
}
}
public static async Task<HttpResponseMessage> DeleteAsync(string uri, object id)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await client.DeleteAsync(new Uri(String.Format("{0}/{1}", uri, id)));
}
}
}
public static class HttpResponseMessageExtensions
{
public static async Task<T> DeserialiseContentAsync<T>(this HttpResponseMessage message)
where T : class
{
return await Json.DeserialiseAsync<T>(await message.Content.ReadAsStringAsync());
}
}
Web API 2 account controller register:
//
// POST: /Account/Register
[AllowAnonymous]
[Route("Register")]
[HttpPost]
public async Task<IHttpActionResult> Register(RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser
{
UserName = model.Username,
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName
};
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
if (result.Errors != null)
{
foreach (string error in result.Errors)
{
ModelState.AddModelError("", error);
}
return BadRequest(ModelState);
}
return BadRequest();
}
// Send email verification
//string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
//var callbackUrl = new Uri(Url.Link("ConfirmEmail", new { userId = user.Id, code = code }));
//await
// UserManager.SendEmailAsync(user.Id, "Confirm your account",
// "Please confirm your account by clicking here");
Uri locationHeader = new Uri(Url.Link("GetUserById", new { id = user.Id }));
return Created(locationHeader, user);
}
GetErrorResult code:
private IHttpActionResult GetErrorResult(IdentityResult result)
{
if (result == null)
{
return InternalServerError();
}
if (!result.Succeeded)
{
if (result.Errors != null)
{
AddErrors(result);
}
if (ModelState.IsValid)
{
// No ModelState errors are available to send, so just return an empty BadRequest.
return BadRequest();
}
return BadRequest(ModelState);
}
return null;
}
private void AddErrors(IdentityResult result)
{
foreach (string error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
Im new to MVC and WebApi and have mainly been following tutorials, this is assume is basic stuff but i cant find a solution anywhere. I have separated the WebApi from the project intentionally so I can learn how these processes work better.
I'd like the solution not to be in javascript.
I'm assuming all subsequent requests will need to have a bearer token attached to the httpclient but i think this will be in another question.
Thanks in advance for any help on this
All works fine but I cant figure out how to return errors back to the MVC project such as: "password must contain Upper case and lower case" etc.
You can send custom message in BadRequest() method overload. When you perform custom validation on Model just return a custom message.
if (ValidatePasswordPolicy(model.Password))
{
return BadRequest("password must contain Upper case and lower case.");
}
Update:
There's a difference in ModelState validation of Asp.net mvc app and WebAPI. When you're using ModelState to put errors in WebAPI then you have to properly handle the response. Proper handling means the response is succeeded or not. If not then are there ModelState errors or other error. Below snippet will show you how to deserialize the error to anonymous object and check if there are model state errors or other error. In case of ModelState errors you can simply add them to your Asp.net mvc Model and return the view with model to update UI with errors.
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsAsync<T>();
}
else
{
var httpErrorObject = await response.Content.ReadAsStringAsync();
var anonymousErrorObject =
new { message = "", ModelState = new Dictionary<string, string[]>() };
// Deserialize:
var deserializedErrorObject =
JsonConvert.DeserializeAnonymousType(httpErrorObject, anonymousErrorObject);
// Check if there are actually model errors
if (deserializedErrorObject.ModelState != null)
{
var errors =
deserializedErrorObject.ModelState
.Select(kvp => string.Join(". ", kvp.Value));
for (int i = 0; i < errors.Count(); i++)
{
// Add errors to ModelState in Asp.net mvc app
}
}
// Othertimes, there may not be Model Errors:
else
{
var error =
JsonConvert.DeserializeObject<Dictionary<string, string>>(httpErrorObject);
foreach (var kvp in error)
{
// Now this is not model error so you can throw exception
// or any custom action what ever you like
}
}
}
There's a detailed article about handling errors from WebAPI in Asp.net mvc App here.

Categories