Quantcast
Channel: Omar Venado's Blog

Windows 8 Store Apps + new Office 365 Enterprise Preview (SharePoint Online)

$
0
0

So this is my very first blog post here on MSDN blogs. Personally, I’m excited to write it as it involves 2 great Microsoft technologies I’m very passionate about: Windows 8 and the new Office 365. In this post I will cover the communication fundamentals of how to talk to the new Office 365, specifically SharePoint, from a Windows Store App. As part of the Office Division I’m particularly hyped about the new Office 365 offering and all the great value it brings to our customers. If you haven’t done so, I encourage you to sign up and check out the new Office 365 Technical Preview here.

Last week I decided to develop my first Windows Store App and the one thing I wanted to do was to leverage SharePoint Online as my backend service. SharePoint offers a lot of collaboration and document management capabilities as a service through its CSOM (Client-Side Object Model) APIs and the new REST services. Since I’m a bit more proficient with C# than with Javascript the CSOM managed API seemed like the right choice. I thought it was going to be as straight forward as looking for an article on the web on how to develop a Windows Store App in C# that uses the managed CSOM API to talk to SharePoint Online. Surprisingly I was wrong. WictorWilen’sarticle (great article by the way) is the closest I could find. However, there were a few challenges to make the code work for a Windows Store App:

 

1)      1) The .NET assembly for  Windows Store Apps is a subset of the .NET framework so it doesn’t have all the goodies such as the WS Trust classes to communicate to the Microsoft Online Services STS.

2)      2) Most importantly, the compiler complained that System.Web.Services.dll cannot be resolved when I referenced the CSOM dlls (Microsoft.SharePoint.Client and Microsoft.SharePoint.Client.Runtime).

At this point I’m starting to realize this is not exactly going to be a walk in the park. Since I can’t use the managed CSOM my only alternative is to use the new SharePoint REST services. And for the authentication part … well, this is pretty much what this blog post is about.

 

SharePoint Online (SPO) implements a brokered authentication model where it relies on a different service to authenticate the user. This service is the Microsoft Online Services Secure Token Service (MSO STS). When a user navigates to a page in SPO they need to authenticate first. SPO will redirect the request to the MSO STS so the user can enter her credentials. After the STS validates the user’s identity it sends back a security token to the browser. The browser then presents the security token to SPO and after SPO validates the token it issues a cookie that allows the user to finally access the SharePoint page. All this is opaque to the user when done from the browser. The goal here is to allow the user to access SPO resources without leaving our Win 8 App (user experience is key) so our app needs to perform all these steps as well. Next, I will explain the code to make authenticated requests to SPO REST services but first I will tell you the code is written for illustration purposes only so I encourage you to make it more robust, elegant, etc. if you are planning to use it in your apps. Also, I must say I was learning Windows 8 App development on the go so, again, please refer to the Windows Store App samples for best patterns and practices. BTW, you can download the sample code at the end of the post if you want to give it a shot right away. Ok, now that you have been warned let’s dive into it.

 

The diagram below shows the communication flow to make an authenticated REST call to an SPO site from a Windows Store App.

 

Step 1: Prompt user for credentials and get SAML security token from MSO STS (1, 2 and 3 in the diagram above)

Since the WS Trust WCF classes are not included in the .NET for Windows Store Apps assembly I chose to use plain old vanilla http requests. The first thing is to ask the user for credentials and the SPO site she wants to connect to. I created a very simple page to collect such info.

 

 


 The “Click” event handler for the “Login” button is where we are going to try to sign in the user to SPO. The authentication code is in a helper class called “SpoAuthUtility”. Let’s take a look: 

 

        private  asyncvoidloginButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Take the user's Office 365 credentials and
                //
attempt to sign into SharePoint Online

                 awaitSpoAuthUtility.Create(

                    newUri(this.siteUrlInput.Text),

                    this.usernameInput.Text,

                    this.passwordInput.Password);

 

The create method instantiates the helper class and calls GetCookieContainer which gets the auth cookies from SPO. But first it needs to authenticate with MSO STS. This logic is encapsulated in the GetMsoStsSamlToken method which does 2 things:

 

1) Constructs an issue request SOAP message that conforms to the WS-Trust standard specifying a SAML security token type, the user’s credentials, and the SPO site url. This is what the request security token soap message looks like:

 

<s:Envelopexmlns:s="http://www.w3.org/2003/05/soap-envelope"xmlns:a="http://www.w3.org/2005/08/addressing"xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <s:Header>
    <a:Actions:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:Tos:mustUnderstand="1">https://login.microsoftonline.com/extSTS.srf</a:To>
    <o:Securitys:mustUnderstand="1"xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <o:UsernameToken>
        <o:Username>[username]</o:Username>
        <o:Password>[password]</o:Password>
      </o:UsernameToken>
    </o:Security>
  </s:Header>
  <s:Body>
    <t:RequestSecurityTokenxmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
      <wsp:AppliesToxmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
        <a:EndpointReference>
          <a:Address>[url]</a:Address>
        </a:EndpointReference>
      </wsp:AppliesTo>      <t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>      <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
      <t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
    </t:RequestSecurityToken>
  </s:Body>
</s:Envelope>

 

2) Makes the request to the MSO STS and extracts the security token from the body of the response

 

        privateasyncTask<SamlSecurityToken> GetMsoStsSAMLToken()

        {

            // Makes a request that conforms with the WS-Trust standard to

            // Microsoft Online Services Security Token Service to get a SAML

            // security token back so we can then use it to sign the user to SPO

 

            // generate the WS-Trust security token request SOAP message passing

            // in the user's credentials and the site we want access to

            byte[] saml11RTBytes = Encoding.UTF8.GetBytes(ParameterizeamlRTString(

                this.spSiteUrl.ToString(),

                this.username,

                this.password));

 

            // make the post request to MSO STS with the WS-Trust payload

            byte[] response = awaitHttpUtility.SendHttpRequest(

                newUri(msoStsUrl),

                HttpMethod.Post,

                newMemoryStream(saml11RTBytes),

                "application/soap+xml; charset=utf-8",

                null);

 

            StreamReadersr = newStreamReader(newMemoryStream(response));

 

            // the SAML security token is in the BinarySecurityToken element of the message body

            XDocumentxDoc = XDocument.Parse(sr.ReadToEnd());

            varbinaryST = from e inxDoc.Descendants()

                           wheree.Name == XName.Get("BinarySecurityToken", wsse)

                           select e;

 

            // get the security token expiration date from the message body

            var expires = from e inxDoc.Descendants()

                          wheree.Name == XName.Get("Expires", wsu)

                          select e;

 

            SamlSecurityTokensamlST = newSamlSecurityToken();

            samlST.BinarySecurityToken = Encoding.UTF8.GetBytes(binaryST.FirstOrDefault().Value);

            samlST.Expires = DateTime.Parse(expires.FirstOrDefault().Value);

 

            returnsamlST;
        }

 

 

Step 2: Sign in the user into SPO by presenting the security token we got from the MSO STS and get the authentication cookies (4, 5 and 6 in the diagram above)

The GetSPOAuthCookies method will make a post request to SPO and get the authentication cookies which come in the response headers. There are 2 encrypted cookies that we get back from SPO: 1) the FedAuth cookie which is basically the cookie we need to pass along with any subsequent requests to the service and 2) the rtFA cookie which is necessary if we want to sign the user out.

 

        privateasyncTask<SPOAuthCookies> GetSPOAuthCookies(SamlSecurityTokenstsToken)
        {
            UrisiteUri = this.spSiteUrl;
            UriwsSigninUrl = newUri(String.Format("{0}://{1}/{2}", siteUri.Scheme, siteUri.Authority, spowssigninUri));
            varclientHandler = newHttpClientHandler(); 

            awaitHttpUtility.SendHttpRequest(
                wsSigninUrl,
                HttpMethod.Post,
                newMemoryStream(stsToken.BinarySecurityToken),
                "application/x-www-form-urlencoded",
                clientHandler);

            SPOAuthCookiesspoAuthCookies = newSPOAuthCookies();
            spoAuthCookies.FedAuth = clientHandler.CookieContainer.GetCookies(wsSigninUrl)["FedAuth"].Value;
            spoAuthCookies.RtFA = clientHandler.CookieContainer.GetCookies(wsSigninUrl)["rtFA"].Value;
            spoAuthCookies.Expires = stsToken.Expires;
            spoAuthCookies.Host = wsSigninUrl;

            returnspoAuthCookies;
        }


 

Step 3: Make the actual OData request to SPO REST services, get back some goodness and render it using the Windows 8 UI (7 and 8 in the diagram above)

In this case I’m making a request to get all the SP lists from the site that are visible to the user from the SPO web interface. In this case I’m using a GET request. For write operations you need to use POST, PUT, and DELETE methods. I will cover a simple example on how to perform a write operation below. You can find more about the SPO REST services notation based on the OData protocol here.

 

            // Send a jsonodata request to SPO rest services to fetch all lists in the site that are visible to the end user.
            var response = awaitHttpUtility.SendODataJsonRequest(
                newUri(String.Format("{0}/_api/web/lists?$filter=Hidden eq false", SpoAuthUtility.Current.SiteUrl)),
                HttpMethod.Get, // reading data from SP through the rest api usually uses the GET verb 
                null,
                newHttpClientHandler(),
                SpoAuthUtility.Current// pass in the helper object that allows us to make authenticated calls to SPO rest services
                );
 

The method below simply appends the SPO authentication cookies we got earlier to the http request and specifies we want to get JSON back (OData will return ATOM payloads by default). JSON has become extremely popular and the .NET framework for Windows Store Apps provides some really helpful methods to parse JSON strings or implement you own custom serializer.

 

        publicstaticasyncTask<byte[]> SendODataJsonRequest(Uriuri, HttpMethod method, StreamrequestContent, HttpClientHandlerclientHandler, SpoAuthUtilityauthUtility, Dictionary<string, string> headers = null)
        {
            if (clientHandler.CookieContainer == null)
                clientHandler.CookieContainer = newCookieContainer();

            CookieContainercookieContainer = awaitauthUtility.GetCookieContainer(); // get the auth cookies from SPO after authenticating with Microsoft Online Services STS

            foreach (Cookie c incookieContainer.GetCookies(uri))
            {
                clientHandler.CookieContainer.Add(uri, c); // apppend SPO auth cookies to the request
            } 

            returnawaitSendHttpRequest(
                uri
                method,
                requestContent
                "application/json;odata=verbose;charset=utf-8", // the http content type for the JSON flavor of SP REST services 
                clientHandler
                headers);
        }

 

 

 

Finally, manipulate the JSON we got back from SPO and feed it to our app UI. The screenshots below show the lists and list items in my SkyDrive Pro (aka SharePoint MySite).

 

 

 

 

 

That’s about it. Before I wrap this up 2 things:

 

1) If you want to write to SPO you need to append an additional piece of information to your REST call. It is call a dynamic canary and it is used to prevent cross site scripting attacks. You can learn more about it here. For all practical purposes, if you do not append the canary to your REST request SharePoint will not let the write go through even if the user has all the right permissions. So here’s you do it. 

 

        publicstaticasyncTask<byte[]> SendODataJsonRequestWithCanary(Uriuri, HttpMethod method, StreamrequestContent, HttpClientHandlerclientHandler, SpoAuthUtilityauthUtility)

        {

            // Make a post request to {siteUri}/_api/contextinfo to get the canary

            var response = awaitHttpUtility.SendODataJsonRequest(

                newUri(String.Format("{0}/_api/contextinfo", SpoAuthUtility.Current.SiteUrl)),

                HttpMethod.Post,

                null,

                clientHandler,

                SpoAuthUtility.Current);

 

            Dictionary<String, IJsonValue> dict = newDictionary<string, IJsonValue>();

            HttpUtility.ParseJson(JsonObject.Parse(Encoding.UTF8.GetString(response, 0, response.Length)), dict); // parse the JSON response containing the canary

 

            string canary = dict["FormDigestValue"].GetString(); // the canary is contained in the FormDigestValue of the response body

 

            // Make the OData request passing the canary in the request headers

            returnawaitHttpUtility.SendODataJsonRequest(

                uri,

                method,

                requestContent,

                clientHandler,

                SpoAuthUtility.Current,

                newDictionary<string, string> {

                { "X-RequestDigest", canary  }

                });
        }

 

To get the dynamic canary you have to make a POST request to {siteUrl}/_api/contextinfo with an empty body. Note you still have to append the auth cookies to the canary request. The canary will be in the body of the response under the FormDigestValue element. Now that you have the canary all you need to do is to append it to the “X-RequestDigest” headers of your write REST request (don’t forget the auth cookies) and voila! You are now able to write to your SPO site. The code has an example that adds a new list to the site.

 

      // Make a POST request to create the new SP list
     
// This is a write call so it requires the canary to be appended to the request headers

                var response = awaitHttpUtility.SendODataJsonRequestWithCanary(
                    newUri(String.Format("{0}/_api/web/lists/", SpoAuthUtility.Current.SiteUrl)),
                    HttpMethod.Post,
                    newMemoryStream(Encoding.UTF8.GetBytes(addItemJsonString)),
                    newHttpClientHandler(),
                    SpoAuthUtility.Current);


 

And this is what the body of the request looks like in JSON notation for adding a new document library

{
  '__metadata': { 'type': 'SP.List' }, 
  'AllowContentTypes': true, 
  'BaseTemplate': 101, 
  'ContentTypesEnabled': true, 
  'Description': 'My list description', 
  'Title': '[title]'
}

 

2) Finally, you can download the sample code here. Enjoy!

 

And with that we have come to the end of this post. I hope you found it helpful. I will finish by saying I’m super enthused about Windows 8 and the new Office 365 and what they bring to end users and developers alike. I also think the combination of both is a fantastic foundation for developers to create the next generation of collaboration and productivity applications (business, education, finance, etc.) that take advantage of the new Windows 8 gorgeous touch interface plus the best collaboration platform there is with Office 365.

 Part 2: Windows Store Apps for SharePoint Online with SSO.

 

Legal Disclaimer:

Everything written in this blog represents my own views only and not those of my employer. Also, any code is provided “AS IS” without warranty of any kind either expressed or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.


Developing Windows 8 Store Apps for SharePoint Online with SSO (Single Sign On)

$
0
0

A few folks have asked me if I could tweak the code from my previous post to enable SSO into SharePoint Online from a Windows Store App so here I am again with Parte Dos. This basically means users can use their corporate credentials or, even better, Windows integrated authentication (if they are connected to their corporate network) to access SPO content right from the Windows Store App. Office 365 has full support for SSO via ADFS (Active Directory Federation Services).

 

There are many advantages to SSO but the most important (always thinking about the customer) in my opinion are:

 

1)      1) Users don’t have to remember and enter multiple pairs of credentials to sign into different services/applications

2)      2) User’s don’t’ have to manage multiple credentials such as periodically updating passwords for security reasons

When you sign up for an O365 tenancy you can configure it for SSO as described here. If you are interested in a quick overview of how SSO works in O365 this is a good place to start. I also recommend looking over this excellent whitepaper that explains in detail SSO configuration as well as the mechanics behind the core SSO scenarios. This post assumes an SSO has been configured as outlined in the links above and an ADFS proxy has been deployed to allow users to connect from outside their corporate network.

 

Now before we proceed you should know the code referenced in this post was written solely for illustration purposes (it is by NO means production quality). Also, it is provided as is without warranty of any kind either expressed or implied. Ok, now that you have been warned let’s dive in.

 

I modified the code in the previous post so the Windows Store App can now authenticate the user in 3 different ways:

 

1)      1) Using integrated Windows authentication if the user is connected to the corporate network

2)      2) Using the user’s corporate credentials if the user is outside of the corporate network

3)      3) Using the user’s O365 credentials (from the previous post)

For 1 and 2 above this post assumes SSO has been configured properly so O365 can recognize the user as being part of a federated domain. Needless to say, all communications must happen over SSL for security reasons. Next we will walk through the code changes for 1 and 2. Most code changes are in SpoAuthUtility.cs. Now for those of you that know all this stuff already and can’t wait to get your hands dirty you can scroll down to the end of the post and download the sample code.

 

1.       SSO via Integrated Windows Authentication


 

 

The diagram above shows at high level how the App achieves SSO using integrated Windows Authentication.

 

The first thing the App does is to ask the user for his UPN (User Principal Name). This is normally in the form of user1@contoso.com.  The Windows Store App presents the UPN to the HRD (Home Realm Discovery) service to resolve the url of the corporate ADFS proxy to use for authentication. This step is not necessary if your app already knows the address of your corporate ADFS.

 

 

const stringmsoHrdUrl = "https://login.microsoftonline.com/GetUserRealm.srf";

 

private async Task<Uri> GetAdfsAuthUrl()

        {

          // make a post request with the user's login name to

// MSO HRD (Home Realm Discovery) service so it can find

// out the url of the federation service (corporate ADFS)

// responsible for authenticating the user

            byte[] response = await HttpUtility.SendHttpRequest(

                 new Uri(msoHrdUrl),

                 HttpMethod.Post,

                 new MemoryStream(Encoding.UTF8.GetBytes(String.Format("handler=1&login={0}", this.username))), // pass in the login name in the body of the form

                 "application/x-www-form-urlencoded",

                 null);

 

            StreamReadersr = newStreamReader(new MemoryStream(response));

            Dictionary<String, IJsonValue> dict = new Dictionary<string, IJsonValue>();

            HttpUtility.ParseJson(JsonObject.Parse(Encoding.UTF8.GetString(response, 0, response.Length)), dict);

 

            // the corporate STS url is in the AuthURL element of the response body

            UricorpAdfsProxyUrl = dict.ContainsKey("AuthURL") ? new Uri(dict["AuthURL"].GetString()) : null;

 

            returncorpAdfsProxyUrl;

        }

 

·        The App then sends a security token request via HTTP GET to the corporate ADFS integrate authentication endpoint specifying 2 main things: the username and the realm which in this case is the O365 federation service. The url looks something like the following:

https://{contosoADFSUrl}/adfs/ls/auth/integrated/?cbcxt=&vv=&username=user@contoso.com&mkt=&lc=&wa=wsignin1.0&wtrealm=urn:federation:MicrosoftOnline

 

If the user is connected to the corporate network the service request will automatically get authenticated by the magic of Kerberos and we will get a signed SAML token back in the body of the response that asserts who the user is. The token will be in the form of a SAML assertion (refer to the WS-Trust standard for more details). This is our logon token.

 

         HttpClientHandler handler = new HttpClientHandler();
            handler.UseDefaultCredentials = true; // use the default credentials so Kerberos can take care of authenticating our request

 

            byte[] stsresponse = await HttpUtility.SendHttpRequest(
                this.adfsIntegratedAuthUrl,
                HttpMethod.Get,
                null,
                "text/html; charset=utf-8",
                handler);

 

// the security token response we got from ADFS is in the wresult input element 
                varwresult = from e inform.FirstOrDefault().Descendants()
                              wheree.Name == XName.Get("input") &&
                              e.Attribute(XName.Get("name")) != null&&
                              e.Attribute(XName.Get("name")).Value == "wresult"
                              select e;

 

 

// the logon token is in the SAML assertion element
                    XDocument xDoc1 = XDocument.Parse(wresult.FirstOrDefault().Attribute(XName.Get("value")).Value, LoadOptions.PreserveWhitespace);
                    var assertion = from e in xDoc1.Descendants()
                                    wheree.Name == XName.Get("Assertion", saml)
                                    select e;

 

 

The App now issues the security token request to the MSO STS. However, this time instead of appending the user’s O365 credentials to the SOAP message’s security header (as in the previous post) it appends the logon token we got from the corporate ADFS. MSO STS checks the validity of the token and if everything is happy it returns a service token which we can then use to sign into SPO and get the federated authentication cookies (just like in the previous post).

 

// generate the WS-Trust security token request SOAP message passing in the logon token we got from the corporate ADFS

// and the site we want access to

 saml11RTBytes = Encoding.UTF8.GetBytes(ParameterizeSoapRequestTokenMsgWithAssertion(

                      this.spSiteUrl.ToString(),

                      logonToken,
                      msoStsUrl));

 // make the post request to MSO STS with the WS-Trust payload
                byte[] response = await HttpUtility.SendHttpRequest(
                    new Uri(msoStsUrl),
                    HttpMethod.Post,
                    new MemoryStream(saml11RTBytes),
                    "application/soap+xml; charset=utf-8",
                    null);

 

                StreamReadersr = new StreamReader(new MemoryStream(response));

// the SAML security token is in the BinarySecurityToken element of the message body
                XDocumentxDoc = XDocument.Parse(sr.ReadToEnd());
                varbinaryST = from e inxDoc.Descendants()
                               wheree.Name == XName.Get("BinarySecurityToken", wsse)
                               select e;

 

So as you notice the main difference from the previous code is we are now presenting the user’s logon token we got from the corporate ADFS to MSO STS as a proof/claim of who the user is. In this case, MSO STS is what is called a “relying party” in the claims/brokered authentication lingo and the corporate ADFS is what is called an “identity provider”. This basically means that MSO STS trusts the corporate ADFS has done its job authenticating the user and as long as the token signature is valid (hasn’t been tampered with) MSO STS will accept it as a proof of the user’s identity. Before all this code runs the trust relationship between MSO STS and the corporate ADFS should have been already established as outlined in the O365 SSO configuration references at the beginning of this post.

 

And that’s it for integrated Windows Authentication! Obviously, this is the preferred method as we never have to ask users for their credentials as long as they are logged into a machine joined to their domain and are connected to the corporate network.  

 

2.       SSO via user’s corporate credentials

 

 

 

Most steps happen the same way as in the integrated with auth approach except that in this case we ask users for their corporate credentials to get the logon token. This normally happens when users are not connected to the corporate network. This approach requires, as mentioned previously, an ADFS proxy service deployed following O365 SSO recommended guidelines.

 

The code assumes the ADFS proxy usernamemixed endpoint is available. The App asks for the user’s corpnet credentials and constructs a request security token message (following the WS-Trust standard). It sends the request to the usernamemixed endpoint and if the credentials are valid ADFS responds with the logon token. The logon token is then presented to MSO STS just as in the integrated win auth approach.

 

private async Task<string> GetAdfsSAMLTokenUsernamePassword()

        {

            // makes a seurity token request to the corporate ADFS proxy usernamemixed endpoint using
            // the user's corporate credentials. The logon token is used to talk to MSO STS to get
            // an O365 service token that can then be used to sign into SPO.

            stringsamlAssertion = null; 

            // the corporate ADFS proxy endpoint that issues SAML seurity tokens given username/password credentials 
            stringstsUsernameMixedUrl = String.Format("https://{0}/adfs/services/trust/2005/usernamemixed/", adfsAuthUrl.Host);

             // generate the WS-Trust security token request SOAP message passing in the user's corporate credentials 
            // and the site we want access to. We send the token request to the corporate ADFS proxy usernamemixed endpoint.
            byte[] requestBody = Encoding.UTF8.GetBytes(ParameterizeSoapRequestTokenMsgWithUsernamePassword(
                "urn:federation:MicrosoftOnline", // we are requesting a logon token to talk to the Microsoft Federation Gateway
                this.username,
                this.password,
                stsUsernameMixedUrl));

 

            try
            {

                byte[] response = await HttpUtility.SendHttpRequest(
                    new Uri(stsUsernameMixedUrl),
                    HttpMethod.Post,
                    new MemoryStream(requestBody),
                    "application/soap+xml; charset=utf-8",
                    null);

 

                // the logon token is in the SAML assertion element of the message body
                XDocumentxDoc = XDocument.Parse(Encoding.UTF8.GetString(response, 0, response.Length), LoadOptions.PreserveWhitespace);
                var assertion = from e inxDoc.Descendants()
                                wheree.Name == XName.Get("Assertion", saml)
                                select e;

 

 

And with that we have come to the end of this post. I hope you found it useful. Oh I almost forgot :)! You can download the sample code here.

 

Happy coding.

 

 

Legal Disclaimer:

Everything written in this blog represents my own views only and not those of my employer. Also, any code is provided “AS IS” without warranty of any kind either expressed or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.

 

The new OneNote API

$
0
0
Hi guys, It's been a while since the last post. A bunch of things happened in the last year and a half. I moved from the SharePoint to the OneNote team and I've been doing consumer stuff for a while now. More concretely I'm working on the OneNote API...(read more)

Windows 8 Store Apps + new Office 365 Enterprise Preview (SharePoint Online)

$
0
0

So this is my very first blog post here on MSDN blogs. Personally, I’m excited to write it as it involves 2 great Microsoft technologies I’m very passionate about: Windows 8 and the new Office 365. In this post I will cover the communication fundamentals of how to talk to the new Office 365, specifically SharePoint, from a Windows Store App. As part of the Office Division I’m particularly hyped about the new Office 365 offering and all the great value it brings to our customers. If you haven’t done so, I encourage you to sign up and check out the new Office 365 Technical Preview here.

Last week I decided to develop my first Windows Store App and the one thing I wanted to do was to leverage SharePoint Online as my backend service. SharePoint offers a lot of collaboration and document management capabilities as a service through its CSOM (Client-Side Object Model) APIs and the new REST services. Since I’m a bit more proficient with C# than with Javascript the CSOM managed API seemed like the right choice. I thought it was going to be as straight forward as looking for an article on the web on how to develop a Windows Store App in C# that uses the managed CSOM API to talk to SharePoint Online. Surprisingly I was wrong. Wictor Wilen’s article (great article by the way) is the closest I could find. However, there were a few challenges to make the code work for a Windows Store App:

 

1)      1) The .NET assembly for  Windows Store Apps is a subset of the .NET framework so it doesn’t have all the goodies such as the WS Trust classes to communicate to the Microsoft Online Services STS.

2)      2) Most importantly, the compiler complained that System.Web.Services.dll cannot be resolved when I referenced the CSOM dlls (Microsoft.SharePoint.Client and Microsoft.SharePoint.Client.Runtime).

At this point I’m starting to realize this is not exactly going to be a walk in the park. Since I can’t use the managed CSOM my only alternative is to use the new SharePoint REST services. And for the authentication part … well, this is pretty much what this blog post is about.

 

SharePoint Online (SPO) implements a brokered authentication model where it relies on a different service to authenticate the user. This service is the Microsoft Online Services Secure Token Service (MSO STS). When a user navigates to a page in SPO they need to authenticate first. SPO will redirect the request to the MSO STS so the user can enter her credentials. After the STS validates the user’s identity it sends back a security token to the browser. The browser then presents the security token to SPO and after SPO validates the token it issues a cookie that allows the user to finally access the SharePoint page. All this is opaque to the user when done from the browser. The goal here is to allow the user to access SPO resources without leaving our Win 8 App (user experience is key) so our app needs to perform all these steps as well. Next, I will explain the code to make authenticated requests to SPO REST services but first I will tell you the code is written for illustration purposes only so I encourage you to make it more robust, elegant, etc. if you are planning to use it in your apps. Also, I must say I was learning Windows 8 App development on the go so, again, please refer to the Windows Store App samples for best patterns and practices. BTW, you can download the sample code at the end of the post if you want to give it a shot right away. Ok, now that you have been warned let’s dive into it.

 

The diagram below shows the communication flow to make an authenticated REST call to an SPO site from a Windows Store App.

 

Step 1: Prompt user for credentials and get SAML security token from MSO STS (1, 2 and 3 in the diagram above)

Since the WS Trust WCF classes are not included in the .NET for Windows Store Apps assembly I chose to use plain old vanilla http requests. The first thing is to ask the user for credentials and the SPO site she wants to connect to. I created a very simple page to collect such info.

 

 

 The “Click” event handler for the “Login” button is where we are going to try to sign in the user to SPO. The authentication code is in a helper class called “SpoAuthUtility”. Let’s take a look: 

 

        private  asyncvoid loginButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Take the user’s Office 365 credentials and
                //
attempt to sign into SharePoint Online

                 awaitSpoAuthUtility.Create(

                    newUri(this.siteUrlInput.Text),

                    this.usernameInput.Text,

                    this.passwordInput.Password);

 

The create method instantiates the helper class and calls GetCookieContainer which gets the auth cookies from SPO. But first it needs to authenticate with MSO STS. This logic is encapsulated in the GetMsoStsSamlToken method which does 2 things:

 

1) Constructs an issue request SOAP message that conforms to the WS-Trust standard specifying a SAML security token type, the user’s credentials, and the SPO site url. This is what the request security token soap message looks like:

 

<s:Envelopexmlns:s=http://www.w3.org/2003/05/soap-envelopexmlns:a=http://www.w3.org/2005/08/addressingxmlns:u=http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd>
  <s:Header>
    <a:Actions:mustUnderstand=1>http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:Tos:mustUnderstand=1>https://login.microsoftonline.com/extSTS.srf</a:To>
    <o:Securitys:mustUnderstand=1xmlns:o=http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd>
      <o:UsernameToken>
        <o:Username>[username]</o:Username>
        <o:Password>[password]</o:Password>
      </o:UsernameToken>
    </o:Security>
  </s:Header>
  <s:Body>
    <t:RequestSecurityTokenxmlns:t=http://schemas.xmlsoap.org/ws/2005/02/trust>
      <wsp:AppliesToxmlns:wsp=http://schemas.xmlsoap.org/ws/2004/09/policy>
        <a:EndpointReference>
          <a:Address>[url]</a:Address>
        </a:EndpointReference>
      </wsp:AppliesTo>      <t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>      <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
      <t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
    </t:RequestSecurityToken>
  </s:Body>
</s:Envelope>

 

2) Makes the request to the MSO STS and extracts the security token from the body of the response

 

        privateasyncTask<SamlSecurityToken> GetMsoStsSAMLToken()

        {

            // Makes a request that conforms with the WS-Trust standard to

            // Microsoft Online Services Security Token Service to get a SAML

            // security token back so we can then use it to sign the user to SPO

 

            // generate the WS-Trust security token request SOAP message passing

            // in the user’s credentials and the site we want access to

            byte[] saml11RTBytes = Encoding.UTF8.GetBytes(ParameterizeamlRTString(

                this.spSiteUrl.ToString(),

                this.username,

                this.password));

 

            // make the post request to MSO STS with the WS-Trust payload

            byte[] response = awaitHttpUtility.SendHttpRequest(

                newUri(msoStsUrl),

                HttpMethod.Post,

                newMemoryStream(saml11RTBytes),

                application/soap+xml; charset=utf-8″,

                null);

 

            StreamReader sr = newStreamReader(newMemoryStream(response));

 

            // the SAML security token is in the BinarySecurityToken element of the message body

            XDocument xDoc = XDocument.Parse(sr.ReadToEnd());

            var binaryST = from e in xDoc.Descendants()

                           where e.Name == XName.Get(BinarySecurityToken, wsse)

                           select e;

 

            // get the security token expiration date from the message body

            var expires = from e in xDoc.Descendants()

                          where e.Name == XName.Get(“Expires”, wsu)

                          select e;

 

            SamlSecurityToken samlST = newSamlSecurityToken();

            samlST.BinarySecurityToken = Encoding.UTF8.GetBytes(binaryST.FirstOrDefault().Value);

            samlST.Expires = DateTime.Parse(expires.FirstOrDefault().Value);

 

            return samlST;
        }

 

 

Step 2: Sign in the user into SPO by presenting the security token we got from the MSO STS and get the authentication cookies (4, 5 and 6 in the diagram above)

The GetSPOAuthCookies method will make a post request to SPO and get the authentication cookies which come in the response headers. There are 2 encrypted cookies that we get back from SPO: 1) the FedAuth cookie which is basically the cookie we need to pass along with any subsequent requests to the service and 2) the rtFA cookie which is necessary if we want to sign the user out.

 

        privateasyncTask<SPOAuthCookies> GetSPOAuthCookies(SamlSecurityToken stsToken)
        {
            Uri siteUri = this.spSiteUrl;
            Uri wsSigninUrl = newUri(String.Format(“{0}://{1}/{2}”, siteUri.Scheme, siteUri.Authority, spowssigninUri));
            var clientHandler = newHttpClientHandler(); 

            awaitHttpUtility.SendHttpRequest(
                wsSigninUrl,
                HttpMethod.Post,
                newMemoryStream(stsToken.BinarySecurityToken),
                application/x-www-form-urlencoded,
                clientHandler);

            SPOAuthCookies spoAuthCookies = newSPOAuthCookies();
            spoAuthCookies.FedAuth = clientHandler.CookieContainer.GetCookies(wsSigninUrl)[“FedAuth”].Value;
            spoAuthCookies.RtFA = clientHandler.CookieContainer.GetCookies(wsSigninUrl)[“rtFA”].Value;
            spoAuthCookies.Expires = stsToken.Expires;
            spoAuthCookies.Host = wsSigninUrl;

            return spoAuthCookies;
        }

 

Step 3: Make the actual OData request to SPO REST services, get back some goodness and render it using the Windows 8 UI (7 and 8 in the diagram above)

In this case I’m making a request to get all the SP lists from the site that are visible to the user from the SPO web interface. In this case I’m using a GET request. For write operations you need to use POST, PUT, and DELETE methods. I will cover a simple example on how to perform a write operation below. You can find more about the SPO REST services notation based on the OData protocol here.

 

            // Send a json odata request to SPO rest services to fetch all lists in the site that are visible to the end user.
            var response = awaitHttpUtility.SendODataJsonRequest(
                newUri(String.Format(“{0}/_api/web/lists?$filter=Hidden eq false”, SpoAuthUtility.Current.SiteUrl)),
                HttpMethod.Get, // reading data from SP through the rest api usually uses the GET verb 
                null,
                newHttpClientHandler(),
                SpoAuthUtility.Current// pass in the helper object that allows us to make authenticated calls to SPO rest services
                );
 

The method below simply appends the SPO authentication cookies we got earlier to the http request and specifies we want to get JSON back (OData will return ATOM payloads by default). JSON has become extremely popular and the .NET framework for Windows Store Apps provides some really helpful methods to parse JSON strings or implement you own custom serializer.

 

        publicstaticasyncTask<byte[]> SendODataJsonRequest(Uri uri, HttpMethod method, Stream requestContent, HttpClientHandler clientHandler, SpoAuthUtility authUtility, Dictionary<string, string> headers = null)
        {
            if (clientHandler.CookieContainer == null)
                clientHandler.CookieContainer = newCookieContainer();

            CookieContainer cookieContainer = await authUtility.GetCookieContainer(); // get the auth cookies from SPO after authenticating with Microsoft Online Services STS

            foreach (Cookie c in cookieContainer.GetCookies(uri))
            {
                clientHandler.CookieContainer.Add(uri, c); // apppend SPO auth cookies to the request
            } 

            returnawait SendHttpRequest(
                uri
                method,
                requestContent
                application/json;odata=verbose;charset=utf-8″, // the http content type for the JSON flavor of SP REST services 
                clientHandler
                headers);
        }

 

 

 

Finally, manipulate the JSON we got back from SPO and feed it to our app UI. The screenshots below show the lists and list items in my SkyDrive Pro (aka SharePoint MySite).

 

 

 

 

 

That’s about it. Before I wrap this up 2 things:

 

1) If you want to write to SPO you need to append an additional piece of information to your REST call. It is call a dynamic canary and it is used to prevent cross site scripting attacks. You can learn more about it here. For all practical purposes, if you do not append the canary to your REST request SharePoint will not let the write go through even if the user has all the right permissions. So here’s you do it. 

 

        publicstaticasyncTask<byte[]> SendODataJsonRequestWithCanary(Uri uri, HttpMethod method, Stream requestContent, HttpClientHandler clientHandler, SpoAuthUtility authUtility)

        {

            // Make a post request to {siteUri}/_api/contextinfo to get the canary

            var response = awaitHttpUtility.SendODataJsonRequest(

                newUri(String.Format(“{0}/_api/contextinfo, SpoAuthUtility.Current.SiteUrl)),

                HttpMethod.Post,

                null,

                clientHandler,

                SpoAuthUtility.Current);

 

            Dictionary<String, IJsonValue> dict = newDictionary<string, IJsonValue>();

            HttpUtility.ParseJson(JsonObject.Parse(Encoding.UTF8.GetString(response, 0, response.Length)), dict); // parse the JSON response containing the canary

 

            string canary = dict[FormDigestValue].GetString(); // the canary is contained in the FormDigestValue of the response body

 

            // Make the OData request passing the canary in the request headers

            returnawaitHttpUtility.SendODataJsonRequest(

                uri,

                method,

                requestContent,

                clientHandler,

                SpoAuthUtility.Current,

                newDictionary<string, string> {

                { X-RequestDigest, canary  }

                });
        }

 

To get the dynamic canary you have to make a POST request to {siteUrl}/_api/contextinfo with an empty body. Note you still have to append the auth cookies to the canary request. The canary will be in the body of the response under the FormDigestValue element. Now that you have the canary all you need to do is to append it to the “X-RequestDigest” headers of your write REST request (don’t forget the auth cookies) and voila! You are now able to write to your SPO site. The code has an example that adds a new list to the site.

 

      // Make a POST request to create the new SP list
     
// This is a write call so it requires the canary to be appended to the request headers

                var response = awaitHttpUtility.SendODataJsonRequestWithCanary(
                    newUri(String.Format(“{0}/_api/web/lists/”, SpoAuthUtility.Current.SiteUrl)),
                    HttpMethod.Post,
                    newMemoryStream(Encoding.UTF8.GetBytes(addItemJsonString)),
                    newHttpClientHandler(),
                    SpoAuthUtility.Current);

 

And this is what the body of the request looks like in JSON notation for adding a new document library

{
  ‘__metadata’: { ‘type’: ‘SP.List‘ }, 
  AllowContentTypes‘: true, 
  BaseTemplate‘: 101, 
  ContentTypesEnabled‘: true, 
  ‘Description’: ‘My list description’, 
  ‘Title’: ‘[title]’
}

 

2) Finally, you can download the sample code here. Enjoy!

 

And with that we have come to the end of this post. I hope you found it helpful. I will finish by saying I’m super enthused about Windows 8 and the new Office 365 and what they bring to end users and developers alike. I also think the combination of both is a fantastic foundation for developers to create the next generation of collaboration and productivity applications (business, education, finance, etc.) that take advantage of the new Windows 8 gorgeous touch interface plus the best collaboration platform there is with Office 365.

 Part 2: Windows Store Apps for SharePoint Online with SSO.

 

Legal Disclaimer:

Everything written in this blog represents my own views only and not those of my employer. Also, any code is provided “AS IS” without warranty of any kind either expressed or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.

Developing Windows 8 Store Apps for SharePoint Online with SSO (Single Sign On)

$
0
0

A few folks have asked me if I could tweak the code from my previous post to enable SSO into SharePoint Online from a Windows Store App so here I am again with Parte Dos. This basically means users can use their corporate credentials or, even better, Windows integrated authentication (if they are connected to their corporate network) to access SPO content right from the Windows Store App. Office 365 has full support for SSO via ADFS (Active Directory Federation Services).

 

There are many advantages to SSO but the most important (always thinking about the customer) in my opinion are:

 

1)      1) Users don’t have to remember and enter multiple pairs of credentials to sign into different services/applications

2)      2) User’s don’t’ have to manage multiple credentials such as periodically updating passwords for security reasons

When you sign up for an O365 tenancy you can configure it for SSO as described here. If you are interested in a quick overview of how SSO works in O365 this is a good place to start. I also recommend looking over this excellent whitepaper that explains in detail SSO configuration as well as the mechanics behind the core SSO scenarios. This post assumes an SSO has been configured as outlined in the links above and an ADFS proxy has been deployed to allow users to connect from outside their corporate network.

 

Now before we proceed you should know the code referenced in this post was written solely for illustration purposes (it is by NO means production quality). Also, it is provided as is without warranty of any kind either expressed or implied. Ok, now that you have been warned let’s dive in.

 

I modified the code in the previous post so the Windows Store App can now authenticate the user in 3 different ways:

 

1)      1) Using integrated Windows authentication if the user is connected to the corporate network

2)      2) Using the user’s corporate credentials if the user is outside of the corporate network

3)      3) Using the user’s O365 credentials (from the previous post)

For 1 and 2 above this post assumes SSO has been configured properly so O365 can recognize the user as being part of a federated domain. Needless to say, all communications must happen over SSL for security reasons. Next we will walk through the code changes for 1 and 2. Most code changes are in SpoAuthUtility.cs. Now for those of you that know all this stuff already and can’t wait to get your hands dirty you can scroll down to the end of the post and download the sample code.

 

1.       SSO via Integrated Windows Authentication

 

 

The diagram above shows at high level how the App achieves SSO using integrated Windows Authentication.

 

The first thing the App does is to ask the user for his UPN (User Principal Name). This is normally in the form of user1@contoso.com.  The Windows Store App presents the UPN to the HRD (Home Realm Discovery) service to resolve the url of the corporate ADFS proxy to use for authentication. This step is not necessary if your app already knows the address of your corporate ADFS.

 

 

const string msoHrdUrl = “https://login.microsoftonline.com/GetUserRealm.srf;

 

private async Task<Uri> GetAdfsAuthUrl()

        {

          // make a post request with the user’s login name to

// MSO HRD (Home Realm Discovery) service so it can find

// out the url of the federation service (corporate ADFS)

// responsible for authenticating the user

            byte[] response = await HttpUtility.SendHttpRequest(

                 new Uri(msoHrdUrl),

                 HttpMethod.Post,

                 new MemoryStream(Encoding.UTF8.GetBytes(String.Format(“handler=1&login={0}”, this.username))), // pass in the login name in the body of the form

                 application/x-www-form-urlencoded,

                 null);

 

            StreamReader sr = newStreamReader(new MemoryStream(response));

            Dictionary<String, IJsonValue> dict = new Dictionary<string, IJsonValue>();

            HttpUtility.ParseJson(JsonObject.Parse(Encoding.UTF8.GetString(response, 0, response.Length)), dict);

 

            // the corporate STS url is in the AuthURL element of the response body

            Uri corpAdfsProxyUrl = dict.ContainsKey(AuthURL) ? new Uri(dict[AuthURL].GetString()) : null;

 

            return corpAdfsProxyUrl;

        }

 

·        The App then sends a security token request via HTTP GET to the corporate ADFS integrate authentication endpoint specifying 2 main things: the username and the realm which in this case is the O365 federation service. The url looks something like the following:

https://{contosoADFSUrl}/adfs/ls/auth/integrated/?cbcxt=&vv=&username=user@contoso.com&mkt=&lc=&wa=wsignin1.0&wtrealm=urn:federation:MicrosoftOnline

 

If the user is connected to the corporate network the service request will automatically get authenticated by the magic of Kerberos and we will get a signed SAML token back in the body of the response that asserts who the user is. The token will be in the form of a SAML assertion (refer to the WS-Trust standard for more details). This is our logon token.

 

         HttpClientHandler handler = new HttpClientHandler();
            handler.UseDefaultCredentials = true; // use the default credentials so Kerberos can take care of authenticating our request

 

            byte[] stsresponse = await HttpUtility.SendHttpRequest(
                this.adfsIntegratedAuthUrl,
                HttpMethod.Get,
                null,
                text/html; charset=utf-8″,
                handler);

 

// the security token response we got from ADFS is in the wresult input element 
                var wresult = from e in form.FirstOrDefault().Descendants()
                              where e.Name == XName.Get(“input”) &&
                              e.Attribute(XName.Get(“name”)) != null &&
                              e.Attribute(XName.Get(“name”)).Value == wresult
                              select e;

 

 

// the logon token is in the SAML assertion element
                    XDocument xDoc1 = XDocument.Parse(wresult.FirstOrDefault().Attribute(XName.Get(“value”)).Value, LoadOptions.PreserveWhitespace);
                    var assertion = from e in xDoc1.Descendants()
                                    where e.Name == XName.Get(“Assertion”, saml)
                                    select e;

 

 

The App now issues the security token request to the MSO STS. However, this time instead of appending the user’s O365 credentials to the SOAP message’s security header (as in the previous post) it appends the logon token we got from the corporate ADFS. MSO STS checks the validity of the token and if everything is happy it returns a service token which we can then use to sign into SPO and get the federated authentication cookies (just like in the previous post).

 

// generate the WS-Trust security token request SOAP message passing in the logon token we got from the corporate ADFS

// and the site we want access to

 saml11RTBytes = Encoding.UTF8.GetBytes(ParameterizeSoapRequestTokenMsgWithAssertion(

                      this.spSiteUrl.ToString(),

                      logonToken,
                      msoStsUrl));

 // make the post request to MSO STS with the WS-Trust payload
                byte[] response = await HttpUtility.SendHttpRequest(
                    new Uri(msoStsUrl),
                    HttpMethod.Post,
                    new MemoryStream(saml11RTBytes),
                    application/soap+xml; charset=utf-8″,
                    null);

 

                StreamReader sr = new StreamReader(new MemoryStream(response));

// the SAML security token is in the BinarySecurityToken element of the message body
                XDocument xDoc = XDocument.Parse(sr.ReadToEnd());
                var binaryST = from e in xDoc.Descendants()
                               where e.Name == XName.Get(BinarySecurityToken, wsse)
                               select e;

 

So as you notice the main difference from the previous code is we are now presenting the user’s logon token we got from the corporate ADFS to MSO STS as a proof/claim of who the user is. In this case, MSO STS is what is called a “relying party” in the claims/brokered authentication lingo and the corporate ADFS is what is called an “identity provider”. This basically means that MSO STS trusts the corporate ADFS has done its job authenticating the user and as long as the token signature is valid (hasn’t been tampered with) MSO STS will accept it as a proof of the user’s identity. Before all this code runs the trust relationship between MSO STS and the corporate ADFS should have been already established as outlined in the O365 SSO configuration references at the beginning of this post.

 

And that’s it for integrated Windows Authentication! Obviously, this is the preferred method as we never have to ask users for their credentials as long as they are logged into a machine joined to their domain and are connected to the corporate network.  

 

2.       SSO via user’s corporate credentials

 

 

 

Most steps happen the same way as in the integrated with auth approach except that in this case we ask users for their corporate credentials to get the logon token. This normally happens when users are not connected to the corporate network. This approach requires, as mentioned previously, an ADFS proxy service deployed following O365 SSO recommended guidelines.

 

The code assumes the ADFS proxy usernamemixed endpoint is available. The App asks for the user’s corpnet credentials and constructs a request security token message (following the WS-Trust standard). It sends the request to the usernamemixed endpoint and if the credentials are valid ADFS responds with the logon token. The logon token is then presented to MSO STS just as in the integrated win auth approach.

 

private async Task<string> GetAdfsSAMLTokenUsernamePassword()

        {

            // makes a seurity token request to the corporate ADFS proxy usernamemixed endpoint using
            // the user’s corporate credentials. The logon token is used to talk to MSO STS to get
            // an O365 service token that can then be used to sign into SPO.

            string samlAssertion = null; 

            // the corporate ADFS proxy endpoint that issues SAML seurity tokens given username/password credentials 
            string stsUsernameMixedUrl = String.Format(“https://{0}/adfs/services/trust/2005/usernamemixed/”, adfsAuthUrl.Host);

             // generate the WS-Trust security token request SOAP message passing in the user’s corporate credentials 
            // and the site we want access to. We send the token request to the corporate ADFS proxy usernamemixed endpoint.
            byte[] requestBody = Encoding.UTF8.GetBytes(ParameterizeSoapRequestTokenMsgWithUsernamePassword(
                urn:federation:MicrosoftOnline, // we are requesting a logon token to talk to the Microsoft Federation Gateway
                this.username,
                this.password,
                stsUsernameMixedUrl));

 

            try
            {

                byte[] response = await HttpUtility.SendHttpRequest(
                    new Uri(stsUsernameMixedUrl),
                    HttpMethod.Post,
                    new MemoryStream(requestBody),
                    application/soap+xml; charset=utf-8″,
                    null);

 

                // the logon token is in the SAML assertion element of the message body
                XDocument xDoc = XDocument.Parse(Encoding.UTF8.GetString(response, 0, response.Length), LoadOptions.PreserveWhitespace);
                var assertion = from e in xDoc.Descendants()
                                where e.Name == XName.Get(“Assertion”, saml)
                                select e;

 

 

And with that we have come to the end of this post. I hope you found it useful. Oh I almost forgot :)! You can download the sample code here.

 

Happy coding.

 

 

Legal Disclaimer:

Everything written in this blog represents my own views only and not those of my employer. Also, any code is provided “AS IS” without warranty of any kind either expressed or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.

 

The new OneNote API

$
0
0

Hi guys,

It’s been a while since the last post. A bunch of things happened in the last year and a half. I moved from the SharePoint to the OneNote team and I’ve been doing consumer stuff for a while now. More concretely I’m working on the OneNote API. We released some great stuff around March this year and we are constantly adding new features to our REST endpoint. What I like about the OneNote API Team is we have this startup mentality where we are 100% committed to delivering customer value and rapidly adjusting based on customer feedback. We have a Beta endpoint where we are constantly releasing new functionality for you to try out in your apps. Our latest addition to our Beta endpoint is the ability to query pages across a user’s notebooks (including notebooks shared with the user) using OData. Check it out and tell us what you think. 

Happy coding,
-Omar



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>