Getting lightweight SSO token from PingFederate using WCF or WIF - c#

I'm writing client using WCF for demo settings provided by PingFederate in the similar way as described in this post (Getting a Token from PingFederate using WIF).
My problem is that on the second step I need to exchange SAML token to lightweight SSO token on SP.
I'm getting exception while serialization of response from the SP
ID3135: The element 'TokenType' with namespace 'http://docs.oasis-open.org/ws-sx/ws-trust/200512' has value 'BASE64BINARY' which is not an absolute URI.
as I understand WS2007FederationHttpBinding can't handle that type of tokens.
I'm looking for explanation how such scenario should be handled.
The the client code is as following (after getting SAML token from IDP)
var binding = new WS2007FederationHttpBinding();
binding.Security.Mode = WSFederationHttpSecurityMode.TransportWithMessageCredential;
binding.Security.Message.IssuedKeyType = SecurityKeyType.BearerKey;
binding.Security.Message.EstablishSecurityContext = false;
var factory = new WSTrustChannelFactory(binding, new EndpointAddress(SP_EndPoint));
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.SupportInteractive = false;
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
//AppliesTo = new EndpointReference(#"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"),
};
RequestSecurityTokenResponse rstr;
var channel = factory.CreateChannelWithIssuedToken(idpToken);
SecurityToken spToken = channel.Issue(rst, out rstr);
return spToken;

Related

Salesforce Pub/Sub client in .NET

I would like to subscript to a Salesforce platform event. I am trying to create a client in C#/.NET for a Salesforce Pub/Sub API. There are examples in other languages but not in .NET : https://github.com/developerforce/pub-sub-api
I am using the Grpc.Net.Client nuget packages.
var topicName = "/event/SomeEvent__e";
var pubSubEndpoint = "https://api.pubsub.salesforce.com:7443";
var accessToken = "xxxx";
var organisationId = "xxxx";
var instanceUrl = "https://xxxxx.sandbox.my.salesforce.com";
var credentials = CallCredentials.FromInterceptor((c, m) =>
{
m.Add("accesstoken", accessToken);
m.Add("instanceurl", instanceUrl);
m.Add("tenantid", organisationId);
return Task.CompletedTask;
});
var options = new GrpcChannelOptions
{
Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
};
var channel = GrpcChannel.ForAddress(pubSubEndpoint, options);
var client = new PubSub.PubSubClient(channel);
var topicRequest = new TopicRequest()
{
TopicName = topicName
};
var topic = client.GetTopic(topicRequest);
I know my credentials are correct because I can use postman to hit the oauth2 endpoint and get a valid access token. But when I try and call a client method like client.GetTopic, then I get the following error.
Status(StatusCode="PermissionDenied", Detail="An error occurred while getting the metadata for org CORE/prod/00DN0000000c8Hk and topic /event/SomeEvent__e. Ensure the credentials and topic name are correct. rpcId: 21d854fb-17dc-4778-9524-6264bd1a920d")
Am I setting up the credentials object wrong? I cannot find any example of subscribing to a Salesforce Pub/Sub in .NET.

How to Query Azure Table storage using .Net Standard

I have a .Net Standard client application running on UWP.
My client application contacts the server that generates a sas key like so:
var myPrivateStorageAccount = CloudStorageAccount.Parse(mystorageAccountKey);
var myPrivateTableClient = myPrivateStorageAccount.CreateCloudTableClient();
SharedAccessTablePolicy pol = new SharedAccessTablePolicy()
{
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(59),
Permissions = SharedAccessTablePermissions.Query | SharedAccessTablePermissions.Add
};
CloudTable myPrivateTable = myPrivateTableClient.GetTableReference(tableName);
String sas = myPrivateTable.GetSharedAccessSignature(pol);
return sas;
My client application then runs the following:
StorageCredentials creds = new StorageCredentials(sas);
this.tableClient = new CloudTableClient(tableServiceURI, creds);
this.table = tableClient.GetTableReference(tableName);
TableQuery<DynamicTableEntity> projectionQuery = new TableQuery<DynamicTableEntity>().Select(new string[] { "DocumentName" }).Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, cc));
var res = await table.ExecuteQuerySegmentedAsync<DynamicTableEntity>(projectionQuery, null);
and gets the following error:
Server failed to authenticate the request. Make sure the value of
Authorization header is formed correctly including the signature. sr
is mandatory. Cannot be empty
but as this is tablestorage I dont think sr is required
and my SAS key looks fine to me:
?sv=2018-03-28&tn=MyTable&sig=RandomSig151235341543&st=2019-01-17T12%3A00%3A28Z&se=2019-01-17T12%3A59%3A28Z&sp=ra
so whats the problem here?
Ok so this is kind of stupid but I will post anyway.
I was sending the request to:
https://myaccount.blob.core.windows.net/
and should have been sending the request to:
https://myaccount.table.core.windows.net/

How do I send basic authentication credentials for a prem wcf https service over azure relay C#?

I have a WebAapp on Azure that sends a request to Azure Relay. It should transfer to a listener on premises WCF HTTPS service hosted on IIS that requires basic authentication. How do I send authorization basic header for the onprem WCF service over the Azure Relay . How do I send ? example,
"Authorization": "Basic 239837987XYC"
I have used channel factory,
var ChannelFactory<Overview.MyChannel> cf;
var relayNamespace ="myrelaynamespace";
var relayListener = "myrelaylistener";
var endPointAddress = new EndpointAddress(ServiceBusEnvironment.CreateServiceUri("https", relayNamespace, relayListener));
cf = new ChannelFactory<Overview.ItServiceManagementAOChannel>(binding, endPointAddress);
ClientCredentials loginCredentials = new ClientCredentials();
loginCredentials.UserName.UserName = "onpremWCFusername";
loginCredentials.UserName.Password = "onpremWCFpassword";
cf.Endpoint.Behaviors.Add(new TransportClientEndpointBehavior
{
TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(ConfigurationManager.AppSettings.Get("WcfRelayKeyName"), ConfigurationManager.AppSettings.Get("WcfRelayKey"))
});
cf.Endpoint.Behaviors.Add(loginCredentials);
I get the Error: The value could not be added to the collection, as the collection already contains an item of the same type: 'System.ServiceModel.Description.ClientCredentials'. This collection only supports one instance of each type.
Parameter name: item
using (var ch = cf.CreateChannel())
{
try
{
var resp = ch.CreateTaskAsync(req).Result;
}
}
Try to specify the windows credential as client credential.
factory.Credentials.Windows.ClientCredential.UserName = "administrator";
factory.Credentials.Windows.ClientCredential.Password = "123456";
IService sv = factory.CreateChannel();
Feel free to let me know if the problem still exists.

Need signature after SAML token in client request

I have a serialized SOAP request message with a SAML token holder-of-key that works against a vendor service. I want to create a demonstration program in C# to produce a similar request. To do this, I want to write a client that creates its own SAML token.
I've got a SAML2 token created successfully from a self signed cert and I am able to associate it to the request using the ChannelFactoryOperations.CreateChannelWithIssuedToken approach (.Net 4.0). Everything is working great but I can't figure out the C# required to place the signature after the assertion and use the SAML token as the signature KeyIdentifier to sign the timestamp. I'm not even sure what I am asking, but it seems like the signature after the token itself should be the easy part. But, the only way I've gotten the SAML to come out in the request is by declaring it of type BearerKey. But BearerKey appears to omit the signature after the Assertion. It seems I want SymmetricKey, but the token "has no keys." How do I make a signature element like this appear after the Assertion?
Here URI="#_1" is referring to the WS-Security timestamp (not shown) above.
Hi folks I can't believe I finally figured all of this out. This code loads up a self signed cert, generates a SAML token and then endorses the message with the SAML token. The problem I was having was with the "token has no keys" error. That was solved by creating an issuerToken and a key and passing that in to the token constructor. See below. I think the most helpful information I found online is this great post here http://devproconnections.com/development/generating-saml-tokens-wif-part-2
X509Certificate2 cert = new X509Certificate2("C:\\Users\\foobar\\desktop\\test.pfx", "test", X509KeyStorageFlags.MachineKeySet);
RSACryptoServiceProvider rsa = cert.PrivateKey as RSACryptoServiceProvider;
RsaSecurityKey rsaKey = new RsaSecurityKey(rsa);
RsaKeyIdentifierClause rsaClause = new RsaKeyIdentifierClause(rsa);
SecurityKeyIdentifier signingSki = new SecurityKeyIdentifier(new SecurityKeyIdentifierClause[] { rsaClause });
SigningCredentials signingCredentials = new SigningCredentials(rsaKey, SecurityAlgorithms.RsaSha1Signature, SecurityAlgorithms.Sha1Digest, signingSki);
Saml2NameIdentifier saml2NameIdentifier = new Saml2NameIdentifier("C=US,O=hi mom,CN=test", new System.Uri("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"));
Saml2Assertion saml2Assertion2 = new Saml2Assertion(saml2NameIdentifier);
saml2Assertion2.SigningCredentials = signingCredentials;
Saml2Subject saml2Subject = new Saml2Subject();
saml2NameIdentifier = new Saml2NameIdentifier("foo#bar.edu", new System.Uri("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"));
saml2Subject.NameId = saml2NameIdentifier;
Saml2SubjectConfirmationData subjectConfirmationData = new Saml2SubjectConfirmationData();
Saml2SubjectConfirmation subjectConfirmation = new Saml2SubjectConfirmation(new Uri("urn:oasis:names:tc:SAML:2.0:cm:holder-of-key"));
subjectConfirmation.SubjectConfirmationData = subjectConfirmationData;
subjectConfirmationData.KeyIdentifiers.Add(signingSki);
saml2Subject.SubjectConfirmations.Add(subjectConfirmation);
saml2Assertion2.Subject = saml2Subject;
Saml2AuthenticationContext saml2AuthCtxt = new Saml2AuthenticationContext(new Uri("urn:oasis:names:tc:SAML:2.0:ac:classes:X509"));
Saml2AuthenticationStatement saml2AuthStatement = new Saml2AuthenticationStatement(saml2AuthCtxt);
saml2AuthStatement.SessionIndex = "123456";
saml2Assertion2.Statements.Add(saml2AuthStatement);
Saml2AttributeStatement saml2AttStatement = new Saml2AttributeStatement();
Saml2Attribute saml2Attribute = new Saml2Attribute("urn:oasis:names:tc:xspa:1.0:subject:subject-id", "foo bar test");
saml2AttStatement.Attributes.Add(saml2Attribute);
saml2Attribute = new Saml2Attribute("urn:oasis:names:tc:xspa:1.0:subject:organization", "urn:oid:"+senderOid);
saml2AttStatement.Attributes.Add(saml2Attribute);
saml2Attribute = new Saml2Attribute("urn:oasis:names:tc:xspa:1.0:subject:organization-id", "urn:oid:" + senderOid);
saml2AttStatement.Attributes.Add(saml2Attribute);
saml2Attribute = new Saml2Attribute("urn:nhin:names:saml:homeCommunityId", "urn:oid:" + senderOid);
saml2AttStatement.Attributes.Add(saml2Attribute);
saml2Attribute = new Saml2Attribute("urn:oasis:names:tc:xacml:2.0:subject:role");
saml2AttStatement.Attributes.Add(saml2Attribute);
saml2Assertion2.Statements.Add(saml2AttStatement);
List<SecurityKey> keyList = new List<SecurityKey>();
keyList.Add(rsaKey);
ReadOnlyCollection<SecurityKey> keys = new ReadOnlyCollection<SecurityKey>(keyList);
X509SecurityToken issuerToken = new X509SecurityToken(cert);
Saml2SecurityToken token2 = new Saml2SecurityToken(saml2Assertion2,keys,issuerToken);
XcpdRespondingGatewaySyncService.RespondingGatewaySyncClient myClient = new XcpdRespondingGatewaySyncService.RespondingGatewaySyncClient("IRespondingGatewaySync2");
CustomBinding customBinding = myClient.Endpoint.Binding as CustomBinding;
SecurityBindingElement element = customBinding.Elements.Find<SecurityBindingElement>();
IssuedSecurityTokenParameters tokenParameters = element.EndpointSupportingTokenParameters.Signed[0].Clone() as IssuedSecurityTokenParameters;
tokenParameters.TokenType = System.IdentityModel.Tokens.SecurityTokenTypes.Saml;
tokenParameters.RequireDerivedKeys = false;
tokenParameters.KeyType = SecurityKeyType.SymmetricKey;
element.EndpointSupportingTokenParameters.Signed.Clear();
element.EndpointSupportingTokenParameters.Endorsing.Add(tokenParameters);
myClient.ChannelFactory.Credentials.SupportInteractive = false;
myClient.ChannelFactory.ConfigureChannelFactory();
XcpdRespondingGatewaySyncService.IRespondingGatewaySync myChannel = ChannelFactoryOperations.CreateChannelWithIssuedToken(myClient.ChannelFactory, token2);

Create SAML Authentication request using WIF

It seems most of the WIF information out there is useful for enabling federated authentication across entire applications. I'm interested in using the API to create SAML authentication requests and receive/interpret the SAML responses.
I found the following post on SO Reading SAML Attributes from SAML Token that gets me going in the right direction in regards to receiving and interpreting SAML responses. Can anyone give me more information on how I might use the API to create SAML requests?
Any more info (reading material, videos, etc) on the API in general would be greatly appreciated.
Here's a little example form one of our samples that shows how to programatically create a request for a (SAML) Security Token to an STS:
private static SecurityToken GetSamlToken(string realm, string stsEndpoint, ClientCredentials clientCredentials)
{
using (var factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(new Uri(stsEndpoint))))
{
factory.Credentials.UserName.UserName = clientCredentials.UserName.UserName;
factory.Credentials.UserName.Password = clientCredentials.UserName.Password;
factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
factory.TrustVersion = TrustVersion.WSTrust13;
WSTrustChannel channel = null;
try
{
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress(realm),
KeyType = KeyTypes.Bearer,
};
channel = (WSTrustChannel)factory.CreateChannel();
return channel.Issue(rst);
}
finally
{
if (channel != null)
{
channel.Abort();
}
factory.Abort();
}
}
Since no one else has answered, here's an article from the inimitable Michelle Bustamante:
http://www.devproconnections.com/article/federated-security/Generate-SAML-Tokens-Using-Windows-Identity-Foundation.aspx

Categories

Resources