locked
Silverlight IFD 401 unauthorized error RRS feed

  • Question

  • Hi,

    I am getting  401 unauthorized error in Fiddler tool when trying to connect service from silverlight through IFD. It is working fine onpremise and for IFD it is working for general aspx pages. But when I am trying to call service from silverlight  to service it is showing that error. I think it is the problem with crmtoken. Any Idea???? Remember  i want IFD for silverlight to service not from general aspx pages.

    Thanks in advance 


    Thanks & Regards NarSrav
    Friday, November 19, 2010 2:38 PM

All replies

  • Hi,

    I am getting  401 unauthorized error in Fiddler tool when trying to connect service from silverlight through IFD. It is working fine onpremise and for IFD it is working for general aspx pages. But when I am trying to call service from silverlight  to service it is showing that error. I think it is the problem with crmtoken. Any Idea???? Remember  i want IFD for silverlight to service not from general aspx pages.

    Thanks in advance  


    Thanks & Regards NarSrav
    • Merged by DavidJennawayMVP, Moderator Tuesday, November 23, 2010 8:58 PM Duplicate thread. Merged so all replies are in the same place
    Friday, November 19, 2010 2:37 PM
  • Hi,

    I am getting  401 unauthorized error in Fiddler tool when trying to connect service from silverlight through IFD. It is working fine onpremise and for IFD it is working for general aspx pages. But when I am trying to call service from silverlight  to service it is showing that error. I think it is the problem with crmtoken. Any Idea???? Remember  i want IFD for silverlight to service not from general aspx pages.

    Thanks in advance 


    Thanks & Regards NarSrav
    Friday, November 19, 2010 2:39 PM
  • You're right about the problem being likely from the CrmAuthenticationToken.  For ASPX pages, the current token can be used by calling the "ExtractCrmAuthenticationToken()" method on the page context, and with Javascript, the method "GenerateAuthenticationHeader()" can be used to assemble the token information into the SOAP message issued to the Web Service.  Since Silverlight is client-side, I'm going to presume that you have to establish your own <CrmAuthenticationToken> nodes without the benefit of a utility like the ones provided for either JS or ASPX.

    This page sheds some light on how to create your own authentication header: http://msdn.microsoft.com/en-us/library/cc150838.aspx   However, it lacks the appropriate information for assembling the CrmTicket component necessary for IFD interfacing.  If you could access the CrmTicket from the page, as you could with "GenerateAuthenticationHeader()", you could complete the proper <CrmAuthenticationHeader> information.  Unfortunately, I'm altogether uncertain how to accomplish that.  It must be possible.

    So, I started hunting through the core JS files in my CRM deployment for the function, to see where it obtains its information.  If it could be found, then you might be able to translate the code into Silverlight.  I found it in the util.js file of <www root>\_static\_controls\util .  Here's the function in its entirety:

    function GenerateAuthenticationHeader()
    {
    var xml = new StringBuilder();
    
    xml.Append("<soap:Header><CrmAuthenticationToken xmlns=\"");
    xml.Append(CrmEncodeDecode.CrmXmlEncode(CRM2007_WEBSERVICE_NS));
    xml.Append("\"><AuthenticationType xmlns=\"");
    xml.Append(CrmEncodeDecode.CrmXmlEncode(CRM2007_CORETYPES_NS));
    xml.Append("\">");
    xml.Append(CrmEncodeDecode.CrmXmlEncode(AUTHENTICATION_TYPE));
    xml.Append("</AuthenticationType><CrmTicket xmlns=\"");
    xml.Append(CrmEncodeDecode.CrmXmlEncode(CRM2007_CORETYPES_NS));
    xml.Append("\"></CrmTicket><OrganizationName xmlns=\"");
    xml.Append(CrmEncodeDecode.CrmXmlEncode(CRM2007_CORETYPES_NS));
    xml.Append("\">");
    xml.Append(CrmEncodeDecode.CrmXmlEncode(ORG_UNIQUE_NAME));
    xml.Append("</OrganizationName><CallerId xmlns=\"");
    xml.Append(CrmEncodeDecode.CrmXmlEncode(CRM2007_CORETYPES_NS));
    xml.Append("\">00000000-0000-0000-0000-000000000000</CallerId></CrmAuthenticationToken></soap:Header>");
    
    return xml.ToString();
    }
    

    This looks suspiciously empty of information for a proper authentication header.  For instance, there is no reference to the current CrmTicket value--how can that be?  Obviously the function works for IFD.  Additionally, a blank Guid in the CallerId node will not work for IFD either!  I can't seem to find any explanation for how to extract the current CrmTicket from the client interface.  Or how the current user's Id is provided to the CallerId node for IFD.  It may be impossible to access CrmTicket from the client, given that it operates for sessions and is probably held at the server only.  It stands to reason then, that there are probably two ways to move forward:

    1. Prompt the user for their login information to generate the CrmTicket from IFD and use that to create a web-service connection; or
    2. Create a "code-behind" ASPX file that supports the Silverlight application by performing the authentication and web-service interaction .

    Dave Berry - MVP Dynamics CRM - http:\\crmentropy.blogspot.com
    Friday, November 19, 2010 6:31 PM
    Moderator
  • Try adding "[ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]" to the WCF service
    Jai Ho CRM http://mscrmkb.blogspot.com Skype - amol.gholap

    Mark as answer if a post has answered the question
    Sunday, November 21, 2010 1:32 PM
  • adding "[ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]" to the service
    Jai Ho CRM http://mscrmkb.blogspot.com Skype - amol.gholap

    Mark as answer if a post has answered the question
    Sunday, November 21, 2010 1:33 PM
  • Thanks for replay

     

    I want to IFD  from web-service instead of aspx pages. Means we were able to catch service but in fildertool it is showing  unauthorized error when trying to call service methods (like Execute, create etc.). 


    Thanks & Regards NarSrav
    Monday, November 22, 2010 5:11 PM
  • Have you modified the "AuthenticationType" value of the CrmAuthenticationToken to "2" instead of "0" when operating in IFD-mode?  That's the first, most important step.
    Dave Berry - MVP Dynamics CRM - http:\\crmentropy.blogspot.com
    Monday, November 22, 2010 10:24 PM
    Moderator
  • yes 

    For IFD

    These are the line for .svc

    token = CrmAuthenticationToken.ExtractCrmAuthenticationToken(this.Context, this._orgName);            

                    token.AuthenticationType = 2; (i tried with 0 and 2 and by removing this line but no use)

    If i put impersonator it is giving 

    OpenThreadToken failed with hr=1008 error

    so i removed impersonator still it is not working when calling from .svc and getting 401 error 

    But

    These are the line for .aspx which contains silverlight application 

    this._impersonator = new CrmImpersonator();  

    token = CrmAuthenticationToken.ExtractCrmAuthenticationToken(this.Context, this._orgName);

    and it is working fine 

    At the same time if i remove impersonator for .aspx it is giving 401 error.

    That means .aspx asking for impersonator where as .svc returning 

    OpenThreadToken failed with hr=1008 error

    I have been sticked with this error from long time... 


    Thanks & Regards NarSrav
    Tuesday, November 23, 2010 10:27 AM
  • Hi Narsrav,

    I had faced the similar issues while working with silverlight.

    You can not use above code to authenticate CRM service.

    token = CrmAuthenticationToken.ExtractCrmAuthenticationToken(this.Context, this._orgName);   

    This line extract the token from user context of asp.net application. However asp.net app which host silverlight uses different asp.net version than CRM 4.0. This means both application runs under different process. Due to this you won't get token from CRM user context.

    When you open a Silverlight Page try passing UserId and get the UserId using querystring and pass to crm service for authentication.

    Here is how you will authenticate using UserId:

    private CrmService CreateCrmServiceConnection()
        {
          if (theCrmService == null)
          {
            theCrmService = new CrmService();
          }
          var aToken = new CrmAuthenticationToken
          {
            AuthenticationType = AuthenticationType.AD,
            OrganizationName = ConfigurationSettings.AppSettings["OrgName"].ToString(),
             // Use the global user ID of the system user that is to be impersonated. Set GUID of user passed from QueryString
            CallerId = CurrentUserId
          };
            theCrmService.Url = ConfigurationSettings.AppSettings["CrmWebServiceUrl"].ToString();
            theCrmService.CrmAuthenticationTokenValue = aToken;
            theCrmService.UseDefaultCredentials = true;
          return theCrmService;
          
        }
    
    check this link http://msdn.microsoft.com/en-us/library/cc151052.aspx

    Javascript code to get UserId in ISV config:

    <Button JavaScript="&#xD;&#xA;/*Define the soapBody for the WhoAmI request.*/&#xD;&#xA;var soapBody = &quot;&lt;soap:Body&gt;&quot;+&#xD;&#xA; &quot;&lt;Execute xmlns='http://schemas.microsoft.com/crm/2007/&quot;+&#xD;&#xA; &quot;WebServices'&gt;&quot;+&#xD;&#xA; &quot;&lt;Request xsi:type='WhoAmIRequest' /&gt; &quot;+&#xD;&#xA; &quot;&lt;/Execute&gt;&lt;/soap:Body&gt;&quot;;&#xD;&#xA; &#xD;&#xA;/*Wrap the Soap Body in a soap:Envelope.*/&#xD;&#xA; var soapXml = &quot;&lt;soap:Envelope &quot; +&#xD;&#xA; &quot;xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' &quot;+&#xD;&#xA; &quot;xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' &quot;+&#xD;&#xA; &quot;xmlns:xsd='http://www.w3.org/2001/XMLSchema'&gt;&quot;;&#xD;&#xA; soapXml += GenerateAuthenticationHeader();&#xD;&#xA; soapXml += soapBody;&#xD;&#xA; soapXml += &quot;&lt;/soap:Envelope&gt;&quot;;&#xD;&#xA; &#xD;&#xA;&#xD;&#xA;/* Create the XMLHTTP object for the execute method.*/&#xD;&#xA;var xmlhttp = new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);&#xD;&#xA;xmlhttp.open(&quot;POST&quot;, &quot;/MSCRMservices/2007/crmservice.&quot;+&#xD;&#xA; &quot;asmx&quot;, false);&#xD;&#xA; xmlhttp.setRequestHeader(&quot;Content-Type&quot;, &quot;text/xml; &quot;+&#xD;&#xA; &quot;charset=utf-8&quot;);&#xD;&#xA; xmlhttp.setRequestHeader(&quot;SOAPAction&quot;, &quot;http://schemas.&quot;+&#xD;&#xA; &quot;microsoft.com/crm/2007/WebServices/Execute&quot;);&#xD;&#xA; &#xD;&#xA;/* Send the XMLHTTP object. */&#xD;&#xA; xmlhttp.send(soapXml);&#xD;&#xA; &#xD;&#xA;/* Create an XML object to parse the results.*/&#xD;&#xA; xmlDoc= new ActiveXObject(&quot;Microsoft.XMLDOM&quot;);&#xD;&#xA; xmlDoc.async=false;&#xD;&#xA; xmlDoc.loadXML(xmlhttp.responseXML.xml);&#xD;&#xA; &#xD;&#xA;/* Get the user's ID. */&#xD;&#xA;var userid;&#xD;&#xA; try&#xD;&#xA; {&#xD;&#xA; var rawUserid = xmlDoc.getElementsByTagName(&quot;UserId&quot;)[0].childNodes[0].nodeValue;&#xD;&#xA; userid = &quot;{&quot;+rawUserid.toUpperCase()+&quot;}&quot;;&#xD;&#xA; }&#xD;&#xA; catch (e)&#xD;&#xA; {&#xD;&#xA; err = xmlDoc.getElementsByTagName(&quot;description&quot;)[0].childNodes[0].nodeValue; &#xD;&#xA; alert(&quot;Error :&quot;+e.description+&quot; : &quot;+err);&#xD;&#xA; }            window.showModalDialog('/OpportunityWizard/JLLOpportunityManagementWeb.aspx?userId='+userid,'', 'dialogWidth:1040px;dialogHeight:800px;resizable:yes;status:yes;scroll:no');crmGrid.Refresh();" ValidForCreate="0" ValidForUpdate="0">
             <Titles>
              <Title LCID="1033" Text="New Wizard" />
             </Titles>
            </Button>
    

     

     


    Jai Ho CRM http://mscrmkb.blogspot.com Skype - amol.gholap

    Mark as answer if a post has answered the question
    Tuesday, November 23, 2010 11:32 AM
  • Check this:

    http://social.microsoft.com/Forums/en-US/crmdevelopment/thread/ea916a0f-5de6-402f-987f-7a671000857b

     


    Jai Ho CRM http://mscrmkb.blogspot.com Skype - amol.gholap

    Mark as answer if a post has answered the question
    Tuesday, November 23, 2010 11:35 AM
  • I can't believe with all my hours of searching and reading about
    CRM and a 401 error, the subject has never once come up. Now that I know
    what to look for, I've made some progress. I have another question, but I
    will post that in a separate thread since it's a different topic.
    • Marked as answer by Nar_mscrm Wednesday, December 1, 2010 10:10 AM
    • Unmarked as answer by Nar_mscrm Wednesday, December 1, 2010 10:10 AM
    Tuesday, November 23, 2010 1:27 PM
  •  

    I Tried this 

    token.CallerId = new Guid(HttpContext.Current.Request.QueryString["uid"].ToString());

     

    But still getting same 401 error.


    Thanks & Regards NarSrav
    Tuesday, November 23, 2010 3:39 PM
  • This should work, I am using it for couple of deployments... Are you getting GUID there..?

    private
     CrmService CreateCrmServiceConnection()
    {
    if (theCrmService == null )
    {
    theCrmService = new CrmService();
    }
    var aToken = new CrmAuthenticationToken
    {
    AuthenticationType = AuthenticationType.AD,
    OrganizationName = ConfigurationSettings.AppSettings["OrgName" ].ToString(),
    // Use the global user ID of the system user that is to be impersonated. Set GUID of user passed from QueryString
    CallerId = CurrentUserId
    };
    theCrmService.Url = ConfigurationSettings.AppSettings["CrmWebServiceUrl" ].ToString();
    theCrmService.CrmAuthenticationTokenValue = aToken;
    theCrmService.UseDefaultCredentials = true ;
    return theCrmService;

    }


    Jai Ho CRM http://mscrmkb.blogspot.com Skype - amol.gholap

    Mark as answer if a post has answered the question
    Tuesday, November 23, 2010 4:31 PM
  • Please don't post the same question at multiple forums.

    http://social.microsoft.com/Forums/en-US/crmdevelopment/thread/ea916a0f-5de6-402f-987f-7a671000857b


    Jai Ho CRM http://mscrmkb.blogspot.com Skype - amol.gholap

    Mark as answer if a post has answered the question
    Tuesday, November 23, 2010 4:44 PM
  • Amol,

    His code works for "On-Premise" authentication, which uses the AD-type (read: Windows Integrated) that your code has specified.  The problem he's having is strictly with IFD use of the code.  The cause, I believe, is the lack of the available CrmTicket.  I believe the problem is that the request made to the server by Silverlight does not expose the session cookie set by IFD mode, which is generally available to IIS through HTTP requests sent by the browser (and by extension, JS SOAP calls made therein)--therefore CRM believes the request is being made by an unauthenticated caller, which causes the "401" errors.

    -Dave


    Dave Berry - MVP Dynamics CRM - http:\\crmentropy.blogspot.com
    Tuesday, November 23, 2010 5:58 PM
    Moderator
  • Yes, you are correct. However I was facing similar issues earlier with On-Premise vs IFD installation. The only thing that worked for me is above code. I don't remember why I modified the Authentication type to "AD".

    But this is the working code in my IFD environment.

     


    Jai Ho CRM http://mscrmkb.blogspot.com Skype - amol.gholap

    Mark as answer if a post has answered the question
    Tuesday, November 23, 2010 6:18 PM
  • NarSrav, am I correct in thinking you've created your own web service (.svc) that you're calling from the Silverlight application ? If so, I expect that Amol's explanation above is correct - i.e. your web service is running in a separate web site and/or application pool and/or AppDomain from the Crm web site and application, and hence you cannot access the CrmTicket.

    Amol's solution also looks correct, but this uses Crm impersonation and will only work if the identity that your web service runs under is a member of the PrivUserGroup AD group. The AuthenticationType of AD is correct here as the connection to the Crm web services is made from code running on the Crm web server, rather than the client, and the Crm web server is necessarily considered part of the internal network, so will use AD authentication. One drawback of this solution is that I don't consider it to be very secure, as any other code could pass a userid to this web service. It would be a bit more secure if you pass the userid in the body of the web request rather than on the querystring, as this would be encrypted over SSL.

    Alternatively, I would expect (though I'm by no means a Silverlight expert) that you could build a more secure solution by directly calling the Crm web services using the WebClient or HttpWebRequest class, rather than creating your own web services. In this scenario all code would remain in the security boundaries of the Crm web site, and IFD authentication should work fine. I think you would have to build the SoapHeader in code - i.e. do the equivalent of the GenerateAuthenticationHeader function


    Microsoft CRM MVP - http://mscrmuk.blogspot.com  http://www.excitation.co.uk
    • Proposed as answer by DavidBerryMVP, Moderator Wednesday, November 24, 2010 2:51 AM
    • Marked as answer by Nar_mscrm Wednesday, December 1, 2010 2:00 PM
    • Unmarked as answer by Nar_mscrm Wednesday, December 1, 2010 2:00 PM
    Tuesday, November 23, 2010 8:55 PM
    Moderator
  • Amol Gholap [Neudesic]Hi AMol

     

    Sorry for late replay

     

    could you please tell me which binding you are using for service?????Users MedalsUsers Medals

     

    Thanks in advance

     

     


    Thanks & Regards NarSrav
    Wednesday, December 1, 2010 12:39 PM
  • Here's another solution.
     
    This page helped me resolve a problem that I was having that plagued more over multiple Dynamics CRM projects. "OpenThreadToken failed with hr = 1008"Also reference: (Microsoft.Crm.Sdk.DLL)  
    using (new CrmImpersonator())
    {
     ...
    }
    
    

    ========
    I had a WCF service that needed to communicate with Dynamics CRM in a standard way. It also needed to use the standard security modem for IFD.

    I didn't want to have the user prompted to enter credentials and I wanted to be able to secure the WCF service using the Windows security (Kerberos or NTLM).
    So, what is the solution, I looked everywhere for a fix or a work around. I could not find one after a couple of days.

    ========
    Create a seperate assembly/DLL, like the one below.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace ClassLibrary1
    {
     public static class NativeMethodCalls
     {
     [DllImport("advapi32.dll", SetLastError = true)]
     internal static extern int ImpersonateLoggedOnUser(IntPtr hToken);
    
     [return: MarshalAs(UnmanagedType.Bool)]
     [DllImport("advapi32.dll", SetLastError = true)]
     internal static extern bool RevertToSelf();
     }
     public class CrmImpersonator : IDisposable
     {
     public CrmImpersonator(IntPtr token)
     {
     int iRc = NativeMethodCalls.ImpersonateLoggedOnUser(token);
     }
     ~CrmImpersonator()
     {
     Dispose();
     }
     public void Dispose()
     {
     bool bRc = NativeMethodCalls.RevertToSelf();
     }
     }
    }
    
    
      
    ========
    Now, we have to be able to instantiate this correctly and pass the security token in from the WCF Service.
    Below, I will create the call so that you know how this is called.using System;
    using System;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Activation;
    using System.ServiceModel.Web;
    
    using Microsoft.Crm.Sdk;
    using Microsoft.Crm.Sdk.Query;
    using Microsoft.Crm.SdkTypeProxy;
    using Microsoft.Win32;
    using System.Web;
    using System.Diagnostics;
    using System.Xml.Linq;
    using System.Text;
    
    using ClassLibrary1; 
    
    namespace WcfService1
    {
     [ServiceContract(Namespace = "")]
     [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
     public class CRMAccess2
     {
     bool _isDevEnvironment;
     string _orgName;
     string _crmServiceUrl;
     HttpContext Context;
     HttpRequest Request;
    
     string result = string.Empty;
    
     [OperationContract]
     public string WhoAmIRequest(string organization)
     {
     // Set the Context and Request objects...
     Context = HttpContext.Current;
    
     if (HttpContext.Current == null)
     Request = null;
     else
     Request = HttpContext.Current.Request;
    
     result = string.Empty;
    
     try
     {
     this._orgName = organization;
     this._crmServiceUrl = this.BuildCrmServiceUrl();
    
     using (new CrmImpersonator3(OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Token))
     {
     CrmAuthenticationToken token = this.CreateAuthenticationToken();
     CrmService crmService = this.CreateCrmService(token);
    
     WhoAmIRequest req = new WhoAmIRequest();
     WhoAmIResponse res = (WhoAmIResponse)crmService.Execute(req);
     return res.UserId.ToString();
     }
     }
     catch (Exception ex)
     {
     return "bad - " + ex.Message;
     }
     }
    
     private CrmAuthenticationToken CreateAuthenticationToken()
     {
     CrmAuthenticationToken token;
     CrmAuthenticationToken.ExtractCrmAuthenticationToken(this.Context, this._orgName);
    
     token = new CrmAuthenticationToken();
     token.AuthenticationType = AuthenticationType.AD;
     token.OrganizationName = this._orgName;
    
     return token;
     }
     }
    }
    
    

    Lastly, don't forget to sign your assembly with a strong name.

    Happy Dynamics coding!

    ..X..

     



    Wednesday, June 22, 2011 9:14 PM
  • I recently made a presentation for CRMUG about this very topic, and have some code that you can use up on my blog:  http://crmentropy.blogspot.com/2011/06/silverlight-and-crm-4.html

    This weekend, I'll be working up a quick example of how to use the code.  Hopefully, you find it to your liking, and sufficient to your needs.


    Dave Berry - MVP Dynamics CRM - http:\\crmentropy.blogspot.com Please follow the forum guidelines when inquiring of the dedicated CRM community for assistance.
    Friday, June 24, 2011 4:52 PM
    Moderator