Calling WebApi from webforms looses authentication - c#

I am trying to upgrade some parts of an old project, and ran across the issue of calling the webapi from the aspx.cs.
When i call the webapi from the htmlpage/angularjs, the request is authenticated and everything works fine.
When i try to call it from the aspx page, even though this.User is Authenticated(on the aspx page), when the call is recieved in the controller, the user is not set and therefor not authenticated.
We are using owin cookie based authentication.
On framework 4.7.
Is there any way to authenticate the webapi call, by include the RVT cookie or something?
the call:
protected void Page_Load(object sender, EventArgs e)
{
var accessList = GetAccessList();
}
private List<ClientAccessEntityWithStatisticsDto> GetAccessList()
{
string baseUrl = Request.Url.GetLeftPart(UriPartial.Authority);
ApiHelper.InitializeClient(baseUrl);
string apiUrl = ApiHelper.ApiClient.BaseAddress + "/client/api/ClientAccess/ClientsUserCanAccessWithStatistics/";
var task = Task.Run(() => ApiHelper.ApiClient.GetAsync(apiUrl));
task.Wait();
return task.Result.Content.ReadAsAsync<List<ClientAccessEntityWithStatisticsDto>>().Result;
}
}
The helper:
public class ApiHelper
{
public static HttpClient ApiClient { get; set; }
public static void InitializeClient(string baseUrl)
{
ApiClient = new HttpClient()
{
BaseAddress = new Uri(baseUrl)
};
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}

Anyone coming across this.
Just parse all the request cookies to the api call, and it works like a charm.

Related

OAuth 2.0 REST Web API and xamarin forms

I make webservice api based on this tutorial https://www.c-sharpcorner.com/article/asp-net-mvc-oauth-2-0-rest-web-api-authorization-using-database-first-approach/ and I need to consume the service form xamarin forms. But I don't know how to authorize client.
Before you try authorizing in code, you should try talking to your API via an api client such as Postman.
You can see in step 11 of the article you reference that the writer is infact doing this.
He is performing the following steps:
Calling the token endpoint (no auth)
Adding the token to his subsequent requests
In order to call an API with authorization, you must first know the auth method (basic, OAuth etc). In this case you're saying it's OAuth:
Take a look at the guide, it shares this picture:
To do this in code you will need to add the following header to your http client. Lets assume you're using vanilla System.Net.Http.HttpClient you would need to implement a class that looks something like this:
public class APIClient
{
private HttpClient _client;
public APIClient()
{
_client = SetupClient();
}
private HttpClient SetupClient()
{
//setup your client here
var client = new HttpClient();
//string oauthToken = TokenService.GetUserToken();
string oauthToken = "eyJhbGciO......."; //Example token
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {oauthToken}");
//more setup here
return client;
}
public async Task<HttpResponseMessage> Get(string endpoint)
{
var request = new HttpRequestMessage(HttpMethod.Get, endpoint);
return await CallAsync(request);
}
private async Task<HttpResponseMessage> CallAsync(HttpRequestMessage request)
{
//do things before?
var result = await _client.SendAsync(request);
//handle result? null? not success code?
return result;
}
}
When you initialise your HttpClient you should add the following header:
Authorization : Bearer {yourtoken}
Now subsequent api requests will have authorization from your api client. How you set this bearer value is up to you. Some people store credentials in the xamarin main App class, and then retrieve the property. Other will persist the data into a plist and have the apiclient read this value (maybe credentials expire every 30 days).
Regardless there are a number of pitfalls that come with talking to api's from a xamarin app. You should always start by calling your api from outside of your app, from within an api client. This will teach you how to configure the requests correctly, without the overhead of worrying if your code/configuration is correct.
Please check my class if help you
`public class ServicesClient
{
private HttpClient httpClient;
private bool _IsConnection { get { return CheckInternet(); } }
public bool IsConnection { get { return _IsConnection; } }
public ServicesClient()
{
httpClient = new HttpClient(new HttpClientHandler());
//You can change the key as you need and add value
httpClient.DefaultRequestHeaders.Add("key", "000000");
}
//Get Method
public async Task<T> GetAsync<T>(string URL) where T : class
{
if (IsConnection)
{
var result = await httpClient.GetStringAsync(URL);
if (!string.IsNullOrEmpty(result))
return JsonConvert.DeserializeObject<T>(result);
else
return null;
}
return null;
}
//Post Method
public async Task<T> PostAsync<T>(string URL, object param) where T : class
{
if (IsConnection)
{
var json = JsonConvert.SerializeObject(param);
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
var result = await httpClient.PostAsync(URL, httpContent);
if (result.IsSuccessStatusCode)
return JsonConvert.DeserializeObject<T>(result.Content.ReadAsStringAsync().Result);
}
return null;
}
bool CheckInternet()
{
return Connectivity.NetworkAccess == NetworkAccess.Internet;
}
}
}`

Blazor service injection not working properly

I have this code in Blazor 3.0.0-preview4-19216-03 targeting a client app:
namespace BlazorShared.Services
{
public interface ILogin
{
Task<string> Login();
}
public class LoginService : ILogin
{
private HttpClient _client;
public LoginService(HttpClient client)
{
_client = client;
}
public async Task<string> Login()
{
var myclient = new HttpClient();
var responseMessage = await myclient.GetAsync("http://www.google.es");
var content = await responseMessage.Content.ReadAsStringAsync();
Debug.WriteLine(content);
return content;
}
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ILogin, LoginService>();
}
public void Configure(IComponentsApplicationBuilder app)
{
app.AddComponent<App>("app");
}
}
and this HTML
#functions {
public async Task Submit()
{
var str = await LoginService.Login(null, null, null);
Console.WriteLine(str);
}
}
Full Razor file: https://pastebin.com/3LbQQvk0
I have tested it and the web request never gets done and I'm not able to show the service response in the client. I have tried debugging in chrome following the instructions And I see the service method is being called but the constructor of the service is not, and as I understood Blazor should inject the HttpClient. Any ideas what can be happening? Thanks.
The following is wrong even if it is not the culprit...
In the LoginService you define an HttpClient variable into which you assign an instance of HttpClient provided by DI. On the other hand, you define a new HttpClient object named myclient and use it in the Login method.
You should use the object provided by DI. You shouldn't yourself define HttpClient objects. Why ? Because Blazor configure for you the HttpClient it creates. For instance, setting the Document base URI, which enable your web app to be navigated as an SPA app.
Hope this helps...

JWT doesn't get stored in ASP.NET Core with Blazor

I followed this tutorial: https://medium.com/#st.mas29/microsoft-blazor-web-api-with-jwt-authentication-part-1-f33a44abab9d
I downloaded the example: https://github.com/StuwiiDev/DotnetCoreJwtAuthentication/tree/Part2
I can see that the token is created but I don't understand how it is or should be saved on the client side as each time I access the SampleDataController, which has the Authorize tag, it returns a 401.
When calling and adding the token using Postman it works.
What am I missing for my user to be authenticated? Doesn't Microsoft.AspNetCore.Authentication.JwtBearer handle the client part (storing the token)?
What am I missing for my user to be authenticated? Doesn't Microsoft.AspNetCore.Authentication.JwtBearer handle the client part (storing the token)?
The JwtBearer runs on server side , it will only validate the authorization header of request, namely Authorization: Bearer your_access_token, and won't care about how you WebAssembly codes runs . So you need send the request with a jwt accessToken . Since the tutorial suggests you should use localStorage , let's store the accessToken with localStorage .
Because WebAssembly has no access to BOM yet, we need some javascript codes served as glue . To do that, add a helper.js under the JwtAuthentication.Client/wwwroot/js/ :
var wasmHelper = {};
wasmHelper.ACCESS_TOKEN_KEY ="__access_token__";
wasmHelper.saveAccessToken = function (tokenStr) {
localStorage.setItem(wasmHelper.ACCESS_TOKEN_KEY,tokenStr);
};
wasmHelper.getAccessToken = function () {
return localStorage.getItem(wasmHelper.ACCESS_TOKEN_KEY);
};
And reference the script in your JwtAuthentication.Client/wwwroot/index.html
<body>
<app>Loading...</app>
<script src="js/helper.js"></script>
<script src="_framework/blazor.webassembly.js"></script>
</body>
Now, let's wrap the javascript codes into C# . Create a new file Client/Services/TokenService.cs:
public class TokenService
{
public Task SaveAccessToken(string accessToken) {
return JSRuntime.Current.InvokeAsync<object>("wasmHelper.saveAccessToken",accessToken);
}
public Task<string> GetAccessToken() {
return JSRuntime.Current.InvokeAsync<string>("wasmHelper.getAccessToken");
}
}
Register this service by :
// file: Startup.cs
services.AddSingleton<TokenService>(myTokenService);
And now we can inject the TokenService into Login.cshtml and use it to save token :
#using JwtAuthentication.Client.Services
// ...
#page "/login"
// ...
#inject TokenService tokenService
// ...
#functions {
public string Email { get; set; } = "";
public string Password { get; set; } = "";
public string Token { get; set; } = "";
/// <summary>
/// response from server
/// </summary>
private class TokenResponse{
public string Token;
}
private async Task SubmitForm()
{
var vm = new TokenViewModel
{
Email = Email,
Password = Password
};
var response = await Http.PostJsonAsync<TokenResponse>("http://localhost:57778/api/Token", vm);
await tokenService.SaveAccessToken(response.Token);
}
}
Let's say you want to send data within FetchData.cshtml
#functions {
WeatherForecast[] forecasts;
protected override async Task OnInitAsync()
{
var token = await tokenService.GetAccessToken();
Http.DefaultRequestHeaders.Add("Authorization",String.Format("Bearer {0} ",token));
forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts");
}
}
and the result will be :
Apologies in advance as this is somewhat responding to a previous answer, but I don't have the rep to comment on that.
If it helps anyone else who was similarly looking for a solution to using JWT in a Blazor app, I found #itminus answer incredibly useful, but it also pointed me to another course.
One problem I found was that calling FetchData.cshtml a second time would blow up when it tries to add the Authorization header a second time.
Instead of adding the default header there, I added it to the HttpClient singleton after a successful login (which I believe Blazor creates for you automatically). So changing SubmitForm in Login.cshtml from #itminus' answer.
protected async Task SubmitForm()
{
// Remove any existing Authorization headers
Http.DefaultRequestHeaders.Remove("Authorization");
TokenViewModel vm = new TokenViewModel()
{
Email = Email,
Password = Password
};
TokenResponse response = await Http.PostJsonAsync<TokenResponse>("api/Token/Login", vm);
// Now add the token to the Http singleton
Http.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0} ", response.Token));
}
Then I realised, than as I'm building a SPA, so I didn't need to persist the token across requests at all - it's just in attached to the HttpClient.
The following class handle the login process on the client, storing the JWT token in local storage. Note: It is the developer responsibility to store the JWT token, and passes it to the server. The client (Blazor, Angular, etc.) does not do that for him automatically.
public class SignInManager
{
// Receive 'http' instance from DI
private readonly HttpClient http;
public SignInManager(HttpClient http)
{
this.http = http;
}
[Inject]
protected LocalStorage localStorage;
public bool IsAuthenticated()
{
var token = localStorage.GetItem<string>("token");
return (token != null);
}
public string getToken()
{
return localStorage.GetItem<string>("token");
}
public void Clear()
{
localStorage.Clear();
}
// model.Email, model.Password, model.RememberMe, lockoutOnFailure: false
public async Task<bool> PasswordSignInAsync(LoginViewModel model)
{
SearchInProgress = true;
NotifyStateChanged();
var result = await http.PostJsonAsync<Object>("/api/Account", model);
if (result)// result.Succeeded
{
_logger.LogInformation("User logged in.");
// Save the JWT token in the LocalStorage
// https://github.com/BlazorExtensions/Storage
await localStorage.SetItem<Object>("token", result);
// Returns true to indicate the user has been logged in and the JWT token
// is saved on the user browser
return true;
}
}
}
// This is how you call your Web API, sending it the JWT token for // the current user
public async Task<IList<Profile>> GetProfiles()
{
SearchInProgress = true;
NotifyStateChanged();
var token = signInManager.getToken();
if (token == null) {
throw new ArgumentNullException(nameof(AppState)); //"No token";
}
this.http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// .set('Content-Type', 'application/json')
// this.http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
Profiles = await this.http.GetJsonAsync<Profile[]>("/api/Profiles");
SearchInProgress = false;
NotifyStateChanged();
}
// You also have to set the Startup class on the client as follows:
public void ConfigureServices(IServiceCollection services)
{
// Add Blazor.Extensions.Storage
// Both SessionStorage and LocalStorage are registered
// https://github.com/BlazorExtensions/Storage
**services.AddStorage();**
...
}
// Generally speaking this is what you've got to do on the client. // On the server, you've got to have a method, say in the Account controller, whose function is to generate the JWT token, you've to configure the JWT middleware, to annotate your controllers with the necessary attribute, as for instance:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
and so on...
Hope this helps...

Displaying result in console instead of web page

The project I have created, is an ASP.NET Web API which communicates with a Java Web Service through HttpClient. When I run the Java Web Service, I get the result {"id":2,"content":"Hello, World!"}. When I run the ASP.NET Web API, The ASP.NET Web API gets result from the Java Web Service and displays result as "{\"id\":2,\"content\":\"Hello, World!\"}" in a web page.
How do I display the result in console, which means I create a console application and put in these codes and I want the result to come out in a console and not web page. How do I do that? What are the codes that has to be modified? Someone please kindly do help me thank you so much.
Here are my ASP.NET Codes that I have done so far:
ClientController.cs
public class ClientController : ApiController
{
private ServerClient serverClient = new ServerClient();
public async Task<IHttpActionResult> GET()
{
try
{
var result = await serverClient.content();
return Ok(result);
}
catch (Exception e)
{
var result = "Server is not running";
return Ok(new { ErrorMessage = result });
}
}
}
ServerClient.cs
public class ServerClient
{
private static HttpClient client;
private static string BASE_URL = "http://localhost:8080/";
static ServerClient()
{
client = new HttpClient();
client.BaseAddress = new Uri(BASE_URL);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<string> content()
{
var endpoint = string.Format("greeting");
var response = await client.GetAsync(endpoint);
return await response.Content.ReadAsStringAsync();
}
}
WebApiConfig.cs
config.Routes.MapHttpRoute(
name: "TestClient",
routeTemplate: "api/testclient",
defaults: new { actcion = "Get", controller = "Client" }
);
static void Main(string[] args)
{
var result = serverClient.content().Result;
Console.WriteLine(result);
}
Please note that using Result or Wait() in async programming might cause deadlock

Call Web API from MVC Controller using the same HttpClient

I have an MVC5 project, from the MVC controller I need to call the Web API method. The Web API uses token based authentication, so I have to pass the token for each call. I am using the code below to pass the token in the HTTP header:
HttpClient httpClient = new HttpClient();
string baseUrl = "http://localhost:60477/";
dynamic token = Session["token"];
if (token.AccessToken != null)
{
httpClient.DefaultRequestHeaders.Add("Authorization", String.Format("Bearer {0}", token.AccessToken));
}
There are multiple action methods in my controller, and I want to use a single HttpClient and headers, added in one place, instead of adding a header in each and every action method.
Where can I place the HttpClient headers registration code in the MVC application, so it can be common to all controllers? That means I don't want to repeat code, like adding the token in each and every action method. How can I do that?
Public ActionResult Postuser(UserModel user)
{
// post code
}
Public ActionResult getuser(UserModel user)
{
HttpResponseMessage response = httpClient.GetAsync(baseUrl + "api/Admin/GetStates").Result;
if (response.IsSuccessStatusCode)
{
string stateInfo = response.Content.ReadAsStringAsync().Result;
}
}
Public ActionResult PostRoles(RoleModel role)
{
// post roles code
}
You can try creating a small helper class for creating your httpclient object. Something like
public class HttpClientHelper
{
public static HttpClient GetHttpClient()
{
var MyHttpClient = new HttpClient();
dynamic _token = HttpContext.Current.Session["token"];
if (_token == null) throw new ArgumentNullException(nameof(_token));
MyHttpClient.DefaultRequestHeaders.Add("Authorization", String.Format("Bearer {0}", _token.AccessToken));
return MyHttpClient;
}
}
and then call it in your controllers as
public ActionResult getuser(UserModel user)
{
var httpClient = HttpClientHelper.GetHttpClient();
HttpResponseMessage response = httpClient.GetAsync(baseUrl + "api/Admin/GetStates").Result;
if (response.IsSuccessStatusCode)
{
string stateInfo = response.Content.ReadAsStringAsync().Result;
}
}
It is better to adhere to the Single Responsibility Principle and extract the interaction with another service in a it's own class, e.g.
public class ServiceClient : IServiceClient
{
private HttpClient m_Client;
public ServiceClient
{
m_Client = new HttpClient();
// Initialize the client as you need here
}
public void CallSomeMethod()
{
// Call method on the client
}
}
Then you inject the IServiceClient in your controller and just call it's methods. If you do not use injection (which I advise you do) you can just create a new instance in the controller's constructor.
You can try using an action filter in your controller. Try adding an override that looks something like this-
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// some condition code to target a specific method in the controller
// Example
if (filterContext.ActionDescriptor.ActionName == "getuser") // <-- your method
{
// put your token based authentication code here
}
base.OnActionExecuting(filterContext);
}
The OnActionExecuting method is at the controller scope so you can have different logic for different controllers.
There's also an OnActionExecuted method override if you want to run code after your action method.
------edit--------------
As far as where to place your HttpClient code snippet, you can try this-
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpClient httpClient = new HttpClient();
string baseUrl = "http://localhost:60477/";
dynamic token = Session["token"];
if (token.AccessToken != null)
{
httpClient.DefaultRequestHeaders.Add(
"Authorization",
string.Format("Bearer {0}", token.AccessToken)
);
httpClient.BaseAddress = new Uri(baseUrl);
}
if(filterContext.ActionParameters.ContainsKey("httpClient"))
{
filterContext.ActionParameters["httpClient"] = httpClient;
}
else
{
// error
}
base.OnActionExecuting(filterContext);
}
So the HttpClient object along with the assignment of your baseUrl is established in OnActionExecuting. This code will run before any method returning a ActionResult in the controller you are refactoring. If you want to target some and not all methods, see the first example of OnActionExecuting above.
public ActionResult getuser(UserModel user, HttpClient httpClient)
{
HttpResponseMessage response = httpClient.GetAsync("api/Admin/GetStates").Result;
if(response.IsSuccessStatusCode)
{
string stateInfo = response.Content.ReadAsStringAsync().Result;
}
// the rest of your code for getuser..
return View();
}
Now your getuser method has an extra parameter ( HttpClient httpClient ).
why don't you move the code in Global asax or create custom Atribute?
here is one good link:
http://www.diaryofaninja.com/blog/2011/07/24/writing-your-own-custom-aspnet-mvc-authorize-attributes

Categories

Resources