locked
Other webpage authenticate user credentials RRS feed

  • Question

  • Forgive me if any part of my question is blatantly wrong or off. I have little to no experience with ADFS

    We host Dynamics CRM 2011 and have user accounts stored in our AD DS (so usernames and passwords are with us).

    We have a partner site that holds user credentials (for our system for their users) and would like to do SSO (click a link from their system, use the credentials which they are storing to log into the crm which we host). We have no federation set up between the two companies and it sounds like they want it to be done this way... So any way that they can authenticate the credentials they're storing and have the client's browser receive the token necessary to get into CRM?

     


    Thank you in advance for your help. If you think you may be able to help with any of my unanswered threads please look at them here
    Thursday, November 3, 2011 4:41 PM

Answers

  • Found a solution to this while dealing with a separate issue.

    The solution in this post was the starting point:

    http://social.msdn.microsoft.com/Forums/en-US/sharepoint2010programming/thread/59ec8c6b-f913-4fb1-b6ec-45e3b5be1b45

    NOTE: The windowstransport endpoint needs to be enabled on the adfs server in order for this to work.

    Here's the resulting code:

    public static void LoginAndRedirect(Page page, string org, string username, string password, string domain, string adfsServer, bool newWindow = false)
    {
    	var samlToken = SAMLToken.GetSAMLToken(org, username, password, domain, adfsServer);
     
    	var nv = new NameValueCollection
    	{
    		{ "wa""wsignin1.0" },
    		{ "wctx""rm=1&id="+Guid.NewGuid()+"&ru="+HttpUtility.UrlEncode(org) },
    		{ "wresult"HttpUtility.HtmlEncode(samlToken) }
    	};
    	RedirectAndPOST(page, org, nv, newWindow);
    }
     
     
    /// <summary>
    /// POST data and Redirect to the specified url using the specified page.
    /// </summary>
    /// <param name="page">The page which will be the referrer page.</param>
    /// <param name="destinationUrl">The destination Url to which
    /// the post and redirection is occuring.</param>
    /// <param name="data">The data should be posted.</param>
    /// <param name="newWindow"></param>
    /// <Author>Samer Abu Rabie</Author>
    private static void RedirectAndPOST(Control page, string destinationUrl,
    																		NameValueCollection data, bool newWindow = false)
    {
    	//Prepare the Posting form
    	var strForm = PreparePOSTForm(destinationUrl, data, newWindow);
    	//Add a literal control the specified page holding 
    	//the Post Form, this is to submit the Posting form with the request.
    	page.Controls.Add(new LiteralControl(strForm));
    }
     
    /// <summary>
    /// This method prepares an Html form which holds all data
    /// in hidden field in the addetion to form submitting script.
    /// </summary>
    /// <param name="url">The destination Url to which the post and redirection
    /// will occur, the Url can be in the same App or ouside the App.</param>
    /// <param name="data">A collection of data that
    /// will be posted to the destination Url.</param>
    /// <param name="newWindow"></param>
    /// <returns>Returns a string representation of the Posting form.</returns>
    /// <Author>Samer Abu Rabie</Author>
    private static string PreparePOSTForm(string url, NameValueCollection data, bool newWindow = false)
    {
    	//Set a name for the form
    	const string formID = "PostForm";
    	//Build the form using the specified data to be posted.
    	var strForm = new StringBuilder();
    	strForm.Append("<form id=\"" + formID + "\" name=\"" +
    			            formID + "\" action=\"" + url +
    			            "\"");
    	if (newWindow)
    		strForm.Append(" target=\"_blank\"");
     
    	strForm.Append(" method=\"POST\">");
     
    	foreach (string key in data)
    	{
    		strForm.Append("<input type=\"hidden\" name=\"" + key +
    										"\" value=\"" + data[key] + "\">");
    	}
     
    	strForm.Append("</form>");
    	//Build the JavaScript which will do the Posting operation.
    	var strScript = new StringBuilder();
    	strScript.Append("<script language='javascript'>");
    	strScript.Append("var v" + formID + " = document." + formID + ";");
    	strScript.Append("v" + formID + ".submit();");
    	strScript.Append("</script>");
    	//Return the form and the script concatenated.
    	//(The order is important, Form then JavaScript)
    	return strForm.ToString() + strScript;
    }

    public static string GetSAMLToken(string rpurl, string username, string password, string domain, string adfsServer)
    {
    	var baseUri = new Uri(rpurl);
    	var baseURL = baseUri.GetComponents(UriComponents.SchemeAndServerUriFormat.Unescaped);
    	baseURL = baseURL.EndsWith("/") ? baseURL : baseURL + "/";
     
    	var stsServer = adfsServer.EndsWith("/") ? adfsServer : adfsServer + "/";
    	var stsUrl = stsServer + "adfs/services/trust/13/windowstransport";
     
    	//get token from STS
     
    	return GetResponse(stsUrl, baseURL, username, password, domain);
    }
     
    private static string GetResponse(string stsUrl, string baseUrl, string username, string password, string domain)
    {
    	var rst = new RequestSecurityToken
    	{
    		RequestType = WSTrust13Constants.RequestTypes.Issue,
    		AppliesTo = new EndpointAddress(baseUrl),
    		KeyType = WSTrust13Constants.KeyTypes.Bearer
    	};
     
     
    	//bearer token, no encryption
    	var trustSerializer = new WSTrust13RequestSerializer();
    	var binding = new WSHttpBinding
    	{
    		Security =
    		{
    			Mode = SecurityMode.Transport,
    			Message =
    			{
    				ClientCredentialType = MessageCredentialType.None,
    				EstablishSecurityContext = false
    			}
    		}
    	};
     
    	binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
    	var address = new EndpointAddress(stsUrl);
    	var trustClient = new WSTrust13ContractClient(binding, address);
     
    	trustClient.ClientCredentials.Windows.AllowNtlm = true;
    	trustClient.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
    	trustClient.ClientCredentials.Windows.ClientCredential = new NetworkCredential(username, password, domain);
    	var response = trustClient.EndIssue(trustClient.BeginIssue(Message.CreateMessage(MessageVersion.DefaultWSTrust13Constants.Actions.Issuenew RequestBodyWriter(trustSerializer, rst)), nullnull));
    	trustClient.Close();
     
    	var reader = response.GetReaderAtBodyContents();
    	return reader.ReadOuterXml();
    }
    	class RequestBodyWriter : BodyWriter
    	{
    		readonly WSTrustRequestSerializer _serializer;
    		readonly RequestSecurityToken _rst;
     
    		/// <summary>
    		/// Constructs the Body Writer.
    		/// </summary>
    		/// <param name="serializer">Serializer to use for serializing the rst.</param>
    		/// <param name="rst">The RequestSecurityToken object to be serialized to the outgoing Message.</param>
    		public RequestBodyWriter(WSTrustRequestSerializer serializer, RequestSecurityToken rst)
    			: base(false)
    		{
    			if (serializer == null)
    				throw new ArgumentNullException("serializer");
     
    			_serializer = serializer;
    			_rst = rst;
    		}
     
     
    		/// <summary>
    		/// Override of the base class method. Serializes the rst to the outgoing stream.
    		/// </summary>
    		/// <param name="writer">Writer to which the rst should be written.</param>
    		protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
    		{
    			_serializer.WriteXml(_rst, writer, new WSTrustSerializationContext());
    		}
    	[ServiceContract]
    	public interface IWSTrust13Contract
    	{
    		[OperationContract(ProtectionLevel = ProtectionLevel.EncryptAndSignAction = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue"ReplyAction = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTRC/IssueFinal"AsyncPattern = true)]
    		IAsyncResult BeginIssue(Message request, AsyncCallback callback, object state);
    		Message EndIssue(IAsyncResult asyncResult);
    	}
     
    	public partial class WSTrust13ContractClient : ClientBase<IWSTrust13Contract>, IWSTrust13Contract
    	{
    		public WSTrust13ContractClient(Binding binding, EndpointAddress remoteAddress)
    			: base(binding, remoteAddress)
    		{
    		}
     
    		public IAsyncResult BeginIssue(Message request, AsyncCallback callback, object state)
    		{
    			return base.Channel.BeginIssue(request, callback, state);
    		}
     
    		public Message EndIssue(IAsyncResult asyncResult)
    		{
    			return base.Channel.EndIssue(asyncResult);
    		}
    	}


    Thank you in advance for your help. If you think you may be able to help with any of my unanswered threads please look at them here
    Tuesday, November 29, 2011 6:34 PM