I am creating a windows 8 client app in c#.This app will use odata service of SAP. For authentication I need SAML token issued by ADFS. Is there any method to get SAML token from ADFS using windows credentials?
You can get the SAML token using the below code.
var factory = new WSTrustChannelFactory(new Microsoft.IdentityModel.Protocols.WSTrust.Bindings.UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), adfsEndpoint);
factory.Credentials.UserName.UserName = "username";
factory.Credentials.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("https://yourserviceendpoint.com/"),
KeyType = KeyTypes.Bearer,
};
channel = (WSTrustChannel)factory.CreateChannel();
return channel.Issue(rst);
}
catch (Exception e)
{
return null;
}
Related
I'm attempting to move from WIF 3.5 and onto WIF 4.5. However, the conversion is proving to be more difficult than I anticipated. The questions will correspond to the comments in the code.
Full error message:
System.Web.Services.Protocols.SoapException: 'System.Web.Services.Protocols.SoapException: Authentication Failed --->
System.ServiceModel.Security.SecurityNegotiationException: Secure
channel cannot be opened because security negotiation with the remote
endpoint has failed. This may be due to absent or incorrectly
specified EndpointIdentity in the EndpointAddress used to create the
channel. Please verify the EndpointIdentity specified or implied by
the EndpointAddress correctly identifies the remote endpoint.
Secure channel cannot be opened because security negotiation with the
remote endpoint has failed. This may be due to absent or incorrectly
specified EndpointIdentity in the EndpointAddress used to create the
channel. Please verify the EndpointIdentity specified or implied by
the EndpointAddress correctly identifies the remote endpoint.
#1. Which username/password combination is needed and which is not?
#2. This is where SecurityNegotiationException is thrown. What am I missing exactly?
So, am I way off or is it something simple I'm missing? Do I need to entirely rewrite how the WSTrustChannelFactory is being created?
Code:
public string GetToken(string url, string domain, string realm, string username, string password)
{
string rp = realm;
string token = "";
WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory
(
new WSHttpBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(new Uri(url))
);
trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
trustChannelFactory.Credentials.Windows.ClientCredential.Domain = domain;
trustChannelFactory.Credentials.Windows.ClientCredential.UserName = username; // #1; not sure which pair is needed?
trustChannelFactory.Credentials.Windows.ClientCredential.Password = password;
trustChannelFactory.Credentials.UserName.Password = password;
trustChannelFactory.Credentials.UserName.UserName = username;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
try
{
RequestSecurityToken rst = new RequestSecurityToken(RequestTypes.Issue, KeyTypes.Bearer);
rst.AppliesTo = new EndpointReference(rp);
rst.TokenType = SecurityTokenTypes.Saml;
WSTrustChannel channel = (WSTrustChannel)trustChannelFactory.CreateChannel();
GenericXmlSecurityToken token = channel.Issue(rst) as GenericXmlSecurityToken; // #2; Exception thrown here
token = token.TokenXml.OuterXml;
}
catch (SecurityNegotiationException e)
{
LogError("Authentication Failed", e);
}
catch (TimeoutException e)
{
LogError("Unable to authenticate", e);
}
catch (CommunicationException e)
{
LogError("Communication exception", e);
}
catch (Exception e)
{
LogError("Unknown exception", e);
}
return token;
}
You need to use a SecurityTokenHandlerCollection
public SecurityToken GetToken(string url, string realm, string username, string password)
{
string rp = realm;
WS2007HttpBinding binding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential, false);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Message.EstablishSecurityContext = false;
EndpointAddress endpoint = new EndpointAddress(url);
WSTrustChannelFactory factory = new WSTrustChannelFactory(binding, endpoint);
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.UserName.UserName = username;
factory.Credentials.UserName.Password = password;
WSTrustChannel channel = (WSTrustChannel) factory.CreateChannel();
RequestSecurityToken rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Bearer,
AppliesTo = new EndpointReference(rp),
TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"
};
GenericXmlSecurityToken genericXmlSecurityToken = (GenericXmlSecurityToken) channel.Issue(rst, out RequestSecurityTokenResponse rstr);
SecurityTokenHandlerCollection tokenHandlers = new SecurityTokenHandlerCollection(
new SecurityTokenHandler[]
{
new SamlSecurityTokenHandler(),
new Saml2SecurityTokenHandler()
}
);
tokenHandlers.Configuration.AudienceRestriction = new AudienceRestriction();
tokenHandlers.Configuration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(rp));
TrustedIssuerNameRegistry trustedIssuerNameRegistry = new TrustedIssuerNameRegistry();
tokenHandlers.Configuration.IssuerNameRegistry = trustedIssuerNameRegistry;
SecurityToken token =
tokenHandlers.ReadToken(
new XmlTextReader(new StringReader(genericXmlSecurityToken.TokenXml.OuterXml)));
return token;
}
public class TrustedIssuerNameRegistry : IssuerNameRegistry
{
public override string GetIssuerName(SecurityToken securityToken)
{
return "Trusted Issuer";
}
}
We decided to continue to use WIF 3.5 for now and will have an entire rewrite for WIF 4.5 instead of trying to do something that isn't possible.
There was simply too much change and not enough documentation to "shoehorn" our existing code from WIF 3.4 to WIF 4.5
Below is the code I'm using to request "Symmetric" token type from Adfs and I receive the error as mentioned in the title.
A quick google search points out that, it is because of the absence of token signing certificate but I do have it defined in the Adfs.
Do I need to explicitly assign it to "Relying party" or having it in ADFS 2.0 works?
Please can anybody point me to right direction or have somebody knows step to associate token signing certificate to relying party if it is required?
WS2007HttpBinding binding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Message.EstablishSecurityContext = false;
binding.BypassProxyOnLocal = true;
binding.UseDefaultWebProxy = true;
System.ServiceModel.Security.WSTrustChannelFactory factory = new System.ServiceModel.Security.WSTrustChannelFactory(
binding, new EndpointAddress(userNameMixedRequest.GetUserNameMixedUrl));
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.SupportInteractive = false;
factory.Credentials.UserName.UserName = "domain\username"
factory.Credentials.UserName.Password = "password"
System.IdentityModel.Protocols.WSTrust.RequestSecurityToken request = new System.IdentityModel.Protocols.WSTrust.RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointReference(userNameMixedRequest.ClientUri),
KeyType = KeyTypes.Symmetric
};
System.ServiceModel.Security.IWSTrustChannelContract channel = factory.CreateChannel();
channel.Issue(request)
I have developed the test application in which I have added .asmx file for ADFS authentication, after authentication, it would redirect to my original already developed web application. the common code is shown below
var stsEndpoint = ConfigurationManager.AppSettings["EndPoint"];
var relayPartyUri = ConfigurationManager.AppSettings["RelayPartyUri"];
EndpointAddress epoint = new EndpointAddress(stsEndpoint);
using (var factory = new WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), epoint) { TrustVersion = TrustVersion.WSTrust13 })
{
if (factory.Credentials != null)
{
factory.Credentials.UserName.UserName = #"Domain\" + userName;
factory.Credentials.UserName.Password = password;
}
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointReference(relayPartyUri),
KeyType = WSTrust13Constants.KeyTypes.Bearer
};
var channel = factory.CreateChannel();
var token = channel.Issue(rst);
var genericToken = token as GenericXmlSecurityToken;
...
}
...
The line var token = channel.Issue(rst); throws error
Error is ID3082: The request scope is not valid or is unsupported
Also, I am not sure which URI I should use for RelyingPartyURI
the service URL which I am using for ADFS authentication. or
the URL of my already developed web application
Please help.
relayPartyUri is the unique identifier for your app.
I'm trying to authenticate a request to Dynamics using the app's client id and the application key. This is failing in a web app project in visual studio (debugging locally). Below is the code I'm trying to use. It looks like I'm getting an auth token back to pass in but it throws this error:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Bearer authorization_uri=https://login.windows.net/....
string organizationUrl = "https://myurl.dynamics.com";
string clientId = "myclientid";
string appKey = "myclientsecret";
string aadInstance = "https://login.microsoftonline.com/";
string tenantID = "mytenantid";
ClientCredential clientcred = new ClientCredential(clientId, appKey);
AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID);
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(organizationUrl, clientcred);
var requestedToken = authenticationResult.AccessToken;
using (var sdkService = new OrganizationWebProxyClient(GetServiceUrl(organizationUrl), false))
{
sdkService.HeaderToken = requestedToken;
OrganizationRequest request = new OrganizationRequest()
{
RequestName = "WhoAmI"
};
WhoAmIResponse response = sdkService.Execute(new WhoAmIRequest()) as WhoAmIResponse;
Console.WriteLine(response.UserId);
}
I am trying to write console application with the following scenario:
client first requests a token from an identity provider, and then uses this token to request a new token from a Resource STS
Using the following link: http://leastprivilege.com/2010/10/28/wif-adfs-2-and-wcfpart-6-chaining-multiple-token-services/
I managed get the token from Idp but didn't managed getting the token from Resource STS.
This is my code:
string RPRealm = "https://service.contoso.com/";
string RSTSRealm = "http://fsweb.contoso.com/adfs/services/trust";
string IdPstsEndpoint = "https://IdpAdfs.domain.com/adfs/services/trust/13/kerberosmixed";
string RSTSEndpoint = "https://fsweb.contoso.com/adfs/services/trust/13/IssuedTokenMixedSymmetricBasic256";
private static SecurityToken GetIdPToken(string rstsRealm, string IdPstsEndpoint)
{
using (var factory = new WSTrustChannelFactory(
new KerberosWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(new Uri(IdPstsEndpoint))))
{
WSTrustChannel channel = null;
factory.TrustVersion = TrustVersion.WSTrust13;
try
{
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress(rstsRealm),
KeyType = WSTrust13Constants.KeyTypes.Bearer,
};
channel = (WSTrustChannel)factory.CreateChannel();
RequestSecurityTokenResponse rstr;
SecurityToken token = channel.Issue(rst, out rstr);
return token;
}
finally
{
if (channel != null)
{
channel.Abort();
}
factory.Abort();
}
}
}
private static SecurityToken GetRSTSToken(SecurityToken IdPToken, string RSTSEndpoint, string RPRealm)
{
var binding = new WS2007FederationHttpBinding();
binding.Security.Message.IssuedKeyType = SecurityKeyType.BearerKey;
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Mode = WSFederationHttpSecurityMode.TransportWithMessageCredential;
using (var factory = new WSTrustChannelFactory(
binding,
new EndpointAddress(new Uri(RSTSEndpoint))))
{
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress(RPRealm),
KeyType = WSTrust13Constants.KeyTypes.Bearer,
};
factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.SupportInteractive = false;
factory.ConfigureChannelFactory();
var channel = factory.CreateChannelWithIssuedToken(IdPToken);
RequestSecurityTokenResponse rstr;
SecurityToken token = channel.Issue(rst, out rstr);
return token;
}
}
I get this error:
The content type text/html of the response message does not match the content type of the binding (application/soap+xml; charset=utf-8)
what is worng with my code?
Thanks in advance
ADFS does not support bearer tokens on its federation endoints. In other words, on your first hop you need to specify a KeyTypes.Symmetric on the RST.