Pass the username and password in Authorization headers for OData - asp.net-core

How to pass the username and password in Authorization headers for OData URL in Asp.net core 2.2

Use an HTTPClient where you can set the specific header value for authorization:
string username = "bbb";
string password = "abc";
string url = "https://yourOData.com"
HttpClient httpClient = new HttpClient();
httpClient .DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Basic",
Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes($"{username}:{password}")));
HttpResponseMessage response = await HttpClient.GetAsync(url);
If you got that one working, take a look at https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ and refactor your code to not instantiate HttpClient in the way I did.

Related

Migrating a SOAP client from to .NET5 that uses WSE and Microsoft.Web.Services2

I am migrating a .NetFramework application that access a DMS system over SOAP
The working implementation involves some generated code where it was necessary to change the base class to Microsoft.Web.Services2.WebServiceClientProtocol in order for the security headers to be correctly built.
public partial class MyService: Microsoft.Web.Services2.WebServicesClientProtocol
The following code successfully calls the WsSearchDmsDocument
var token = new UsernameToken(DmsUsername, DmsPassword, PasswordOption.SendHashed);
var client = new MyService() {Url = ReinsUrl};
SoapContext requestContext = client.RequestSoapContext;
requestContext.Security.Timestamp.TtlInSeconds = 60;
requestContext.Security.Tokens.Add(token);
var myRequest = new Request();
var response = client.WsSearchDmsDocument(request);
Which sends the username/password security header looking like and returns the expected response
<wsse:UsernameToken wsu:Id='UsernameToken-238be95be3bf445fb8534666a7a8693c'>
<wsse:Username>***login***</wsse:Username>
<wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-usernametoken-profile-1.0#PasswordDigest'>***Base64 (SHA-1 (nonce + created + password) )***</wsse:Password>
<wsse:Nonce EncodingType='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soapmessage-security-1.0#Base64Binary'>***Base64 nonce***</wsse:Nonce>
<wsu:Created>2019-09-06T12:09:15.604Z</wsu:Created>
</wsse:UsernameToken>
In .Net5
I modified the MyService class in the following way
internal partial class MyService: System.ServiceModel.ClientBase<MyReinsServices>, MyReinsServices
Then I try to call the service
var token = new UsernameToken(DmsUsername, DmsPassword, PasswordOption.SendHashed);
MyService client = new MyService(ReinsServicesClient.EndpointConfiguration.ReinsServicesSoap11, ReinsUrl);
UserNamePasswordClientCredential credential = client.ClientCredentials.UserName;
credential.UserName = DmsUsername;
credential.Password = DmsPassword;
var myRequest = new Request();
var response = client.WsSearchDmsDocument(request);
But this fails with
com.sun.xml.wss.XWSSecurityException:
Message does not conform to configured policy [ AuthenticationTokenPolicy(S) ]:
No Security Header found;
nested exception is com.sun.xml.wss.XWSSecurityException:
com.sun.xml.wss.XWSSecurityException:
Message does not conform to configured policy [ AuthenticationTokenPolicy(S) ]:
No Security Header found
This is a similar error to what I was getting in the .Net Framework version before I used Microsoft.Web.Services2.WebServicesClientProtocol
I think I am very close to a solution but no matter what client.ClientCredentials I take it does not satisfy the security header needed by this particular SOAP service
EDIT
I am able to use SoapUI to call this service. I have to set in WS-Security setting a username and password with PasswordDigest and adding this as a Basic Auth to the outgoing WSS. I then copy the resulting SOAP Envelope into Soap.txt and try to send this via .NET5 in the following code
string soap = File.ReadAllText("soup.txt");
XmlDocument document = new XmlDocument();
document.LoadXml(soap); //loading soap message as string
XmlNamespaceManager manager = new XmlNamespaceManager(document.NameTable);
manager.AddNamespace("reins", "http://scor.com/dms-reins-webservices/schemas/2.0/reins");
manager.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
manager.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
manager.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
//Build the token
RNGCryptoServiceProvider Generator = new RNGCryptoServiceProvider();
var _nonce = new byte[16];
Generator.GetBytes(_nonce);
string nonce = Convert.ToBase64String(_nonce);
var created = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ");
string payLoad = nonce + created + DmsPassword;
byte[] payLoadBytes = System.Text.Encoding.UTF8.GetBytes(payLoad);
SHA1 sha = new SHA1CryptoServiceProvider();
var tokenBytes = sha.ComputeHash(payLoadBytes);
string token = Convert.ToBase64String(tokenBytes);
document.SelectSingleNode("//soapenv:Envelope/soapenv:Header/wsse:Security/wsse:UsernameToken/wsu:Created", manager).InnerText = created;
document.SelectSingleNode("//soapenv:Envelope/soapenv:Header/wsse:Security/wsse:UsernameToken/wsse:Password", manager).InnerText = token;
document.SelectSingleNode("//soapenv:Envelope/soapenv:Header/wsse:Security/wsse:UsernameToken/wsse:Nonce", manager).InnerText = nonce;
var httpClient = new HttpClient();
httpClient.DefaultRequestVersion = HttpVersion.Version11;
HttpResponseMessage response;
var soapMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(url),
Content = new StringContent(s, Encoding.UTF8, MediaTypeNames.Text.Xml),
};
soapMessage.Headers.Add("Accept", "text/xml");
soapMessage.Headers.Add("Accept-Encoding", "gzip,deflate");
soapMessage.Headers.Add("SOAPAction", "");
soapMessage.Headers.Add("Connection", "Keep-Alive");
soapMessage.Headers.Add("User-Agent", "Apache-HttpClient/4.5.5 (Java/12.0.1)");
response = httpClient.Send(soapMessage);
When I make the request via SoapUi I get a StatusCode 200 and the data I want. When I use the above code that should perform the exact same operation I get an StatusCode 500 Internal Server error.
Any suggestions how to solve either the first part of this or to get this hack to work using .NET5 or .NET6 would be welcome

asp.net core webapp - OAUTH2 AUTHORIZATION - 401 Error

i want to use the itwin api and i created a little asp.net core 3.1 webapp.
The Authorization endpoint: https://ims.bentley.com/connect/authorize works fine and i get a code.
Now i want to use the code to get the access token with this backend method:
string url = "https://ims.bentley.com/connect/token";
string clientID = "webapp-XXXXXXXXXXXXXXXXXXXXXX";
string secretID = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
//string svcCredentials = Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(clientID + ":" + secretID));
//string svcCredentials = Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(clientID + ":" + secretID));
string svcCredentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(clientID + ":" + secretID));
var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
nvc.Add(new KeyValuePair<string, string>("code", code));
nvc.Add(new KeyValuePair<string, string>("redirect_uri", "http://localhost:44343/signin-callback"));
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", svcCredentials);
HttpResponseMessage response = client.PostAsync(url, new FormUrlEncodedContent(nvc)).Result;
var token = response.Content.ReadAsStringAsync().Result;
}`
Everthing works fine but i get an 401 Error. Use i the wrong BASE64 encoding?
thank you for help.
I suggest trying the following:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", svcCredentials);
Based on the documentation for AuthenticationHeaderValue the first parameter is the scheme and we use Bearer.
You need to url-encode client_id and client_secret before using them for Basic authentication, as described in documentation
The client must authenticate using the HTTP Basic method and provide the url-encoded clientId and the clientSecret
This is also stated in OAuth specification
The client identifier is encoded using the
"application/x-www-form-urlencoded" encoding algorithm per
Appendix B, and the encoded value is used as the username; the client
password is encoded using the same algorithm and used as the
password.

Using httpClient.postasync for web api calls .netcore

I am new to .netcore, I am working on web api that are running on docker container and while using postman the web api's are working really fine outputting the results. I want to make a program in .netcore calling the webapi endpoints and getting the response and using that particular response in other endpoints with MVC.
The explanation is given below.
The default username and password for admin is default set for example username:admin , password: helloworld
. The first time admin login the api requires a new personal password as shown in the Postman figure below.
The login api is: localhost://..../v1/users/login
The first question is How to give the values in Authorization->BasicAuth using .netcore.
The body of the api looks like the figure below.
After setting the new_password the response of the api is a token as given below.
The particular token is then use in the Environment to create user. The image for more clear problem is given below.
Lastly, the token then used to make other API calls such as creating a user.
API: https://localhost/..../v1/users
The image is below.
As a newbie in .netcore language, I am really struggling to do this kind of API calls, as most of the tutorials I tried are generating their own token from API, but here I just want to take the response token and save it and then use it in other API calls.
The StackOverflow community's support was always really handy for me.
The Code I'm trying is given below.
**Controller**
public class Login_AdminController : ControllerBase
{
[Route("/loginAdmin")]
[HttpPost]
public async Task<string> LoginAdminAsync([FromBody] dynamic content)
{
LoginAdmin L = new LoginAdmin();
var client = new HttpClient();
client.BaseAddress = new Uri("https://localhost:9090");
var request = new HttpRequestMessage(HttpMethod.Post, "/v1/users/login");
var byteArray = new UTF8Encoding().GetBytes($"<{L.username}:{L.df_Password}>");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
var formData = new List<KeyValuePair<string, string>>();
formData.Add(new KeyValuePair<string, string>("new_password", "helloWorld123!"));
request.Content = new FormUrlEncodedContent(formData);
var response = await client.SendAsync(request);
Console.WriteLine(response);
return content;
}
}
}
***Model***
public class LoginAdmin
{
public string username = "admin";
public string df_Password = "secret";
public string new_Password { get; set; }
}
Thank you.
Do you want to get token from response? If yes. Try this:
var client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:12345/Api");
var request = new HttpRequestMessage(HttpMethod.Post, "/token");
var keyValues = new List<KeyValuePair<string, string>>();
keyValues.Add(new KeyValuePair<string, string>("username", "yourusername"));
keyValues.Add(new KeyValuePair<string, string>("password", "yourpassword"));
request.Content = new FormUrlEncodedContent(keyValues);
var response = client.SendAsync(request).Result;
return response.Content.ReadAsStringAsync().Result;
Authorization is handled via the Authorization request header, which will include a token of some sort, prefixed by the scheme. What you're talking about here isn't really basic auth. With that, you literally pass the username and pass in the Authorization header with each request. What you're doing is just authenticating once to get an auth token, and then using that auth token to authorize further requests. In that scenario, you should really be posting the username and pass in the request body. Then, you'd do bearer auth with the token for the other requests, using the Authorization header. Still, to cover both bases:
Basic Auth
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
request.Headers.Add("Authorization", $"Basic {token}");
Bearer Auth
request.Headers.Add("Authorization", $"Bearer {token}");
// where `token` is what was returned from your auth endpoint
FWIW, List<KeyValuePair<string, string>> is just Dictionary<string, string>. It's better to use the real type. Then, you can just do formData.Add("new_password", "helloWorld123!") instead of formData.Add(new KeyValuePair<string, string>("new_password", "helloWorld123!"))

NTLM Auth with RestSharp

I am attempting to create some tests using RestSharp for a project I am working on.
This project uses Single Sign-on NTLM Authentication.
I am attemping to use a NTLMAuthenticator but my getUser request is always failing. I am not positive what URL to put in for the CredentialCache, the project or the SSO Id Provider.
SharedRequests shared = new SharedRequests();
var credential = new CredentialCache
{
{
new Uri("project or ID Provider URL or something else?"),
"NTLM",
new NetworkCredential("doamin\Username", "Password")
}
};
RestClient client = new RestClient();
client.BaseUrl=new Uri("projectURL");
client.Authenticator = new NtlmAuthenticator(credential);
client.PreAuthenticate = true;
RestRequest request = shared.GetCurrentUser();
IRestResponse response = client.Execute(request);
my response always gets a 500 error which is what is expected when no auth cookies are present.

Survey Monkey is Giving me an Invalid Authorization in Request Header Error

I am using the .NET HttpClient in xamarin forms to try to connect to survey monkey. I am new to HttpClient, Xamarin, REST, and survey monkey so I could be making mistakes anywhere or everywhere.
I've had some success using the survey monkey examples with curl and I am trying to convert that to C# code with HttpClient.
I have some "test code" like
HttpClient httpClient = new HttpClient();
Uri uri = new Uri("https://api.surveymonkey.net/v2/surveys/get_survey_list?api_key=humkanu389g5dp9tvsdrh8fv");
HttpContent content = new StringContent(String.Empty,Encoding.UTF8,"application/json");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization","Bearer XXXXXXXXX");
httpClient.Timeout = TimeSpan.FromSeconds(10);
HttpResponseMessage response = Task.Run(() => httpClient.PostAsync(uri,content)).Result;
String responseContent = Task.Run(() => response.Content.ReadAsStringAsync()).Result;
In my code, I have replaced the XXXXXXX's with my authorization token.
But I am getting a response like
{"status":1,"errmsg":"Invalid \"Authorization\" data in request header"}
I am wondering if I am handling the AuthenticationHeaderValue() part wrong. Or something else?
Your header is not in the correct format, you have "Authorization bearer XXXXX" as the value. So it looks like this:
Authorization: Authorization bearer XXXXX
I'm not sure why based on your code, you should debug and see the value you are sending out. But it should be:
Authorization: bearer XXXXX
My assumption is AuthenticationHeaderValue is doing something you're not expecting. Searching the docs it looks like you're doing it right - so I'm not sure exactly what's wrong with the C# code, just that the header you're sending out is not correct.
General Kandalaft really pointed out the problem--which in retrospect was obvious, but I want to post the final code here in case someone else is trying this.
I ended up fixing the AuthenticationHeaderValue as pointed out above. Then I ran into a problem because my request content was an empty string. I put in some json text from an example and it worked. So the final code
HttpClient httpClient = new HttpClient();
Uri uri = new Uri("https://api.surveymonkey.net/v2/surveys/get_survey_list?api_key=humkanu389g5dp9tvsdrh8fv");
HttpContent content = new StringContent ("{ \"fields\": [ \"title\", \"date_modified\" ] }",Encoding.UTF8,"application/json");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer","XXXXXX");
httpClient.Timeout = TimeSpan.FromSeconds(10);
HttpResponseMessage response = Task.Run(() => httpClient.PostAsync(uri,content)).Result;
String responseContent = Task.Run(() => response.Content.ReadAsStringAsync()).Result;