Implementing ACS with and ADFS as STS - c#

We are attempting to use the ACS sample 4 (from http://claimsid.codeplex.com/) as template for our ADFS project.
We have no problem with passive requests to ADFS authenticated services. In the sample, the federation provider is a custom STS, and the sample works fine.
Now we wish to replace the custom federation provider (Adatum FP in the sample) with our own ADFS.
Our setup right now is as follows (Namespaces hidden)
ServiceClient: Console Applicaton, calls Services
Services: WCF Webservice, Single method returning a string. This as is default
[Ordertracking.Services in sample]
Services.Authentication: Our custom Identity Provider. This is as default [Litware.SimulatedIssuer in sample]
ADFS: Our Federation Provider [FederationProvider.Adatum in
example]
ServiceClient wants to call Services, and from configuration it knows it has to get a token from IP (Services.Authentication). The token is then passed to ADFS, who validates the token
and sends a new token back to ServiceClient. The client new passes the FP token to the service, and the service (being a relying party on ADFS) verifies the token against ADFS, and performs the service method.
The issue:
Replacing the STS in the example with ADFS, seems to break the integration. We seem to be getting the token from the IP back correctly, but we are running into problems when passing the IP token to ADFS.
It seems that we have a problem with our Audience Uri's, but we have added
https://'adfs fqdn'/adfs/services/Trust/13/IssuedTokenMixedSymmetricBasic256
Client Exception
We get an MessageSecurityException in the client with this InnerException
InnerException
{"ID3242: The security token could not be authenticated or authorized."}
[System.ServiceModel.FaultException]: {"ID3242: The security token could not be authenticated or authorized."}
Data: {System.Collections.ListDictionaryInternal}
HelpLink: null
InnerException: null
Message: "ID3242: The security token could not be authenticated or authorized."
Source: null
StackTrace: null
TargetSite: null
ADFS Debug log
<TraceRecord xmlns="http://schemas.microsoft.com/2009/10/IdentityModel/TraceRecord" Severity="Error">
<Description>Handled exception.</Description>
<AppDomain>Microsoft.IdentityServer.ServiceHost.exe</AppDomain>
<Exception>
<ExceptionType>Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</ExceptionType>
<Message>ID1038: The AudienceRestrictionCondition was not valid because the specified Audience is not present in AudienceUris. Audience: 'https://<adfs fqdn>/adfs/services/Trust/13/IssuedTokenMixedSymmetricBasic256'</Message>
<StackTrace>
at Microsoft.IdentityModel.Tokens.SamlSecurityTokenRequirement.ValidateAudienceRestriction(IList`1 allowedAudienceUris, IList`1 tokenAudiences) at Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateConditions(Saml2Conditions conditions, Boolean enforceAudienceRestriction) at Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateToken(SecurityToken token) at Microsoft.IdentityServer.Service.Tokens.MSISSaml2TokenHandler.ValidateToken(SecurityToken token) at Microsoft.IdentityModel.Tokens.WrappedSaml2SecurityTokenAuthenticator.ValidateTokenCore(SecurityToken token) at System.IdentityModel.Selectors.SecurityTokenAuthenticator.ValidateToken(SecurityToken token) at Microsoft.IdentityModel.Tokens.WrappedSamlSecurityTokenAuthenticator.ValidateTokenCore(SecurityToken token) at System.IdentityModel.Selectors.SecurityTokenAuthenticator.ValidateToken(SecurityToken token) at System.ServiceModel.Security.ReceiveSecurityHeader.ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver, IList`1 allowedTokenAuthenticators, SecurityTokenAuthenticator&amp; usedTokenAuthenticator) at
....
</StackTrace>
</Exception>
</TraceRecord>
We have added the audience uri to our IP Web.config:
<audienceUris mode="Always">
<add value="https://<adfs fqdn>/adfs/services/Trust/13/IssuedTokenMixedSymmetricBasic256" />
</audienceUris>
If necessary we can post additional config files and screenshots of ADFS configuration.

This took a bit of work but we finally solved the problem. Instead of configuring this, we built the connection in code. I'm thinking we probably had an error somewhere in the client configuration.
Some advice to anyone trying this - build the connections in code first. The XML configuration is a bit harder to work with.
We found some sample code on leastprivilege.com
private static SecurityToken GetIdPToken()
{
var factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
"https://systemidp.dk/Issuer.svc");
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.UserName.UserName = "LITWARE\\rick";
factory.Credentials.UserName.Password = "thisPasswordIsNotChecked";
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress("https://adfsfqdn/adfs/services/trust"),
KeyType = WSTrust13Constants.KeyTypes.Symmetric,
ReplyTo = "https://adfsfqdn/adfs/services/trust/13/issuedtokenmixedsymmetricbasic256/"
};
factory.ConfigureChannelFactory();
var channel = factory.CreateChannel();
return channel.Issue(rst);
}
private static SecurityToken GetRSTSToken(SecurityToken idpToken)
{
var binding = new IssuedTokenWSTrustBinding();
binding.SecurityMode = SecurityMode.TransportWithMessageCredential;
var factory = new WSTrustChannelFactory(
binding,
"https://adfsfqdn/adfs/services/trust/13/issuedtokenmixedsymmetricbasic256/");
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.SupportInteractive = false;
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress("https://services.dk/WebService.svc"),
KeyType = WSTrust13Constants.KeyTypes.Symmetric
};
factory.ConfigureChannelFactory();
var channel = factory.CreateChannelWithIssuedToken(idpToken);
return channel.Issue(rst);
}
Creating a WCF call with the token
var ipdtoken = GetIdPToken();
var stsToken = GetRSTSToken(ipdtoken);
var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.EstablishSecurityContext = false;
var factory = new ChannelFactory<IWebService>(binding, "https://services.dk/WebService.svc");
factory.ConfigureChannelFactory();
factory.Credentials.SupportInteractive = false;
var serviceChannel = factory.CreateChannelWithIssuedToken(stsToken);
var s = serviceChannel.GetUserInformation();

The audienceUri configuration on your IP looks fine. I think ADFS is the one that's throwing the ID3242 fault. Can you check to make sure your IP is correctly configured under Claim Provider Trusts on your ADFS server?
If you have your IP's federation metadata handy, you could also try recreating it in ADFS.

Related

gRPC and ASP Net Core: using SslCredentials with non-null arguments is not supported by GrpcChannel

I am trying to connect from a client to the service. The service is configurated to use a self signed Ssl certificate and I am trying to configurate the client with the client certificate. I am using this code:
string cacert = System.IO.File.ReadAllText("certificados/ca.crt");
string cert = System.IO.File.ReadAllText("certificados/client.crt");
string key = System.IO.File.ReadAllText("certificados/client.key");
KeyCertificatePair keypair = new KeyCertificatePair(cert, key);
SslCredentials sslCreds = new SslCredentials(cacert, keypair);
var channel = GrpcChannel.ForAddress("https://x.x.x.x:5001", new GrpcChannelOptions { Credentials = sslCreds });
var client = new Gestor.GestorClient(channel);
But I am getting the following error: using SslCredentials with non-null arguments is not supported by GrpcChannel.
I don't understand very good the message error. SslCredentials is ChannelCredentials? type, and SslCreds is Grpc.Core.SslCredentials. It can be compiled, so the type I guess it is correct.
What I would like to know it is how I can configure the client to use the self signed certificate that I have created.
Thanks.
The SslCredentials support in only available grpc-dotnet is to provide some level of compatibility with Grpc.Core in the most common use case, it doesn't expose all the functionality though. In grpc-dotnet, only SslCredentials() (parameterless which uses the default roots) is supported. If you want to provide your self-signed creds, you can certainly do that, you'll need to use a different API for configuring GrpcChannel:
See example here (creating a GrpcChannel with custom credentials).
https://github.com/grpc/grpc-dotnet/blob/dd72d6a38ab2984fd224aa8ed53686dc0153b9da/testassets/InteropTestsClient/InteropClient.cs#L170
I spend a fair bit of time googling around for solutions to this problem, and didn't find a concise answer. Here is ultimately how I was able to configure a dotnet client to use mutual SSL authentication:
MyService.MyServiceClient GetClient(){
var httpClientHandler = new HttpClientHandler();
// Validate the server certificate with the root CA
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, _) => {
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
chain.ChainPolicy.CustomTrustStore.Add(new X509Certificate2("ca.crt"));
return chain.Build(cert);
};
// Pass the client certificate so the server can authenticate the client
var clientCert = X509Certificate2.CreateFromPemFile("client.crt", "client.key");
httpClientHandler.ClientCertificates.Add(clientCert);
// Create a GRPC Channel
var httpClient = new HttpClient(httpClientHandler);
var channel = GrpcChannel.ForAddress("https://localhost:8080", new GrpcChannelOptions{
HttpClient = httpClient,
});
return new MyService.MyServiceClient(channel);
}

.Net Core 2.0 call to WCF client configuration

I have a .NET Core 2.0 application and need to call a WCF client from one of its controllers, and pass the user credentials for authentication.
Within the .net core app I created a reference for the WCF client using the Connected Services (WCF Web Service Reference Provider) and now in a process of configuring the call. Note that I can use the same endpoint form a 4.6 framework application without any problems.
Here's my code:
var binding = new BasicHttpBinding {Security = {Mode = BasicHttpSecurityMode.Transport}};
var address = new EndpointAddress("https://my-endpoint.asmx");
var client = new MyAppSoapClient(binding, address);
var credentials = CredentialCache.DefaultNetworkCredentials;
client.ClientCredentials.Windows.ClientCredential = credentials;
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
var response = client.GetStuff("param").Result;
I face a number of problems:
It has to be a https call
I need to pass the currently log in user credentials to the call
The current error I get is as follows:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Negotiate, NTLM'
Also the ConnectedService.json (created automativcally by WCF Web Service Reference Provider) has a predefined endpoint Uri.. I don't understand why I need to pass the address to the client manually (the code seems to be forcing me to do so).. ideally I'd like to get this dynamically amended in json depending on environment.
Thanks.
I noticed that you passed the current logged-in user as a Windows credential (which is also necessary for enabling impersonation), but you did not explicitly set the client credentials for the transport layer security.
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
Also the ConnectedService.json (created automativcally by WCF Web
Service Reference Provider) has a predefined endpoint Uri.. I don't
understand why I need to pass the address to the client manually (the
code seems to be forcing me to do so)
You can modify the method of automatic generation of proxy client to construct client proxy class (located in the reference.cs)
Modify the binding security
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.WebService1Soap))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
result.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows;
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
return result;
}
Modify the endpoint.
private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.WebService1Soap))
{
return new System.ServiceModel.EndpointAddress("http://10.157.13.69:8001/webservice1.asmx");
Construct the client proxy class.
ServiceReference1.WebService1SoapClient client = new WebService1SoapClient(WebService1SoapClient.EndpointConfiguration.WebService1Soap);
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
client.ClientCredentials.Windows.ClientCredential.UserName = "administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "123456";
Feel free to let me know if there is anything I can help with.
My binding was missing the security Ntlm credential type (see below).
Problem solved.
var binding = new BasicHttpBinding {Security = {Mode = BasicHttpSecurityMode.Transport,
Transport = new HttpTransportSecurity(){ClientCredentialType = HttpClientCredentialType.Ntlm } }};

Authenticating against ReportExecution2005.asmx in .NET Core

I'm trying to execute an SSRS report in .NET Core.
Since .NET Core doesn't let you add service references, you have to use the WCF Connected Service to add a reference to the WSDL so it can generate .NET Core compatible code. This is what I did for ReportExecution2005.asmx (SQL Server 2016 if it matters).
I tried using the following to authenticate against the service:
var rsExec = new ReportExecutionServiceSoapClient(ReportExecutionServiceSoapClient.EndpointConfiguration.ReportExecutionServiceSoap,
new EndpointAddress("http://server/ReportServer/ReportExecution2005.asmx"))
{
ClientCredentials =
{
Windows =
{
AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation,
ClientCredential = new NetworkCredential("username", "password")
}
}
};
Also tried setting the Username object instead of Windows object, but either way the result is the following error:
MessageSecurityException: The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM'.
Looking at Fiddler, the code isn't passing the credentials along.
This is the code that got generated off the WSDL
public ReportExecutionServiceSoapClient(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress)
: base(ReportExecutionServiceSoapClient.GetBindingForEndpoint(endpointConfiguration), remoteAddress)
{
this.Endpoint.Name = endpointConfiguration.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}
static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);
I may be mistaken, but isn't this calling the private method ConfigureEndpoint with the ClientCredentials object before the ClientCredentials object has even been set?
I'm not seeing any other way to configure the ClientCredentials or call ConfigureEndpoint, so how exactly are you supposed to authenticate? The other constructors are basically the same thing, except for one which takes in a Binding instead of an EndpointConfiguration. Any ideas?
After fighting with this for a day, I found an approach that seems to work, by using the only constructor that does not immediately call ConfigureEndpoint as pointed out in the question. If I create a binding that specifies NTLM, and I pass that binding along with a manually created endpoint, it works:
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly)
{
Security =
{
Transport = new HttpTransportSecurity {ClientCredentialType = HttpClientCredentialType.Ntlm}
}
};
var reportService = new CssbiReportService.ReportExecutionServiceSoapClient(binding,
new EndpointAddress("http://myserver/ReportServer/ReportExecution2005.asmx"));
This is working for me in .NET Core.
Edit: update the code for .NET Core
Unfortunately, I don't have SSRS here to test the code right now.
But, try this code (no error check):
// parameters of report (if any)
ParameterValue[] parameters = {new ParameterValue {Name = "ApontamentoID", Value = "364"}};
// connect to the service
ReportExecutionServiceSoapClient webServiceProxy =
new ReportExecutionServiceSoapClient(
ReportExecutionServiceSoapClient.EndpointConfiguration.ReportExecutionServiceSoap,
"http://report_server_url/ReportExecution2005.asmx?wsdl");
// logon the user
await webServiceProxy.LogonUserAsync("username", "password", null);
// ask for the report
await webServiceProxy.LoadReportAsync("/report_path", null);
await webServiceProxy.SetExecutionParametersAsync(parameters, null);
// you can use RenderStreamRequest too
RenderRequest request = new RenderRequest("pdf", null);
RenderResponse response = await webServiceProxy.RenderAsync(request);
// save to the disk
System.IO.File.WriteAllBytes(#"c:\temp\output.pdf", response.Result);
// logoff the user
await webServiceProxy.LogoffAsync();
// close
await webServiceProxy.CloseAsync();
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly)
{
Security =
{
Transport = new HttpTransportSecurity {
ClientCredentialType = HttpClientCredentialType.Ntlm
}
}
};
yourClient = ReportExecutionServiceSoapClient(rsBinding, rsEndpointAddress) {
ClientCredentials =
{ ...
^^^ This for NTLM.
Also, I was getting read-only errors trying to set some properties on the client after it had been created. In case it helps someone, properties must all be set at client-creation time to avoid this as per "yourClient" above.
I had the same problem, for me the following addition was helpful:
ReportExecutionServiceSoapClient rsClient = new ReportExecutionServiceSoapClient(rsBinding, rsEndpointAddress);
rsClient.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

EWS: "The remote server returned error (401) Unauthorized"

I'm trying to find a single item fronm all items in the current context, but I seem to constantly get this error message:
The request failed. The remote server returned an error: (401) Unauthorized.
First, I set everything up to access the exchange service:
var signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationResult authenticationResult = null;
AuthenticationContext authenticationContext = new AuthenticationContext(
SettingsHelper.Authority, new model.ADALTokenCache(signInUserId));
authenticationResult = authenticationContext.AcquireToken(
SettingsHelper.ServerName,
new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret));
ExchangeService exchange = new ExchangeService(ExchangeVersion.Exchange2013);
exchange.Url = new Uri(SettingsHelper.ServerName + "ews/exchange.asmx");
exchange.TraceEnabled = true;
exchange.TraceFlags = TraceFlags.All;
exchange.Credentials = new OAuthCredentials(authenticationResult.AccessToken);
And then I define what Item I want to receive (by ID):
ItemView view = new ItemView(5);
view.PropertySet = new PropertySet(BasePropertySet.IdOnly);
var tempId = id.Replace('-', '/').Replace('_', '+');
SearchFilter.IsEqualTo searchid = new SearchFilter.IsEqualTo(ItemSchema.Id, tempId);
And last but not least, I try to search for this item, within my items:
FindItemsResults<Microsoft.Exchange.WebServices.Data.Item> results = exchange.FindItems(WellKnownFolderName.Inbox, searchid, view);
And this is where my error happens. I've tried various other ways of doing this, but no matter what I do, I get unauthorized.
Could someone maybe guide me in the correct way, in order to solve this issue?
EDIT
I do receive the access token from the:
authenticationResult = authenticationContext.AcquireToken(
SettingsHelper.ServerName,
new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret));
as I can see by debugging the code.
No refresh token is present though, and I don't know if this has something to say?
EDIT
I just managed to debug all my way into the exchange.ResponseHeaders in where I saw this:
The access token is acquired using an authentication method that is
too weak to allow access for this application. Presented auth
strength was 1, required is 2
I decoded the JWT, as this is my result:
{
typ: "JWT",
alg: "RS256",
x5t: "MnC_VZcATfM5pOYiJHMba9goEKY",
kid: "MnC_VZcATfM5pOYiJHMba9goEKY"
}.
{
aud: "https://outlook.office365.com/",
iss: "https://sts.windows.net/d35f5b06-f051-458d-92cc-2b8096b4b78b/",
iat: 1445416753,
nbf: 1445416753,
exp: 1445420653,
ver: "1.0",
tid: "d35f5b06-f051-458d-92cc-2b8096b4b78b",
oid: "c5da9088-987d-463f-a730-2706f23f3cc6",
sub: "c5da9088-987d-463f-a730-2706f23f3cc6",
idp: "https://sts.windows.net/d35f5b06-f051-458d-92cc-2b8096b4b78b/",
appid: "70af108f-5c8c-4ee4-a40f-ab0b6f5922e0",
appidacr: "1"
}.
[signature]
Where to go from here?
I already got this error while using EWS in the past "The access token is acquired using an authentication method that is too weak to allow access for this application. Presented auth strength was 1, required is 2"
What you need to do is to enforce your authentication with a certificate.
AuthenticationContext authContext = new AuthenticationContext(authority);
exchangeService.Credentials = new OAuthCredentials(authContext.AcquireToken("https://outlook.office365.com", new ClientAssertionCertificate(ConfigurationManager.AppSettings["ida:ClientId"], certificate)).AccessToken);
The key part is to define a new ClientAssertionCertificate as you ClientAssertion.
You will also have to modify the manifest of your Azure Active Directory Application.
Looks at this reference (the part about "Configuring a X.509 public cert for your application") : https://msdn.microsoft.com/en-us/office/office365/howto/building-service-apps-in-office-365

How to get specific RequestSecurityToken serialization

I'm new to WCF, .NET, web services and everything - in fact, I was mainly a java & SQL coder until I took on my current job.
The task at hand: Portions of our customers' data in our database needs to be exported regularly to a database that is provided by a third party and accessible through a web service, which in turn is federation-secured with an STS provided by a fourth party.
After hours spent with confusing MSDN documentation and tons of blog posts on WCF, I still can't get the STS to talk to me, all I get is a (400) Bad Request..
My code:
private static SecurityToken RequestSecurityToken()
{
WSHttpBinding binding = new WSHttpBinding();
binding.AllowCookies = true;
WSHttpSecurity security = new WSHttpSecurity();
security.Mode = SecurityMode.TransportWithMessageCredential;
security.Message.ClientCredentialType = MessageCredentialType.Certificate;
security.Message.NegotiateServiceCredential = true;
security.Message.EstablishSecurityContext = false;
binding.Security = security;
WSTrustChannelFactory factory = new WSTrustChannelFactory(binding, "https://FOURTH_PARTY_STS");
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.TrustedPeople, X509FindType.FindBySubjectName, "CUSTOMER_CERTIFICATE");
RequestSecurityToken rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointReference("https://THIRD_PARTY_WS"),
TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"
};
rst.Claims.Dialect = "http://docs.oasis-open.org/wsfed/authorization/200706/authclaims"; // Taken from an exception message
rst.Claims.Add(new RequestClaim("urn:tgic:names:ISTS:1.0:user:PartnerId", false, "CUSTOMER_ID"));
return factory.CreateChannel().Issue(rst);
}
produces this SOAP body:
<trust:RequestSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Address>https://THIRD_PARTY_WS</wsa:Address>
</wsa:EndpointReference>
</wsp:AppliesTo>
<trust:Claims Dialect="http://docs.oasis-open.org/wsfed/authorization/200706/authclaims" xmlns:auth="http://docs.oasis-open.org/wsfed/authorization/200706">
<auth:ClaimType Uri="urn:tgic:names:ISTS:1.0:user:PartnerId" Optional="true">
<auth:Value>CUSTOMER_ID</auth:Value>
</auth:ClaimType>
</trust:Claims>
<trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>
<trust:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</trust:TokenType>
</trust:RequestSecurityToken>
According to the fourth party's documentation, the STS expects something like this:
<wst:RequestSecurityToken
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsp15="http://www.w3.org/ns/ws-policy"
xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
Context="1aae57c8-092c-47a4-a5eb-c2ecbc21441d">
<wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</wst:TokenType>
<wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>
<wsp:AppliesTo>
<wsp15:URI>https://THIRD_PARTY_WS</wsp15:URI>
</wsp:AppliesTo>
<wst:Claims Dialect="urn:oasis:names:tc:SAML:2.0:assertion:AttributeStatementType">
<saml2:AttributeStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:Attribute Name="urn:tgic:names:ISTS:1.0:user:PartnerId" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml2:AttributeValue>9330931615</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</wst:Claims>
<wst:Lifetime>
<wsu:Created>2013-09-17T18:18:10Z</wsu:Created>
<wsu:Expires>2013-09-17T18:23:10Z</wsu:Expires>
</wst:Lifetime>
</wst:RequestSecurityToken>
Somewhere else in that documentation it's stated that Context and Lifetime are optional. Therefore, as far as I can see, I have two issues:
How do I get the AppliesTo address to be serialized as URI element?
How do I get the Claims to be serialized as Attribute/AttributeValue elements (with correct dialect)?
Do I have to implement some custom serialization? If so, how and where do I hook it into the factory/binding/request?
To work with STS you have to use WS2007HttpBinding instead of WSHttpBinding. You can find STS example in WIF SDK. It contain an example with custom token. Look here
Try issue securitytoken without additional client parameters. Make a working solution that just issue token and call RP method.
As far as I understand you want pass to STS custom client credential PartnerId. In my project I'm using passing custom credentials through the request's AdditionalContext.
rst.AdditionalContext = new AdditionalContext();
rst.AdditionalContext.Items.Add(new ContextItem(new Uri("you_any_uri"), PartnerId));
This way does not require any serializers.
Another way to pass client credentials's: using RequestSecurityToken.Properties. In this case you must implement a custom request serializer.
var channelFactory = new WSTrustChannelFactory(binging, endpoint)
{
TrustVersion = TrustVersion.WSTrust13
};
channelFactory.WSTrustRequestSerializer = CustomRequestSerializer;
Here implementation of custom WSTrust13RequestSerializer
public class CustomRequestSerializer: WSTrust13RequestSerializer
{
public override void WriteXmlElement(XmlWriter writer, string elementName, object elementValue, RequestSecurityToken rst,
WSTrustSerializationContext context)
{
var parameters = new string[1] {"paramname"};
if (parameters.Any(p => p == elementName))
{
writer.WriteElementString(elementName, (string)elementValue);
}
else
{
base.WriteXmlElement(writer, elementName, elementValue, rst, context);
}
}
public override void ReadXmlElement(XmlReader reader, RequestSecurityToken rst, WSTrustSerializationContext context)
{
var parameters = new string[1] {"paramname"};
var key = parameters.FirstOrDefault(reader.IsStartElement);
if (!string.IsNullOrWhiteSpace(key))
{
rst.Properties.Add(key, reader.ReadElementContentAsString());
return;
}
base.ReadXmlElement(reader, rst, context);
}
}

Categories