.NET client calling HTTPS Java web service - c#

I have a HTTPS Java Web Service, I am trying to access the web service with .NET[.NET 2.0 style client / WCF client]. I am getting back this error from WS.
"HTTP Status 401 - This request requires HTTP authentication ()."
How do I find out what kind of security this WS has?
Besides SSL, I have user name and password to send it to WS, I believe this is part of message authentication.
Java client seems like successfully communicating, and it has few interesting lines,
System.setProperty("javax.net.ssl.keyStorePassword", new String(jsseKeyStorePassword));
System.setProperty("javax.net.ssl.trustStorePassword", new String(jsseTrustStorePassword));
----------------------------------------------
BindingProvider bindingProvider = (BindingProvider) port;
bindingProvider.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username);
bindingProvider.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, new String(password));
I would appreciate any help.

Long story short,
Two problems I had to solve,
HTTP Status 401 - This request
requires HTTP authentication ()
Changed Authentication schema to Basic in the VS generated customeBinding in app.config
HTTP/1.1 505 HTTP Version Not
Supported
Remove Expect: 100-continue SOAP header. Add this line ServicePointManager.Expect100Continue = false;. For the details of the issue, go here
Long story here http://www.irasenthil.com/2010/10/wcf-client-to-java-web-service.html

It seems as though the Java WS requires a server and root certificate stored in a couple of key stores. It requires knowledge of the passwords to these key stores to obtain the certificates, which seem to be available in the jsseKeyStorePassword and jsseTrustStorePassword variables.
Also, you should be using at least .NET Framework 3.0 in order to use Windows Communication Foundation.

Related

Consuming web api with JWT authentication in .NET 7 MAUI?

I have a minimal API .NET 7 installed on an external web server and use JWT for authentication. For testing I created a few endpoints (with authentication and also without) so I can test the web API via Postman. I start Postman from my private machine and access the web address of the API to test everything.
Now everything works as expected. I can log in via Postman, then I get JWT and if I enter JWT in Postman, then I can also access protected endpoint and get the data from the Web API.
Now I have created a desktop application in MAUI .NET 7 and I want to use this web API. Also here the access to unprotected endpoint works as well as logging in with receiving the JWT. Only the last part of the whole thing does not work anymore and that is access to a protected endpoint with the delivery of JWT for which I constantly get the message 401 Unauthorized. If I then put the same JWT into Postman, then the request goes through Posstman and I get the data from Web API!
I have been looking for a solution and have tried all possible code examples from the internet. For example:
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("http://api.mywebsite.com:64591/secret")
};
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Token.token);
var response = await _httpClient.SendAsync(requestMessage);
or
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", Token.token);
var RawData = await _httpClient.GetStringAsync("http://api.mywebsite.com:64591/secret2");
In some places I read that there were problems with the change to .NET 6. The solution was new NuGet packages, but since I'm already on .NET 7, I installed the latest versions.
There was also a post suggesting that in the web API you set issuer and audience to false. I did that as well, but to no success.
ValidateIssuer = false,
ValidateAudience = false,
Does anyone have a working code for MAUI native app that consumes minimal API?
EDIT
Following Heretic Monkey's suggestion, I installed Wireshark software and analyzed Network Transfer.
Here is what I found:
the token I receive from Web Api after authentication, I also send in the same form when requesting to an Authorized Endpoint (so received and sent token from client are identical). My conclusion here is that the token is correct.
When the request with the JWT is sent from the client to the server (Web Api), I then get the error message 401. In the log I see below information with the reason Bearer error="invalid_token":
Hypertext Transfer Protocol
HTTP/1.1 401 Unauthorized\r\n
[Expert Info (Chat/Sequence): HTTP/1.1 401 Unauthorized\r\n]
[HTTP/1.1 401 Unauthorized\r\n]
[Severity level: Chat]
[Group: Sequence]
Response Version: HTTP/1.1
Status Code: 401
[Status Code Description: Unauthorized]
Response Phrase: Unauthorized
Transfer Encoding: chunked\r\n
Server: Microsoft-IIS/10.0\r\n
WWW-Authenticate: Bearer error="invalid_token"\r\n
X-Powered-By: ASP.NET\r\n
Date: Sun, 18 Dec 2022 09:07:00 GMT\r\n
\r\n
[HTTP response 1/1]
[Time since request: 0.047969000 seconds]
[Request in frame: 790]
[Request URI: http://api.myserver.com:64591/secret2]
HTTP chunked response
End of chunked encoding
Chunk size: 0 octets
\r\n
File Data: 0 bytes
There are only two error reasons I could think of:
I still have a bug in my minimal API (Web Api) and that is regarding the JWT I get from the client and somehow still need to convert/crimp the JWT maybe!? By the fact that I may use JWT in exactly the form that is sent to client, then it may be that it is wrong and that is why this error message "invalid_token" comes.
the second cause could be NET 7, so an error that occurs not because my code is wrong but because it is implemented incorrectly in NET 7 (is of course not probable but not impossible).
Maybe someone has a suggestion how I can fix this error?
If this doesn't work (i.e. a request to Web Api with JWT authentication), then Web Api is unusable in NET 7 and I really can't imagine that.
So I truly assume that the bug is in my implementation (either server/minimal Api or client MAUI NET 7).
Thanks
The problem was already kind of strange, because even the many tutorials and posts in the form as they are given in Internet will not work. But if you copy generated token out (e.g. from debug mode) and use it in Postman, then everything will work nicely and this is something that confuses you a lot. Fortunately, there are still people who have incredible mind and can detect such inconsistencies. I wouldn't have seen this in 1000 years either :)
See: https://learn.microsoft.com/en-us/answers/questions/1133200/401-unauthorized-consuming-web-api-with-jwt-authen.html
In my case I noticed that
response.Content.ReadAsStringAsync().Result
in .NET MAUI will return "+token+", I have trimmed the quotation mark (") , and it worked with me
using HttpResponseMessage response = await client.PostAsJsonAsync("Login", loginData);
response.EnsureSuccessStatusCode();
string token = response.Content.ReadAsStringAsync().Result;

How do I setup a valid on-premise ADFS URI?

I have a .NET 4.6.2 Windows client application which needs to get an authentication token from our on-premise ADFS server and use it to call an ASP.NET Core REST API. It's client name, id (GUID) and re-direct URI have been registered with ADFS. I am using the latest ADAL (v3.13) library to facilitate the authentication. I am attempting to get a token as demonstrated in the ADAL sample code like this:
AuthenticationContext authenticationContext = new AuthenticationContext("https://<adfs-sts-server>/<rest-api-host>", false);
var result = authenticationContext.AcquireTokenAsync(<rest-api-resource-uri>, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
The AcquireTokenAsync call returns an error, saying: The browser based authentication dialog failed to complete. Reason: The server has not found anything matching the requested URI (Uniform Resource Identifier).
Can anyone tell me:
Is the "requested URI" refered to in the error the https://<adfs-sts-server>/<rest-api-host> or <rest-api-resource-uri>?
Do I need to register <rest-api-host> or <rest-api-resource-uri> with ADFS in some way, and if so how?
Any other information I need to get this to work?
Thanks!
Peter
Using Active Directory Federation Services (ADFS) to provide authentication for on-premise endpoints from a Windows Client
Configuring ADFS
There are 2 parts to configuring ADFS.
Register the client application with ADFS
ADFS needs to be able to identify the application requesting user authentication, whether it be a service, WPF application, Web client or Office Add-in. I have gone generic and added the following client, which we can use for most of our C# requests; we may need to register a new client with different callback for Web clients.
Use one of the many tools out there to generate a GUID for the client ID.
* CLIENT_ID and APP_NAME should be unique.
* For a web client the redirect URI is where the auth service will redirect your call after authenticating the user. It should be an endpoint where you can process the token and continue with your client application. The redirect URI is not really used with rich clients/services/add-ins.
CLIENT_ID = 26E54EC9-7988-4DAE-A527-483A8A78B1C6
APP_NAME = Investplus
DESCRIPTION = Invest+ rich client suite
REDIRECT_URI = https://server/redirect-adfs.html
Instructions for Client registration
(may be possible in a wizard, but this is what I found on the web and it worked fo us)
Log on to the AD FS server as administrator and open a Windows PowerShell command window.
Enter the following command. In Windows PowerShell
Add-AdfsClient -ClientId <CLIENT_ID> -Name <APP_NAME> -RedirectUri <REDIRECT_URI>
Register the resource to be accessed ('Relying Party' in ADFS speak)
I found this link useful, it takes you through the steps of the wizard for setting up a relying party.
Instructions for Relying Party registration
The administrator on the server team will need to use the ADFS Add Relying Party Trust Wizard, and under the "Select Data Source" step select Enter data about the relying party manually.
Values you need to supply for this wizard:
DISPLAY_NAME = "MyInvestApi" (Unique display name for this Relying party)
PROFILE = "AD FS Profile"
ENABLE_SUPPORT_FOR_WS-FEDERATION_PASSIVE_PROTOCOL = true
URL = "https://server/api" (Unique URL for this RP)
ADD_ONE_OR_MORE_IDENTIFIERS = eg. "urn:myInvestApi" and "https://server/api"
ACCEPT_REMAINING_DEFAULTS
when given the opportunity, Add Claim Rules:
SEND_LDAP_ATTRIBUTES_AS_CLAIMS = true
ATTRIBUTE_STORE = Active Directory
SELECT_USEFUL_ATTRIBUTES = User-Principal-Name; Email; Display-Name
Configuring/Coding the Client application
Microsoft provides Active Directory Authentication Libraries (ADAL) for a range of platforms and languages from C# to Javascript, and from iOS to Cordova to Node.
The API exposed has changed significantly in each major version: I am using the latest C# library, currently 3.13.5.
The library makes the coding very simple, just a few lines; where I had problems was:
I couldn't find an explanation of what URL to use for the ADFS
Secure Token Service (STS)
I couldn't find documentation of the whole process as I am doing here (most documentation focussed on Azure FS), I struggled to work out
how the values provided to ADFS for Client and Relying party mapped
to the values used in the code.
What is the ADFS endpoint/URL to use in code?
Microsoft's best practice is to name your ADFS/STS server URL https://sts.domain.com (some people use https://adfs.domain.com, ask your server admins). However, if you try to hit this from a browser you'll get a 404 - Not found and trying to retrieve a token in the code, the ADAL library reports:
The browser based authentication dialog failed to complete. Reason: The server has not found anything matching the requested URI (Uniform Resource Identifier).
This is how I found the endpoint to use:
ADFS pubishes federation metadata at 'https://sts.domain.com/federationmetadata/2007-06/federationmetadata.xml'
Extract this file and open in a text editor.
When configuring the Relying Party, we specified "Enable Support for WS-Federation Passive Protocol" when specifying our resource endpoint, so search the XML for PassiveRequestorEndpoint.
Use the <Address> from this node - in my case https://sts.domain.com/adfs/ls/. I don't know if this will always be the value, or if it is specified when ADFS is setup and therefore potentially different per site.
What other values to use in the code?
We want our client app to retrieve a JSON Web Token (JWT) from ADFS which we can pass to our protected resource for authentication/authorization purposes.
At its most simple, the access token can be retrieved in 3 lines of code + configuration, and this will show how to translate what we have configured in ADFS to the values required by ADAL:
var stsEndpoint = "https://sts.domain.com/adfs/ls/";
var relyingPartyIdentifier = "urn:myInvestApi"; // Tenant in Azure AD speak, but this is an on-premise service
var authority = stsEndpoint + relyingPartyIdentifier;
var restResourceUrl = "https://server/api";
var redirectUri = "https://server/redirect-adfs.html";
const string CLIENT_ID = "26E54EC9-7988-4DAE-A527-483A8A78B1C6";
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
var asyncRequest = authenticationContext.AcquireTokenAsync(restResourceUrl, CLIENT_ID, redirectUri, new PlatformParameters(PromptBehavior.Auto));
var accessToken = asyncRequest.Result.AccessToken;
Useful references
ASP.NET Core Token Authentication Guide
ADAL - Native App to REST service - Authentication with ACS via Browser Dialog
Create a line-of-business Azure app with AD FS authentication
OAuth 2 Simplified
To issue the token for the web API, we need to make the ADFS to aware it by creating a relying party trust for the web API. And when we add a replying party we need to specify the identifiers for the replying party like figure below(Windows Server 2012 R2):
Then we can use this identifiers as the resource URI to acquire the token for this replying party. Please ensure that the resource URI is correct as you config like figure above.
And here is an article about developing with ADFS using OAuth:
Developing Modern Applications using OAuth and Active Directory Federation Services
Depending on the version of asdf, you may be able to use 'discovery' to obtain the endpoints to use.
Have a look at this post for more details: http://www.cloudidentity.com/blog/2015/08/21/openid-connect-web-sign-on-with-adfs-in-windows-server-2016-tp3/

How to Consume WebApi WebService that Requires Redirect for Login

We have a desktop application that requires authentication with a server in order to operate. This application prepares and sends a query to a webservice, the user is prompted from this webservice to log in and the webservice returns an XML document with application subscription information (Software-as-a-Service Subscription License).
I've created a WebApi webservice that does the following:
Accept incoming request at /api/client?[MACHINEINFOINQUERYSTRING]
Redirect to external authentication provider (think GoogleId or
similar)
Authentication provider sends information back to
/api/subscription/[AUTHENTICATIONID]
The /api/subscription endpoint returns an XML document after pulling info from the servers (or including appropriate error message).
This webservice works and the XML document can be viewed in the browser. I've created a website with a default.aspx to test this, automatically redirecting to the /api/client and it does display this XML document in the browser.
The desktop application properly makes the initial call, redirects through an embedded browser to the login page, and receives an XML, but this XML cannot be parsed. The application team simply gets a "download" option for the XML but cannot capture the response for stuffing into an XmlDocument object. I've attempted to create an example application to instruct the app team, but have had no success.
Questions:
Do I have this architecture fundamentally wrong or do we simply not know how to consume the response properly?
How do I capture and consume the XML that is successfully returned?
As an example of what I've tried:
string requestString = string.Format("http://[server]/api/client?{0}", HttpUtility.HtmlEncode(queryString));
Response.Redirect(requestString);
This works in the browser, displays the login page, allows for input, redirects to the subscription endpoint which then prepares and delivers the XML to the browser. Unfortunately, this is unusable by a consumer.
HttpWebRequest request = WebRequest.Create(requestString) as HttpWebRequest;
request.AllowAutoRedirect = true;
request.MaximumAutomaticRedirections = 20;
request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.None;
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
This doesn't work. The response.ResponseUri has the properly formatted address of the OAuth service (step 2 above). It does not display the login page to the user even though this is all initiated through a browser.
I've also tried using a WebRequest POST, HttpClient PostAsync and several other methods, but:
The response URL is simply the location of the login page. If I string together 3 WebRequest/WebResponse pairs, it fails because the user isn't being properly authenticated at the first request / initial redirect.
What does work in my default.aspx:
I haven't found an example online for my specific needs, but the pattern must exist in practice as plenty of websites utilize OAuth style logins. I've utilized webservices (like OData endpoints) that also require logins, so this pattern must exist for webservices too. I do send back a properly formatted XML document. We just don't know how to capture and consume that document.
Any examples of a similar architecture would be highly appreciated! Or pointing me in the right direction.
Edit ---
I'm thinking that somehow the request.GetResponse() isn't really allowing for redirects and/or since it's an HttpWebRequest, there's no way it will allow for user input.
What's the proper way to make this call and consume the XML from another application? the XML is delivered properly in a browser window (with Response.Redirect) but no login window opens using an HttpWebRequest.
The answer is: This architecture is fundamentally wrong.
OAuth architecture leaves the authorization to the client, which then sends an authorization token to all subsequent services that it requires. The services are simple endpoints and do not contain any authentication logic, (although the service itself is allowed to validate the authentication token with the OAuth server).
The proper sequence of events for this answer should be:
1) Desktop Application makes OAuth authentication request to authentication server.
2) A successful response includes an authorization token which contains identity information, permissions, validity period, etc.
3) The desktop app then requests information from the WebApi service, sending in the request that token.
4) WebApi takes this authentication token, validates it (in my case against a certificate) and may even query the OAuth server again to ensure that the token is still valid.
5) If valid, the web service gathers the data and sends it back to the server.
My problem was I was expecting the subscription service itself to be able to open a web browser, prompt for a login, and then continue the request to another endpoint (2 redirects after initial request). I was in effect, breaking both the WebApi and OAuth 2 designs. Although it worked from the browser, it was not consumable from an application.
After redesigning to this simpler pattern, my web service is now consumable.

How to configure a WCF client using basicHttpBinding and Username authentication over https

I'm trying to consume a web service provided by another group within our company. The service is written in Java and their JAVA test client functions properly. However, I cannot get a .NET4.0 or .NET 4.5 based WCF client to talk to it. They require https transport and since its JAVA I'm using basicHttpBinding. They also require Username/Password. In JAVA, they are saying the username/password is at the binding level and when looking at a message that was captured from their test code, there is NO evidence of the username or password being sent. I can connect to their service but cannot get authenticated because they are not seeing my username & password. Username and password is being set in code. I am currently testing against their dev server using their self signed cert. Our endpoints are physically 1500 miles apart but connect via internal corporate (secure) network.
I just need to know the proper config settings for "binding", "behavior", and "endpoint" to use at the client. I can post my settings but that would just be misleading as I've tried 100's of them and none give the desired result.
Any help you can give will be useful as long as it doesn't involve any changes on (or knowledge of) the service host side (its probably not even IIS).
Thanks for your thoughts.
Java client code looks like this:
ServiceImplServiceLocator locator = new ServiceImplServiceLocator();
locator.setServiceEndpointAddress( "https://<hostaddr>:8443/Service" );
locator.setServiceWSDDServiceName("Service");
ServiceImpl service = locator.getService();
ServiceSoapBindingStub binding = (ServiceSoapBindingStub) service;
binding.setUsername("Username");
binding.setPassword("Password");
// call web service
Return = binding.ServiceMethod( ... );

How to make a call to secured web service(HTTPS) using basic http binding?

I need to import some model file into a Sharepoint central admin(HTTPS) from my local machine. What should be the configuration(using service.model)?
I am using the following configuration...
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_BusinessDataCatalogSharedService"
contract="BusinessDataCatalogSharedService" name="BasicHttpBinding_BusinessDataCatalogSharedService" />
And i am using the following c# code...
BusinessDataCatalogSharedServiceClient client =
new BusinessDataCatalogSharedServiceClient("BasicHttpBinding_BusinessDataCatalogSharedService1");
client.ClientCredentials.UserName.UserName = "...";
client.ClientCredentials.UserName.Password = "....";
I am getting the following error
MessageSecurityException was unhandeled
The HTTP request was forbidden with client authentication scheme 'Basic'.
I dont have enough knowledge about the authentication...Please help me out.
Thanks
Take a look at this information http://blog.adnanmasood.com/2008/07/16/https-with-basichttpbinding-note-to-self/
Most likely the problem is that you are running the service over HTTP not HTTPS. This will not work with username/password authentication. WCF is secure by default such that it will disallow calls with this type of authentication over HTTP.
You need to configure your service with a certificate to run over HTTPS and then also make the appropriate changes to you config (as is described in the link renu has posted).

Categories

Resources