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.
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
I'm trying to call Send on the GmailService from a C# .NET MVC app. and I keep getting a 403 error when I call send.
I've checked my scopes, the Gmail setup definitely has the Gmail API enabled, and my ClientID and ClientSecret are fresh.
var httpClient = new HttpClient{
BaseAddress = new Uri("https://www.googleapis.com")
};
var requestUrl = $"oauth2/v4/token?code={code}&client_id={ClientId}&client_secret={SecretKey}&redirect_uri={RedirectUrl}&grant_type=authorization_code";
var dict = new Dictionary<string, string>{
{ "Content-Type", "application/x-www-form-urlencoded" }
};
var req = new HttpRequestMessage(HttpMethod.Post, requestUrl){Content = new FormUrlEncodedContent(dict)};
var response = await httpClient.SendAsync(req);
var token = JsonConvert.DeserializeObject<GmailToken>(await response.Content.ReadAsStringAsync());
Session["user"] = token.AccessToken;
//var obj = await GetuserProfile(token.AccessToken);
var obj = await DoSendEmail(token);
public void DoSendEmail(GmailToken inToken) {
const string fromAcct = "XXXXXXXX#gmail.com";
TokenResponse token = new TokenResponse();
token.AccessToken = inToken.AccessToken;
token.ExpiresInSeconds = inToken.ExpiresIn;
token.IdToken = inToken.IdToken;
token.TokenType = inToken.TokenType;
token.IssuedUtc = DateTime.UtcNow;
IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer {
ClientSecrets = secrets,
Scopes = SCOPES,
ProjectId = "Xcent CP"
});
UserCredential credential = new UserCredential(flow, fromAcct, token);
if (credential.Token.IsExpired(credential.Flow.Clock)) {
bool success = credential.RefreshTokenAsync(CancellationToken.None).Result;
if (!success) {
throw new Exception("Could not refresh token");
}
}
GmailService gs = null;
try {
gs = new GmailService(new Google.Apis.Services.BaseClientService.Initializer() {
ApplicationName = APP_NAME,
HttpClientInitializer = credential
});
var mailMessage = new System.Net.Mail.MailMessage();
mailMessage.From = new System.Net.Mail.MailAddress(fromAcct);
mailMessage.To.Add("XXXXXXXX#comcast.net");
mailMessage.ReplyToList.Add(fromAcct);
mailMessage.Subject = "Test email";
mailMessage.Body = "<html><body>Hi <b>Lee</b>, this is <b>yet another</b> test message.</body></html>";
mailMessage.IsBodyHtml = true;
var mimeMessage = MimeKit.MimeMessage.CreateFromMailMessage(mailMessage);
var gmailMessage = new Google.Apis.Gmail.v1.Data.Message {
Raw = Encode(mimeMessage.ToString())
};
gs.Users.Messages.Send(gmailMessage, fromAcct).Execute();
}
catch (Exception ex) {
throw ex;
}
finally {
if (gs != null) {
gs.Dispose();
}
gs = null;
}
}
I'm not sure where to look...I've been through many many many online articles and tutorials, tried seemingly everything, and I'm still stuck with the 403 error. Help!
Thanks,
Lee
So after many hours spent looking at this I figured out the problem. My link to the Google login was this:
Response.Redirect($"https://accounts.google.com/o/oauth2/v2/auth?client_id={ClientId}&response_type=code&scope=openid%20email%20profile&redirect_uri={RedirectUrl}&state=abcdef");
"openid%20email%20profile" was the only scope I was specifying for the login, hence the 403 error about the scope I was using for the flow variable.
phew!
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.
Here is my code please let me know why am not able to get emailid and profile pic.
public class LinkedInController : Controller
{
public ActionResult index()
{
return AuthenticateToLinkedIn();
}
static string token_secret = "";
public ActionResult AuthenticateToLinkedIn()
{
var credentials = new OAuthCredentials
{
CallbackUrl = "http://localhost:7326/Linkedin/callback",
ConsumerKey = ConfigurationManager.AppSettings["ConsumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"],
Verifier = "123456",
Type = OAuthType.RequestToken
};
var client = new RestClient { Authority = "https://api.linkedin.com/uas/oauth", Credentials = credentials };
var request = new RestRequest { Path = "requestToken" };
request.AddParameter("scope", "r_emailaddress");
RestResponse response = client.Request(request);
string content = response.Conten
var contents = HttpUtility.ParseQueryString(response.Content)
var token = response.Content.Split('&')[0].Split('=')[1];
token_secret=contents["oauth_token_secret"];
Response.Redirect("https://api.linkedin.com/uas/oauth/authorize?oauth_token=" + token);
return null;
}
string token = "";
string verifier = "";
public ActionResult Callback()
{
token = Request["oauth_token"];
verifier = Request["oauth_verifier"];
var credentials = new OAuthCredentials
{
ConsumerKey = ConfigurationManager.AppSettings["ConsumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"],
Token = token,
TokenSecret = token_secret,
Verifier = verifier,
Type = OAuthType.AccessToken,
ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
SignatureMethod = OAuthSignatureMethod.HmacSha1,
Version = "1.0"
};
var client = new RestClient { Authority = "https://api.linkedin.com/uas/oauth", Credentials = credentials, Method = WebMethod.Post };
var request = new RestRequest { Path = "accessToken" };
request.AddParameter("scope", "r_emailaddress");
RestResponse response = client.Request(request);
string content = response.Content;
var contents = HttpUtility.ParseQueryString(response.Content);
var accessToken =contents["oauth_token"];
var accessTokenSecret=contents["oauth_token_secret"];
var people = new LinkedInService(accessToken, accessTokenSecret).GetCurrentUser();
String companyName = people.FirstName;
return Content(companyName);
}
}
I am using Hammock and i am getting first name and last name and title and all i have enabled the r_emailadress in LinkedIn Portal but please am not able to get these information.
The only scope you're using is r_emailaddress. To get the pic, I think you need to use r_basicprofile. I can't see a field in the docs called 'emailid', only email-address.
https://developer.linkedin.com/docs/fields/basic-profile
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;
}