Authentication in ADFS with Web Service - c#

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.

Related

SecurityNegotiationException - Retrieving token from web service

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

Facebook SDK .NET - (#200) The user hasn't authorized the application to perform this action

I am trying to post a message to a Facebook page from a C# web application. I am getting the following exception thrown on calling FacebookClient.Post(...):
FacebookOAuthException (OAuthException - #200) (#200) The user hasn't
authorized the application to perform this action
Code:
var facebookClient = new FacebookClient();
facebookClient.AppId = appId;
facebookClient.AppSecret = appSecret;
if (Request["code"] == null)
{
var authUrl = facebookClient.GetLoginUrl(new
{
client_id = appId,
client_secret = appSecret,
scope = "publish_stream",
redirect_uri = Request.Url.AbsoluteUri
});
Response.Redirect(authUrl.AbsoluteUri);
}
else
{
dynamic result = facebookClient.Get("oauth/access_token", new
{
client_id = appId,
client_secret = appSecret,
grant_type = "client_credentials",
redirect_uri = Request.Url.AbsoluteUri,
code = Request["code"]
});
facebookClient.AccessToken = result.access_token;
// Store access token
}
Sending a message:
protected void PublishMessage(string message)
{
FacebookClient client = new FacebookClient(AccessToken);
client.AppId = ApplicationId;
client.AppSecret = ApplicationSecret;
dynamic parameters = new ExpandoObject();
parameters.message = message;
client.Post(PageName + "/feed", parameters);
}
I accept the following Facebook prompts to ensure that the app has access:
And in my Facebook profile settings, the app looks like this:
I am using the Facebook SDK for .NET v6.4.2 (the latest).
This may seem a silly question, but aren't you missing the Request code from your access token request? Shouldn't it be like this?
dynamic result = facebookClient.Get("oauth/access_token", new
{
client_id = appId,
client_secret = appSecret,
grant_type = "authorization_code"
redirect_uri = Request.Url.AbsoluteUri,
code = Request["code"]
});
Edit Just realized, you have to use a different grant_type when using the request code :)

active client - get token from resource partner adfs using idp token

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.

asp.net mvc4 linkedin integration - Access to posting shares denied

I am developing social networks integration for my asp.net mvc4 application.
Twitter and Facebook were very easy for me but I am seriously stuck with LinkedIn.
Here is my code.
public ActionResult LinkedInTest(string text)
{
var client = new RestClient
{
Authority = "https://api.linkedin.com/uas/oauth",
Credentials = LinkedInSocialHelper.GetCredentials()
};
var request = new RestRequest {Path = "requestToken"};
RestResponse response = client.Request(request);
token = response.Content.Split('&')[0].Split('=')[1];
tokenSecret = response.Content.Split('&')[1].Split('=')[1];
textToPost = text;
Response.Redirect("https://api.linkedin.com/uas/oauth/authorize?oauth_token=" + token + "&scope=r_basicprofile+r_emailaddress+r_network+r_contactinfo+rw_nus");
return null;
textToPost = text;
return RedirectToAction("LinkedInCallback");
}
public ActionResult LinkedInCallback()
{
verifier = Request["oauth_verifier"];
var client = new RestClient
{
Authority = "https://api.linkedin.com/uas/oauth",
Credentials = LinkedInSocialHelper.GetCredentials(token, tokenSecret, verifier),
Method = WebMethod.Post
};
var request = new RestRequest {Path = "accessToken"};
RestResponse response = client.Request(request);
token = response.Content.Split('&')[0].Split('=')[1];
tokenSecret = response.Content.Split('&')[1].Split('=')[1];
LinkedInSocialHelper.Post(textToPost, token, tokenSecret);
return RedirectToAction("Calendar");
}
public static void Post(string text, string accessToken, string accessTokenSecret)
{
var tokenManager = new TokenManager(ApiKey, ApiSecret);
tokenManager.ExpireRequestTokenAndStoreNewAccessToken(null, null, accessToken, accessTokenSecret);
var authorization = new WebOAuthAuthorization(tokenManager, UserToken);
LinkedInService service = new LinkedInService(authorization);
//var user = service.GetCurrentUser(ProfileType.Public); - IT IS GIVING ME THE SAME ERROR - Access denied
service.CreateShare(text, VisibilityCode.ConnectionsOnly);
}
Everything works fine except last thing - posting shares - I get Access to posting shares denied exception despite the fact that I generate token using all the necessary permissions:
"https://api.linkedin.com/uas/oauth/authorize?oauth_token=" + token + "&scope=r_basicprofile+r_emailaddress+r_network+r_contactinfo+rw_nus"
Hope you good guys help me.
See the last post here - it describes how to solve it
https://developer.linkedin.com/forum/permission-scope-request-token-query-not-working?page=1

Getting SAML token from ADFS using windows credentials

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;
}

Categories