I am working on .NET Core WebAPI (Core 3.1) project where I have to make call to outside third party API's using OAuth2 Toekn based implementation.
Basically it is 2-Step process. First step is to get Token using Token API Call and then make call to Export API by passing this Token-AccessCode and Input JSON.
I am sucessfully able to get Token using below API Controller Code, but second API call(Export API Method) is throwing error.
Where as when i tested in Postman using Bearer Authorization, it worked fine.
Step(1) In Postman, Authorization tab is set to "No Auth".
Step(2) Header Tab has two settings as below (Authorization and Content-Type).
Step(3) From Body Tab, I am sending below JSON.
{
"oneOf": {
"schedule_date": "08/05/2020",
"request_date": null
},
"start_time": "",
"end_time": "",
"order_number": "",
"status": ""
}
After the above 3 settings, when i call POST method in Postman, it is returning response.
Where as below APICode is throwing error at Line exportClient.PostAsync() call.
Error:
**Exception:**The operation was canceled.
InnerException: Unable to read data from the transport connection: The I/O operation has been aborted because of either a thread exit or an application request..
What am i doing wrong?
Appreciate your resposes.
This is the APIController Code:
[HttpGet]
[Route("PullOrders")]
public async Task<IActionResult> PullOrders(
)
{
try
{
var token = await GetElibilityToken();
string exportUrl = "https://XXXXXXX.com/api/v1/export";
var exportClient = new HttpClient();
//exportClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + token.AccessToken);
exportClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
exportClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var oneData = new OneOf() { schedule_date = "08/05/2020" };
var orderReq = new PullOrderInputRequest() { oneOf = oneData };
var json = JsonConvert.SerializeObject(orderReq, Newtonsoft.Json.Formatting.Indented);
var stringContent = new StringContent(json);
HttpResponseMessage orderResponse = await **exportClient.PostAsync**(exportUrl, stringContent);
}
catch (Exception ex)
{
throw ex;
}
return Ok();
}
private static async Task<Token> GetElibilityToken()
{
var tokenClient = new HttpClient();
string baseAddress = #"https:// XXXXXXX/api/v1/oauth2/token";
string grant_type = "client_credentials";
string client_id = " XXXXXXX";
string client_secret = " XXXXXXXXXXXXXX ";
var form = new Dictionary<string, string>
{
{"grant_type", grant_type},
{"client_id", client_id},
{"client_secret", client_secret},
};
HttpResponseMessage tokenResponse = await tokenClient.PostAsync(baseAddress, new FormUrlEncodedContent(form));
var jsonContent = await tokenResponse.Content.ReadAsStringAsync();
Token tok = JsonConvert.DeserializeObject<Token>(jsonContent);
return tok;
}
internal class Token
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
}
I have a database that contains emails and password hashes.
I would like to secure http trigger's from Azure Functions to allow only authorized call thanks to the Authorization header with a BEARER token.
I think I will need
an http trigger that will generate the token from email/password
Authorize and authenticate the user based on the Authorization header
Can someone get me started on how to create a custom authentication provider or use an existing one and configure Azure Functions to work with it?
Microsoft identity platform supports the OAuth 2.0 Resource Owner Password Credentials (ROPC) grant, which allows an application to sign in the user by directly handling their password.
Get the email(username) and password from database, and send the following request to receive the access token.
POST {tenant}/oauth2/v2.0/token
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&scope=user.read%20openid%20profile%20offline_access
&username=MyUsername#myTenant.com
&password=SuperS3cret
&grant_type=password
You could have look following code snippet, I have tested on azure portal , Azure Function V2:
#r "Newtonsoft.Json"
using Newtonsoft.Json;
using System.Net;
using System.Net.Http.Headers;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
try
{
//Parse query parameter
log.LogInformation("C# HTTP trigger function processed a request.");
//Read Request Body
var content = await new StreamReader(req.Body).ReadToEndAsync();
//Extract Request Body and Parse To Class
UserAuthentication objUserInfo = JsonConvert.DeserializeObject<UserAuthentication>(content);
//Message Container
dynamic validationMessage;
//Validate required param
if (string.IsNullOrEmpty(objUserInfo.UserName.Trim()))
{
validationMessage = new OkObjectResult("User name is required!");
return (IActionResult)validationMessage;
}
if (string.IsNullOrEmpty(objUserInfo.Password.Trim()))
{
validationMessage = new OkObjectResult("Password is required!");
return (IActionResult)validationMessage;
}
// Authentication Token Request format
string tokenUrl = $"https://login.microsoftonline.com/common/oauth2/token";
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
tokenRequest.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "password",
["client_id"] = "YourApplicationId",
["client_secret"] = "YourApplicationPassword",
["resource"] = "https://graph.microsoft.com",
["username"] = "" + objUserInfo.UserName + "",
["password"] = "" + objUserInfo.Password + ""
});
// Request For Token Endpoint
using (var _client = new HttpClient())
{
var tokenResponse = await _client.SendAsync(tokenRequest);
AccessTokenClass objAccessToken = JsonConvert.DeserializeObject<AccessTokenClass>(await tokenResponse.Content.ReadAsStringAsync());
// When Token Request Null
if (objAccessToken.access_token == null)
{
validationMessage = new OkObjectResult("Invalid Authentication! Please Check Your Credentials And Try Again!");
return (IActionResult)validationMessage;
}
else
{
return new OkObjectResult(objAccessToken.access_token);
}
}
}
catch (Exception ex)
{
validationMessage = new OkObjectResult("Sorry something went wrong! Please check your given information and try again! {0}" + ex.Message);
return (IActionResult)validationMessage;
}
}
Class I have Used:
UserAuthentication Class
public class UserAuthentication
{
public string UserName { get; set; }
public string Password { get; set; }
}
public class AzureFunctionCreateUserClass
{
public string access_token { get; set; }
public string expires_in { get; set; }
public string token_type { get; set; }
public string resource { get; set; }
}
Note: This an sample for azure portal which I have written on azure function . So try to run on there.
Hope this would help.
I'm quite new to Web API and i still don't get it how to request n JWT.
In My Startup.cs i configured OAuth and my custom JWT:
CustomJwtFormat.cs
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string issuer = string.Empty;
private readonly int timeoutMinutes = 60;
public CustomJwtFormat(string issuer)
{
this.issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audience = "all";
var secret = Convert.FromBase64String("mySecret");
var now = DateTime.UtcNow;
var expires = now.AddMinutes(timeoutMinutes);
string signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256";
string digestAlgorithm = "http://www.w3.org/2001/04/xmlenc#sha256";
var signingKey = new SigningCredentials(
new InMemorySymmetricSecurityKey(secret), signatureAlgorithm, digestAlgorithm);
var token = new JwtSecurityToken(issuer, audience, data.Identity.Claims,
now, expires, signingKey);
var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
return tokenString;
}
public AuthenticationTicket Unprotect(string protectedText)
{
throw new NotImplementedException();
}
}
EDIT: I managed retrieving a token with Postman.
Now my next problem: as long as i send a valid jwt everything works ok. But whenn i send e.g. 'bearer 12345' i get the error message
IDX10708: 'System.IdentityModel.Tokens.JwtSecurityTokenHandler' cannot read this string: '12345'.
...in Visual Studio. Where do i need to put in the token validation? Make a new class and put the TokenSecurityHandler anywhere?
I need to create a POST method in WebApi so I can send data from application to WebApi method. I'm not able to get header value.
Here I have added header values in the application:
using (var client = new WebClient())
{
// Set the header so it knows we are sending JSON.
client.Headers[HttpRequestHeader.ContentType] = "application/json";
client.Headers.Add("Custom", "sample");
// Make the request
var response = client.UploadString(url, jsonObj);
}
Following the WebApi post method:
public string Postsam([FromBody]object jsonData)
{
HttpRequestMessage re = new HttpRequestMessage();
var headers = re.Headers;
if (headers.Contains("Custom"))
{
string token = headers.GetValues("Custom").First();
}
}
What is the correct method for getting header values?
Thanks.
On the Web API side, simply use Request object instead of creating new HttpRequestMessage
var re = Request;
var headers = re.Headers;
if (headers.Contains("Custom"))
{
string token = headers.GetValues("Custom").First();
}
return null;
Output -
Suppose we have a API Controller
ProductsController : ApiController
There is a Get function which returns some value and expects some input header (for eg. UserName & Password)
[HttpGet]
public IHttpActionResult GetProduct(int id)
{
System.Net.Http.Headers.HttpRequestHeaders headers = this.Request.Headers;
string token = string.Empty;
string pwd = string.Empty;
if (headers.Contains("username"))
{
token = headers.GetValues("username").First();
}
if (headers.Contains("password"))
{
pwd = headers.GetValues("password").First();
}
//code to authenticate and return some thing
if (!Authenticated(token, pwd)
return Unauthorized();
var product = products.FirstOrDefault((p) => p.Id == id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
Now we can send the request from page using JQuery:
$.ajax({
url: 'api/products/10',
type: 'GET',
headers: { 'username': 'test','password':'123' },
success: function (data) {
alert(data);
},
failure: function (result) {
alert('Error: ' + result);
}
});
Hope this helps someone ...
As someone already pointed out how to do this with .Net Core, if your header contains a "-" or some other character .Net disallows, you can do something like:
public string Test([FromHeader]string host, [FromHeader(Name = "Content-Type")] string contentType)
{
}
Another way using a the TryGetValues method.
public string Postsam([FromBody]object jsonData)
{
IEnumerable<string> headerValues;
if (Request.Headers.TryGetValues("Custom", out headerValues))
{
string token = headerValues.First();
}
}
For .NET Core:
string Token = Request.Headers["Custom"];
Or
var re = Request;
var headers = re.Headers;
string token = string.Empty;
StringValues x = default(StringValues);
if (headers.ContainsKey("Custom"))
{
var m = headers.TryGetValue("Custom", out x);
}
In case someone is using ASP.NET Core for model binding,
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding
There's is built in support for retrieving values from the header using the [FromHeader] attribute
public string Test([FromHeader]string Host, [FromHeader]string Content-Type )
{
return $"Host: {Host} Content-Type: {Content-Type}";
}
try these line of codes working in my case:
IEnumerable<string> values = new List<string>();
this.Request.Headers.TryGetValues("Authorization", out values);
For WEB API 2.0:
I had to use Request.Content.Headers instead of Request.Headers
and then i declared an extestion as below
/// <summary>
/// Returns an individual HTTP Header value
/// </summary>
/// <param name="headers"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string GetHeader(this HttpContentHeaders headers, string key, string defaultValue)
{
IEnumerable<string> keys = null;
if (!headers.TryGetValues(key, out keys))
return defaultValue;
return keys.First();
}
And then i invoked it by this way.
var headerValue = Request.Content.Headers.GetHeader("custom-header-key", "default-value");
I hope it might be helpful
app.MapGet("/", ([FromHeader(Name = "User-Agent")] string data) =>
{
return $"User agent header is: {data}";
});
A simple function to get a header value, with a "one-liner" variant using TryGetValue:
private string GetHeaderValue(string key) =>
Request.Headers.TryGetValue(key, out var value)
? value.First()
: null;
var headerValue = GetHeaderValue("Custom");
You need to get the HttpRequestMessage from the current OperationContext. Using OperationContext you can do it like so
OperationContext context = OperationContext.Current;
MessageProperties messageProperties = context.IncomingMessageProperties;
HttpRequestMessageProperty requestProperty = messageProperties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
string customHeaderValue = requestProperty.Headers["Custom"];
For .net Core in GET method, you can do like this:
StringValues value1;
string DeviceId = string.Empty;
if (Request.Headers.TryGetValue("param1", out value1))
{
DeviceId = value1.FirstOrDefault();
}
I am trying to implement google fustion table api in my WPF application to show large number of marker in my google map but problem is this when I am going to authenticate the function "GetAuthorization" never call.
public Fusion()
{
// Create the service.
objService = new FusiontablesService(new BaseClientService.Initializer()
{
Authenticator = CreateAuthenticator()
});
//GetAuthorization(provider);
}
/// <summary>
/// The remote service on which all the requests are executed.
/// </summary>
public FusiontablesService objService { get; private set; }
NativeApplicationClient provider = null;
private IAuthenticator CreateAuthenticator()
{
provider = new NativeApplicationClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = ClientCredentials.ClientID,
ClientSecret = ClientCredentials.ClientSecret
};
return new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
}
private IAuthorizationState GetAuthorization(NativeApplicationClient client)
{
// You should use a more secure way of storing the key here as
// .NET applications can be disassembled using a reflection tool.
const string STORAGE = "google.samples.dotnet.fusion";
const string KEY = "AIzaSyCtaH=6+";
string scope = FusiontablesService.Scopes.Fusiontables.GetStringValue();
// Check if there is a cached refresh token available.
IAuthorizationState state = AuthorizationMgr.GetCachedRefreshToken(STORAGE, KEY);
if (state != null)
{
try
{
client.RefreshToken(state);
return state; // Yes - we are done.
}
catch (DotNetOpenAuth.Messaging.ProtocolException ex)
{
CommandLine.WriteError("Using existing refresh token failed: " + ex.Message);
}
}
// Retrieve the authorization from the user.
state = AuthorizationMgr.RequestNativeAuthorization(client, scope);
AuthorizationMgr.SetCachedRefreshToken(STORAGE, KEY, state);
return state;
}
Please help me out of this issue.
P.S This code works fine when i am using TaskService and BookService.
Edit : Forgot that the method is not exposed in the IAuthenticator interface
private IAuthenticator CreateAuthenticator()
{
provider = new NativeApplicationClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = ClientCredentials.ClientID,
ClientSecret = ClientCredentials.ClientSecret
};
var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
auth.LoadAccessToken()
return auth;
}