none
Problem logging in to Office 365 Sharepoint Online from Webole hosted in the cloud

    Question

  • Hi,

    I have a problem logging in to the Office 365 Sharepoint Online from a WebRole hosted in the cloud.
    I have a WebService that logs in a user to Sharepoint Online as described in this post:
    http://www.wictorwilen.se/Post/How-to-do-active-authentication-to-Office-365-and-SharePoint-Online.aspx

    When starting my application with the cloud emulator in Visual Studio 2010, everything works fine.
    When uploading the WebRole to my Azure Account, i get the following message:

    Meldung: Unhandled Error in Silverlight Application Invoke operation 'GetGroups' failed. The remote server returned an error: (403) Forbidden.  at System.Net.HttpWebRequest.GetResponse()
      at Microsoft.SharePoint.Client.SPWebRequestExecutor.Execute()
      at Microsoft.SharePoint.Client.ClientContext.EnsureFormDigest()
      at Microsoft.SharePoint.Client.ClientContext.ExecuteQuery()
      at BTC.WFC.Dashboard.RiaServices.Web.UserInformationService.GetGroups(String username) in D:ProjekteSVN runkCORE_DevelopmentWFC-DashboardsrcDashboardServicesBTC.WFC.Dashboard.RiaServices.WebUserInformationService.cs:line 35
      at GetGroups(DomainService , Object[] )
      at System.ServiceModel.DomainServices.Server.ReflectionDomainServiceDescriptionProvider.ReflectionDomainOperationEntry.Invoke(DomainService domainService, Object[] parameters)
      at System.ServiceModel.DomainServices.Server.DomainService.Invoke(InvokeDescription invokeDescription, IEnumerable`1& validationErrors)
      at System.ServiceModel.DomainServices.Hosting.InvokeOperationBehavior.OperationInvoker.InvokeCore(Object instance, Object[] inputs, Object[]& outputs)

    Is there any restriction or is there some configuration stuff that must be different when using the service in the cloud?

    Kind regards,
    Maik

    Wednesday, August 24, 2011 1:42 PM

All replies

  • Greetings Maik

     

    The reason you are getting this error message is because the program is failing authentication with Office 365, as it uses claims based authentication a token also needs to be sent for authentication to work.

     

    I have found the following resources to assist you with this process:

     

    http://msdn.microsoft.com/en-us/library/hh147177.aspx

     

    http://code.msdn.microsoft.com/Remote-Authentication-in-b7b6f43c

     

    This question is better answered in the Office 365 Communities Form. If you have any further questions regarding this issue, please create a thread here:

     

    http://community.office365.com/en-us/default.aspx

     

    Thank you.

    Thursday, August 25, 2011 5:47 PM
  • Hi Marcus,

    thanks for your replay. Unfortunatly that does not answer my question. I didn't write explicitly in my post, that i want an "silent" authentification without any authentication scrren shown. In the article i've posted there is a description how that works.

    The sample of Wictor works fine when starting it in my cloud emulator in VS2010. I successfully auth against my Sharepoint Online.

    Another problem that I have. I'm developing a Silverlight application. So I can't reference the SharePoint DLLs. The server side is stateless due to the hosting in cloud instances.

    Any other suggestions?

    Here is a code snippet, working in cloud emulator, failing in the cloud instance:

    public class UserInformationService : DomainService 
    {
      public List<string> GetGroupsForUser(string username)
      {      
        List<string> result = new List<string>();
          
        string url = "https://my.sharepoint.com/";
        string adminUser = "admin@my.onmicrosoft.com";
        string password = "XXX";
    
        MsOnlineClaimsHelper claimsHelper = new MsOnlineClaimsHelper(url, adminUser, password);
        using (ClientContext context = new ClientContext(url))
        {
          context.ExecutingWebRequest += claimsHelper.clientContext_ExecutingWebRequest;
    
          //Do some stuff in SP Object Model
    
        }
        return result;
      }
    }
    
    public class MsOnlineClaimsHelper
    {
      #region Properties
    
      readonly string _username;
      readonly string _password;
      readonly bool _useRtfa;
      readonly Uri _host;
    
      CookieContainer _cachedCookieContainer = null;
      DateTime _expires = DateTime.MinValue;
    
      #endregion
    
      #region Constructors
      public MsOnlineClaimsHelper(string host, string username, string password)
          : this(new Uri(host), username, password)
      {
      }
       
      public MsOnlineClaimsHelper(Uri host, string username, string password)
      {
        _host = host;
        _username = username;
        _password = password;
        _useRtfa = true;
      }
      
      public MsOnlineClaimsHelper(Uri host, string username, string password, bool useRtfa)
      {
        _host = host;
        _username = username;
        _password = password;
        _useRtfa = useRtfa;
      }
      #endregion
    
      #region Constants
      public const string office365STS = "https://login.microsoftonline.com/extSTS.srf";
      public const string office365Login = "https://login.microsoftonline.com/login.srf";
      public const string office365Metadata = "https://nexus.microsoftonline-p.com/federationmetadata/2007-06/federationmetadata.xml";
      public const string wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
      public const string wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
      private const string userAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)";
      #endregion
    
      class MsoCookies
      {
        public string FedAuth { get; set; }
        public string rtFa { get; set; }
       
        public DateTime Expires { get; set; }
        public Uri Host { get; set; }
      }
    
      // Method used to add cookies to CSOM
      public void clientContext_ExecutingWebRequest(object sender, WebRequestEventArgs e)
      {
        e.WebRequestExecutor.WebRequest.CookieContainer = getCookieContainer();
        //e.WebRequestExecutor.WebRequest.UserAgent = userAgent;
      }
    
      // Creates or loads cached cookie container
      CookieContainer getCookieContainer()
      {
        if (_cachedCookieContainer == null || DateTime.Now > _expires)
        {
          // Get the SAML tokens from SPO STS (via MSO STS) using fed auth passive approach
          MsoCookies cookies = getSamlToken();
    
          if (cookies != null && !string.IsNullOrEmpty(cookies.FedAuth))
          {
            // Create cookie collection with the SAML token          
            _expires = cookies.Expires;
            CookieContainer cc = new CookieContainer();
    
            // Set the FedAuth cookie
            Cookie samlAuth = new Cookie("FedAuth", cookies.FedAuth)
            {
              Expires = cookies.Expires,
              Path = "/",
              Secure = cookies.Host.Scheme == "https",
              HttpOnly = true,
              Domain = cookies.Host.Host
            };
            cc.Add(samlAuth);
    
    
            if (_useRtfa)
            {
              // Set the rtFA (sign-out) cookie, added march 2011
              Cookie rtFa = new Cookie("rtFA", cookies.rtFa)
              {
                Expires = cookies.Expires,
                Path = "/",
                Secure = cookies.Host.Scheme == "https",
                HttpOnly = true,
                Domain = cookies.Host.Host
              };
              cc.Add(rtFa);
            }
            _cachedCookieContainer = cc;
            return cc;
          }
          return null;
        }
        return _cachedCookieContainer;
      }
    
      public CookieContainer CookieContainer
      {
        get
        {
          if (_cachedCookieContainer == null || DateTime.Now > _expires)
          {
            return getCookieContainer();
          }
          return _cachedCookieContainer;
        }
      }
    
      private MsoCookies getSamlToken()
      {
        MsoCookies ret = new MsoCookies();
    
        try
        {
          var sharepointSite = new
          {
            Wctx = office365Login,
            Wreply = _host.GetLeftPart(UriPartial.Authority) + "/_forms/default.aspx?wa=wsignin1.0"
          };
    
          //get token from STS
          string stsResponse = getResponse(office365STS, sharepointSite.Wreply);
    
          // parse the token response
          XDocument doc = XDocument.Parse(stsResponse);
    
          // get the security token
          var crypt = from result in doc.Descendants()
                where result.Name == XName.Get("BinarySecurityToken", wsse)
                select result;
    
          // get the token expiration
          var expires = from result in doc.Descendants()
                 where result.Name == XName.Get("Expires", wsu)
                 select result;
          ret.Expires = Convert.ToDateTime(expires.First().Value);
    
          HttpWebRequest request = createRequest(sharepointSite.Wreply);
          byte[] data = Encoding.UTF8.GetBytes(crypt.FirstOrDefault().Value);
          using (Stream stream = request.GetRequestStream())
          {
            stream.Write(data, 0, data.Length);
            stream.Close();
    
            using (HttpWebResponse webResponse = request.GetResponse() as HttpWebResponse)
            {
    
              // Handle redirect, added may 2011 for P-subscriptions
              if (webResponse.StatusCode == HttpStatusCode.MovedPermanently)
              {
                HttpWebRequest request2 = createRequest(webResponse.Headers["Location"]);
                using (Stream stream2 = request2.GetRequestStream())
                {
                  stream2.Write(data, 0, data.Length);
                  stream2.Close();
    
                  using (HttpWebResponse webResponse2 = request2.GetResponse() as HttpWebResponse)
                  {
                    ret.FedAuth = webResponse2.Cookies["FedAuth"].Value;
                    ret.rtFa = webResponse2.Cookies["rtFa"].Value;
                    ret.Host = request2.RequestUri;
                  }
                }
              }
              else
              {
                ret.FedAuth = webResponse.Cookies["FedAuth"].Value;
                ret.rtFa = webResponse.Cookies["rtFa"].Value;
                ret.Host = request.RequestUri;
              }
            }
          }
        }
        catch (Exception ex)
        {
          return null;
        }
        return ret;
      }
    
      static HttpWebRequest createRequest(string url)
      {
        HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.CookieContainer = new CookieContainer();
        request.AllowAutoRedirect = false; // Do NOT automatically redirect
        request.UserAgent = userAgent;
        return request;
      }
    
      private string getResponse(string stsUrl, string realm)
      {
        RequestSecurityToken rst = new RequestSecurityToken
        {
          RequestType = WSTrustFeb2005Constants.RequestTypes.Issue,
          AppliesTo = new EndpointAddress(realm),
          KeyType = WSTrustFeb2005Constants.KeyTypes.Bearer,
          TokenType = Microsoft.IdentityModel.Tokens.SecurityTokenTypes.Saml11TokenProfile11
        };
    
        WSTrustFeb2005RequestSerializer trustSerializer = new WSTrustFeb2005RequestSerializer();
    
        WSHttpBinding binding = new WSHttpBinding();
    
        binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
    
        binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
        binding.Security.Message.EstablishSecurityContext = false;
        binding.Security.Message.NegotiateServiceCredential = false;
    
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
    
        EndpointAddress address = new EndpointAddress(stsUrl);
    
        using (WSTrustFeb2005ContractClient trustClient = new WSTrustFeb2005ContractClient(binding, address))
        {
          trustClient.ClientCredentials.UserName.UserName = _username;
          trustClient.ClientCredentials.UserName.Password = _password;
          Message response = trustClient.EndIssue(
            trustClient.BeginIssue(
              Message.CreateMessage(
                MessageVersion.Default,
                WSTrustFeb2005Constants.Actions.Issue,
                new RequestBodyWriter(trustSerializer, rst)
              ),
              null,
              null));
          trustClient.Close();
          using (XmlDictionaryReader reader = response.GetReaderAtBodyContents())
          {
            return reader.ReadOuterXml();
          }
        }
      }
    }
    


    Happy investigating.

    Thanks,
    Maik

    Friday, August 26, 2011 6:25 AM
  • Thanks so much Maik,

    The code you posted works great!

    Update:

    Well, it works great on Dev machine in Visual Studio environment, but gives Forbidden (Error 403) when deployed on IIS on a Windows server 2008  :(

    Update2:

    The server was missing Windows Identity Foundation (http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=17331) Once I installed that and rebooted the machine, it works like a charm!

    • Edited by sarithab Tuesday, January 31, 2012 1:47 AM
    Saturday, January 28, 2012 12:10 AM
  • Hey Maik, I also am trying to do the same thing in SilverLight. Did you ever figure out a way to solve this issue?

    thank you

    Al

    Saturday, April 14, 2012 6:15 PM
  • hi Maik,

    I was also trying the same thing to be deployed as WCF service to azure , i am also getting same error, did you figured out what is causing the problem?

    thanks

    vimal


    regards Vimal

    Thursday, May 31, 2012 7:38 AM