Using Cookies based authentication in WebAPI and asp.net core - c#

Scenario:
I have a solution, in which, i have both WebAPI and Asp.Net Core MVC Project. I have implemented Cookies based authentication in WebAPI. It's working great while testing using Postman. But when i consume the WebAPI Service from my MVC project, authentication seems to be broken.
Here's my code:
WebAPI:
Startup.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "ApiAuth",
AutomaticAuthenticate = true,
AutomaticChallenge = false
});
AccountController.cs
[HttpPost]
[Route("authenticate")]
public IActionResult Authenticate([FromBody]LoginModel login)
{
if (_accountManager.Authenticate(login))
{
var identity = new ClaimsIdentity("password");
identity.AddClaim(new Claim(ClaimTypes.Role, "User"));
HttpContext.Authentication.SignInAsync("ApiAuth", new ClaimsPrincipal(identity)).Wait();
}
else
{
return Unauthorized();
}
return Ok(_accountManager.Authenticate(login));
}
All Controllers have this attribute [Authorize(Roles = "User")]
MVC App:
AccountController.cs
public async Task<IActionResult> Login(LoginModel loginModel)
{
var loginFlag = false;
HttpResponseMessage response = await ServiceCall<LoginModel>.postData(URLPREFIX + "/authenticate", loginModel);
if (response.IsSuccessStatusCode)
{
loginFlag = await response.Content.ReadAsAsync<bool>();
}
if (loginFlag)
{
return RedirectToAction("Index", "Home");
}
else
{
return View();
}
}
ServiceCall.cs:
public static class ServiceCall<T>
{
static HttpClient client = new HttpClient();
const string HTTP_BASE = "http://localhost:13359/";
public static async Task<HttpResponseMessage> postData(string Url, T data)
{
HttpResponseMessage response = null;
StringContent content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
client.BaseAddress = new Uri(HTTP_BASE);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
response = await client.PostAsync(Url, content);
return response;
}
}
Here is my Screenshot:
The login function in both WebAPI and MVC is executing correctly, but when navigating to home page, i could not consume the service. Any Advice would be helpful. Thanks.
Update #1:
Here is my project repo with the issue. Please take a look. Thanks

i think problem is here:
HttpResponseMessage response = await ServiceCall<LoginModel>.postData(URLPREFIX + "/authenticate", loginModel);
if (response.IsSuccessStatusCode)
{
loginFlag = await response.Content.ReadAsAsync<bool>();
}
you are using a new request authenticate, this authenticate write a cookie in the response, of course not working on your real browser request.
you need using browser request the authenticate directly, let cookie write back to client, then your client can request home index.

Related

How can I successfully send http requests to my ASP.NET Core controller

I have built a .NET Core server that is linked up to a postgres database but what I'm attempting to do now is send requests to the server from a mobile app to execute the CRUD functions with the server. This is so the mobile can send requests that will be an object using a model that's setup in both server and mobile app to be the same. Then the server will POST that request to the database.
This code is on the mobile app that makes a baseAddress for the server and then the other code is inside a save method that turns the object into json string and presumably sends it to the controller.
private static readonly HttpClient sharedClient = new()
{
BaseAddress = new Uri("http://10.188.144.18:5240/AandEBacklog"),
};
using StringContent jsonContent = new(JsonSerializer.Serialize(new { note }),
Encoding.UTF8,
"application/json");
using HttpResponseMessage response = await sharedClient.PostAsync("MobileResponse", jsonContent);
I know that I need to do something with routing in the program.cs file in the server
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
I have a controller that connects to the database but don't know weather I can use the httpRequest in that controller or if I need to send it to another one and then the other controller handles it
using Microsoft.AspNetCore.Mvc;
namespace delamainServer.Controllers;
[ApiController]
[Route("[controller]")]
public class AandEBacklogController : ControllerBase
{
//CONNECTING TO DATABASE.
private readonly DataContext context;
//var httpRequest = HttpContext.Request;
public AandEBacklogController(DataContext context)
{
this.context = context;
}
//post method example to add entry
[HttpPost]
public async Task<ActionResult<List<AandEBacklog>>> Addemrgncy(AandEBacklog booking)
{
context.AandEBacklogs.Add(booking);
await context.SaveChangesAsync();
return Ok(await context.AandEBacklogs.ToListAsync());
}
}
Many thanks in advance
For client part:
var client = new HttpClient() { BaseAddress = new Uri("http://10.188.144.18:5240") };
var content = new StringContent("yourJsonString", Encoding.UTF8, "application/json");
var response = await client.PostAsync("/AandEBacklog", content);
var responseString = await response.Content.ReadAsStringAsync();
EDIT:
Let's setup simple GET endpoint:
[ApiController]
[Route("[controller]")]
public class TimeController : ControllerBase
{
public ActionResult<string> GetCurrentTime() => $"{DateTime.Now}";
}
Verify it works from browser (e.g. http://localhost:5000/Time).
If it works try client from console or Xamarin app:
var client = new HttpClient() { BaseAddress = new Uri("http://localhost:5000") };
var response = await client.GetAsync("/Time");
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);

.NET 6 API SignInAsync with cookie authentication is not being kept via front-end but works via Swagger

I'm trying to learn how to use authentication and SignInAsync via an API. For some reason, my code works only if I call the API from Postman or from Swagger, but not when I call it from code in the front-end.
To test this, I created two small projects: a web razor pages front end and a simple API. The API has the 3 possible POST requests (TestLogin, TestLogout and TestVerify) as show below:
[Route("api/[controller]")]
[ApiController]
[Authorize()]
public class LoginTestController : ControllerBase
{
[AllowAnonymous]
[HttpPost("TestLogin")]
public async Task<ActionResult> TestLoginAction([FromBody] LoginData loginData)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, loginData.UserName),
new Claim(ClaimTypes.Role, loginData.UserRole)
};
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
if (principal is not null && principal.Identity is not null)
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
return Ok("Successfully logged in.");
}
else
{
return BadRequest("Login failed.");
}
}
[HttpPost("TestVerify")]
public ActionResult TestVerify()
{
var user = User.Identity;
if (user is not null && user.IsAuthenticated)
return Ok($"Logged in user: " + user.Name);
else
return Ok("No user logged in. Please login.");
}
[HttpPost("TestLogout")]
public async Task<ActionResult> TestLogout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Ok();
}
}
In my Program.cs, I add the cookie authentication
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie();
and
app.UseAuthentication();
app.UseAuthorization();
In my front-end razor page, I called the APIs this way using this code:
public string Message { get; set; }
public async Task OnPostLogin()
{
var client = new HttpClient();
var userLoginInfo = new LoginData
{
UserName = "TestUser",
UserRole = "TestRole"
};
string api = "https://localhost:7049/api/LoginTest/TestLogin";
var response = await client.PostAsJsonAsync(api, userLoginInfo);
Message = await response.Content.ReadAsStringAsync();
}
public async Task OnPostLogout()
{
var client = new HttpClient();
var userLoginInfo = new LoginData();
string api = "https://localhost:7049/api/LoginTest/TestLogout";
var response = await client.PostAsJsonAsync(api, userLoginInfo);
Message = await response.Content.ReadAsStringAsync();
}
public async Task OnPostValidate()
{
var client = new HttpClient();
var userLoginInfo = new LoginData();
string api = "https://localhost:7049/api/LoginTest/TestVerify";
var response = await client.PostAsJsonAsync(api, userLoginInfo);
int statusCode = ((int)response.StatusCode);
if (response.IsSuccessStatusCode)
Message = await response.Content.ReadAsStringAsync();
else
Message = $"Failed with status code: ({response.StatusCode}/{statusCode})";
}
If I try via the front-end, I click on the Login, and it says it is successfully, but when I click on the Validate, it returns a 404 presumably because it doesn't think it is authorized even though I did a log in (or if I authorize anonymous for the validate, it says no one is logged in). If I do the same calls via Swagger, the login works the same, but the validate remembers the login, which is the desired behavior. Why is the login not being remembered when I call the API via the front-end, but it is when I do it via Swagger? And how can I fix this?
Thanks.

c# asp.net website calling a webApi

I meet a problem because of my inexperience managing Threads.
I have this Action bellow :
public static async Task<joueurs> loadjoueurs(int id)
{
joueurs EmpInfo = new joueurs();
using (var client = new HttpClient())
{
//Passing service base url
client.BaseAddress = new Uri("http://www.myWebApi.fr/api/");
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Sending request to find web api REST service resource GetAllEmployees using HttpClient
HttpResponseMessage Res = await client.GetAsync("joueurs?id=" + id);
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
var EmpResponse = Res.Content.ReadAsStringAsync().Result;
//Deserializing the response recieved from web api and storing into the Employee list
EmpInfo = JsonConvert.DeserializeObject<joueurs>(EmpResponse);
return EmpInfo;
}
return null;
}
it s just client to get my data from a webApi (no ssl no authentication, when I test it I receive the right values)
but when I make a call using the function above (in my asp.net website) .... it stay stucked at the HttpResponseMessage = await .... eternally.
In my webApi I have two functions same name but different parameters .
public async Task<IHttpActionResult> Getjoueur(int iduser, int idsport)
and
public async Task<IHttpActionResult> Getjoueur(int id)
So I am don't know where the problem comes from.
(sequel) Here is the place where I call the Task :
public SuperModel(int id)
{
this.joueur = Repojoueurs.loadjoueurs(id).Result;
/* this.classificationSport = Repoclassificationsport.loadclassificationsport().Result;
...
*/
}
And then my Supermodel is instantiated here in my Home controller :
public ActionResult Index(int id)
{
SuperModel superModel = new SuperModel(id);
return View(superModel);
}
Can you try not to use the async and wait. Around three changes like below
public static HttpResponseMessage loadjoueurs(int id)
{
HttpResponseMessage Res = client.GetAsync("joueurs?id=" + id);
return Request.CreateResponse(HttpStatusCode.OK,EmpInfo, "application/json");
}

Redirecting action from one controller to other with different secure attributes, ASP.NETCore 2.0

I have one question, which connected with redirecting and auth policies.
Let's have one controller, which allow Anonymous method like this:
[Route("Authorization")]
[Authorize]
public class AuthorizationController : Controller
{
...
[HttpPost]
[Route("AddUser")]
[AllowAnonymous]
public async Task<IActionResult> AddUser()
{
return await Task.Run<ActionResult>(() =>
{
return RedirectToAction("Post", "Proxy");
});
}
}
Second controller has Post method, which needs authorization
[Authorize]
public class ProxyController : Controller
{
...
[HttpPost]
public async Task Post()
{
var uri = new Uri(UriHelper.GetEncodedUrl(Request));
var routedUri = NewRouteBuilder(uri);
var client = new HttpClient();
var response = await client.PostAsync(routedUri, new StreamContent(Request.Body));
var content = await response.Content.ReadAsStringAsync();
Response.StatusCode = (int)response.StatusCode;
Response.ContentType = response.Content.Headers.ContentType?.ToString();
Response.ContentLength = response.Content.Headers.ContentLength;
await Response.WriteAsync(content);
}
}
If I use this code, I get 401 error in AuthorizationController, when I call AddUser.
Both these controllers are in one project. How it's possible to redirect on action in this case (which allow pass to action only authorized users or calls from ProxyController)?
Thank you.

How to call POST method from API using HttpClient?

I am trying to use the HttpClient class to call a POST method from my API which is supposed to add Server information to a DB.
The way I'm trying to call it is below. But when I step through it to debug it steps through my if statement for response.IsSuccessStatusCode.
public static async Task<Server> PostServer(Server server)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:50489/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
StringContent content = new StringContent(JsonConvert.SerializeObject(server));
// HTTP POST
HttpResponseMessage response = await client.PostAsync("api/Server/", content);
if (response.IsSuccessStatusCode)
{
string data = await response.Content.ReadAsStringAsync();
server = JsonConvert.DeserializeObject<Server>(data);
}
}
return server;
}
Also here is the POST method in my API below it was automatically generated in VS.
// POST: api/Servers
[ResponseType(typeof(Server))]
public async Task<IHttpActionResult> PostServer(Server server)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Servers.Add(server);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = server.Server_ID }, server);
}

Categories