I have little experience with cookies so i need some help here.
I have a scenario where i have to establish WCF connection to the remote endpoint using the https security. The situation is complicated by the thing that endpoint address in client config is actually pointing to a web proxy which redirects request to the actual endpoint if SSON auth has been detected or redirects to auth page.
Example: service address for client config is 'https://a.com/my.svc'. If auth is OK i will connect to this address, if not, i will be redirected to 'https://a.com/auth' and after successful auth will proceed again to 'https://a.com/my.svc'
If i open the address in the Web Browser i'm redirected to auth page and after successful auth (login/pwd) then redirected to the address. After the authenication the SSON cookie is generated and all consequent address queries lead me straight to the address with no additional auth.
As of now i've done the following:
Do auth using WPF WebBrowser control. I've got the cookie after that.
Made client endpoint connection. Yeah the session is used by default in prod environment, i'm not sure if i can remove it.
<customBinding>
<binding name="WSHttpsBinding_IDB2Connector">
<reliableSession inactivityTimeout="23:59:59" ordered="True"/>
<httpsTransport requireClientCertificate="false" />
</binding>
Direct connection works fine. I mean i don't connect to proxy address but use final endpoint address instead and don't use cookies.
Now the problem is how i can made my WCF connection to use SSON coookie to bypass auth page on ClientBase.Open call?
Once you have SSO cookie from auth form you can use OperationContextScope to send HTTP headers.
YourServiceClient client = new YourServiceClient();
using(new OperationContextScope(client.InnerChannel))
{
// Add a HTTP Header to an outgoing request
HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
requestMessage.Headers["Cookie"] = "MySSOCookie=MySSOCookieValue"; //SSOCookie content
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
client.doSomething();
}
Related
I'm having some issues implementing a client that talks to a WCF service. It's a WCF hosted by another company so I don't have access to its code. I used the Connected Service provider tool in Visual Studio to generate the client code so that I could make requests and everything works fine on my local machine. I am having an issue on our development environment where I receive the following error if I try to make a request with the client:
The HTTP request was forbidden with client authentication scheme 'Anonymous'
I've been looking at the client code (it's a lot of code) which is generated by the Provider tool and I think it may have something to do with the following block of code.
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
return result;
This more linked to firewall rules within corporate network.
I had same issue using non authorized proxy but got resolved secured proxy with ntlm ClientCredentialType
This error typically indicates that the WCF server authenticates the client-side with a certificate. The error will occur when the trust relationship between the server and the client have not been established yet.
In general, we need to provide client credential to be authenticated by the server so that be able to call the service. As for what kind of credentials need to be provided, it depends on the binding information on the server-side.
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
Namely, the above errors have indicated that the server authenticates the client with a certificate.
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
About authenticating the client with a certificate, you could refer to the below link for details.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/transport-security-with-certificate-authentication
Feel free to let me know if there is anything I can help with.
Thanks for all the suggestions. This was actually just caused by a firewall rule that was setup within our organisation. Once that was removed the code worked as expected.
result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
Security is provided using HTTPS. The service must be configured with SSL certificates. The SOAP message is protected as a whole using HTTPS. The service is authenticated by the client using the service's SSL certificate. The client authentication is controlled through the ClientCredentialType.
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.basichttpsecuritymode?view=netframework-4.8
Edit:
Here is my question reformulated:
I have a web server with secured api endpoints - one must have been authenticated with Google prior to using them. I implemented Challenge and Callback endpoints for that.
This works well from a browser with my SPA web front-end. The user gets redirected to the Google website to sign-in and then gets redirected back to my webapp; the browser then has the authenticated cookies and the webapp can use the endpoints to update its state.
I also have a WPF application that will communicate with the web server.
I want the WPF application to do the same as the web front-end: Use the web api endpoints after being authenticated with Google. The connection between the WPF application and my web server is done through an HttpClient.
My problem is I don't know how to authenticate that HttpClient connection between the WPF app and the web server.
I tried using the same Challenge endpoint but the response I get is of course the HTML from the Google Sign-In page, so I guess I can't use that with an HttpClient...
I also tried authenticating with GoogleApis from the WPF app and use the authenticated token to set cookies in the HttpClient but apparently this is not compatible.
How to authenticate an HttpClient connection to a web api with an external provider such as Google?
Original question:
From a WPF application, the user authenticates with Google with this code:
using Google.Apis.Auth.OAuth2;
...
public void Authenticate()
{
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "myClientId",
ClientSecret = "myClientSecret"
},
new[] { "email", "openid" },
"user",
CancellationToken.None).Result;
}
This works and the UserCredential object contains the authenticated token:
How to embed this token information in a web request made with an HttpClient in order to call my webapi endpoint?
I think the request must include some cookies to inform the server that it has been authenticated, but I don't know which ones exactly.
The endpoint on the server-side validates that the user is authenticated with the help of IdentityServer:
var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
If I got your question right, you just have to set the Authorization header
var credentials = await GoogleWebAuthorizationBroker.AuthorizeAsync(
clientSecrets,
new[] { "email", "openid" },
"user",
CancellationToken.None);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
credentials.Token.TokenType,
credentials.Token.IdToken);
Maybe you'll find below a helpful hint to better understand OpenID :)
The confusion stems from mixing GoogleApis and IdentityServer frameworks.
Authentication/authorization can be achieved using either of them.
Objects from Google.Apis.Auth.OAuth2 and IdentityServer4 namespaces are not designed to interact.
No manual cookie handling is necessary, for sure.
Ask yourself to whom does Google provide trust for the user. If it calls back to WPF, then webapi trusting WPF is a separate issue.
You answer your own question in the question:
the browser then has the authenticated cookies and the webapp can use
the endpoints to update its state
HttpClient needs to send those same cookies.
How do I set a cookie on HttpClient's HttpRequestMessage
If I understood your question right, then I faced the same problem not too long ago.
The way I implemented it is that in the backend, no matter who tries to access the endpoint, they had to send a Bearer X authorization token.
The token contained the identity of the client that wanted to access the resource, and I checked if he was permitted.
No matter what kind of client wants to access the endpoint, it just has to have that authroziation header in the request that he sends and the backend will treat it the same.
In my scenario, I used an authentication service that returns a cookie to the client with a certain JWT that contains the identity information.
Then from the client I send in every request the JWT received from the authentication service as an authorization header to the backend.
The reason I had to put the JWT that I receive from the service in a header, is that the authentication service and the backend service are not in the same domain, so cookies cant be shared.
This results in such design that no matter how you authenticate the client, the end result must be some sort of token that the backend can receive and read.
Hope this helps.
Scenario: User talks with WebApi called 'Gateway' by angularjs client. 'Gateway' is like a facade or a proxy, so all requests from User to 'Gateway' will be forwarded to another WebApis.
Security details: 'Gateway' WebApi and all others WebApis are placed in IIS with HTTPS binding and SSL client certificate option is 'Accept'. So, user will provide valid client certificate to IIS and after verification, request will be handled by WebApi.
The problem: when 'Gateway' WebApi receives a request, client certificate is presented in Request object. Then I just forward this request using HttpClient to another WebApi. But when another WebApi endpoint receives a request, there is not client certificate attached any more.
Below is a code snipped of request forwarding:
var request = Request; // income request from angularjs
var handler = new WebRequestHandler();
handler.ClientCertificates.Add(request.GetClientCertificate()); // setting up client certificate from user's request
using (var httpClient = new HttpClient(handler))
{
request.RequestUri = *chaning request address here*;
var response = await httpClient.SendAsync(request);
return ResponseMessage(response);
}
Note: If I try to go directly to the another WebApi, client certificate is presented as expected. If I try to go via 'Gateway', client certificate is presented as well in 'Gateway' request, but after it's successfully attached and request is sent, another WebApi does not receive any certificate attached.
Any ideas?
Thank you.
On your gateway server, you need to import an authentication key onto the "machine" certificate store (MMC.exe, add snap-in "Certificates", Computer account", Certficates-Personal-Certificates, import).
Grant permissions (for the auth cert) to the account which your IIS pool is using. (right-click the cert, All-Tasks, Manage private keys. Add, Advanced, Locations=[machine-name], find now, (probably) Network Service, Read).
In your web.config (on your gateway server), check your system.serviceModel / behaviors / endpointBehaviors / behavior / clientCredentials / clientCertificate. Make sure the attribute: storeLoction="LocalMachine", to use the cert from the machine key store.
I'm looking for the way to provide credentials (grammatically) for HTTP proxy that should be used to connect to service (Security Token Service), the proxy credentials differ from the credentials for the service.
I saw several posts here (it took me back to 2006-8) and the solution was by changing the default proxy
WebProxy proxy = new WebProxy("http://myproxyserver",true);
proxy.Credentials = new NetworkCredential("username", "password");
WebRequest.DefaultWebProxy = proxy;
I think it is risky to change the global setting for all WebRequests from my assembly that performs a dedicated task.
I'm wondering if in .NET 4.5 there is a better solution for this case.
Similar questions:
WCF Custom Http Proxy Authentication
How can I set an HTTP Proxy (WebProxy) on a WCF client-side Service proxy?
Don't set default proxy, set proxy object with desired credentials per request
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).