An app for Office often accesses external resources such as search results, blog posts, and videos. To do that, authorization with third-party web-services is required. In this post we share our experience on using the OAuth2.0 authorization protocol to access Sina Weibo (microblog) resources. In particular, OAuth2.0 uses an access token to authorize users for resource access so we describe how to get, store, and remove an OAuth2.0 access token for the Sina Weibo service in the context of apps for Office. We also describe how to trust the Sina domain to run the app smoothly.

The primary goal of an OAuth2.0 access token is to authorize users for resource access. As defined in IETF RFC 674 OAuth2.0 Authorization Framework, “Access tokens are credentials used to access protected resources. An access token is a string representing an authorization issued to the client. The string is usually opaque to the client.” The OAuth 2.0 Authorization Framework: Bearer Token Usage describes how to use tokens in HTTP requests to access protected resources.

In the context of the Sina Weibo service, tokens are issued by the Sina Authorization Server to the Sina app for Office. The app uses an access token to access Sina Weibo protected resources.

Get an access token

For web applications, OAuth2.0 supports two client profiles: Server-side web application and Client-side application running in a web browser. Sina JavaScript library supports Client-side application running in a web browser. Considering that an app for Office needs to be deployed on a secure (HTTPS) server, we decide to use the Server-side web application to authorize with Sina Weibo service.

Sina app authorization flow

Figure 1 shows the server-side web application authorization flow and how to get an access token from the Sina Authorization Server.

Figure 1. Server-side web application authentication flow
Figure 1. Server-side web application authentication flow
  1. A. (Steps A1-A3) When a user chooses the sign-in (登录) button shown in Figure 2, the Azure server sends the app a redirect header set which points to the Sina authentication page. The app then redirects to the Sina authorization page shown in Figure 2 that is backed up by the Sina Authorization Server.
    For example, the code below shows how step A2 is accomplished. Method BuildAuthorizeUri generates the Sina page callback URI. Here, client_id is used by Sina library to mark applications like the Sina Weibo app for Office, and the value appkey from Sina is included.
    public void ProcessRequest(HttpContext context)
    {
        string authorizeUri = BuildAuthorizeUri();
        context.Response.Redirect(authorizeUri, false);
    }
    
    private Uri BuildAuthorizeUri()
    {
        Uri baseUri = new Uri(Constants.ApiSinaAuthorize);
        Dictionary<string, string> config = new Dictionary<string, string>()
            {
                {"client_id", WebConfigurationManager.AppSettings["appkey"]},
                {"redirect_uri", UrlMap.AuthorizeCallbackForSina},
                {"response_type", "code"},
                {"display", "client"},
            };
    
        UriBuilder builder = new UriBuilder(baseUri);
        builder.Query = RequestBuilder.MakeQueryString(config);
        return builder.Uri;
    }
  2. The user enters his/her username and password in the Sina authorization page; chooses the red Authorization button (授权) shown in Figure 2. The authorization request is sent to the Sina Authorization Server.
  3. The Sina Authorization Server responses with authorization code to the Sina app. The app is redirected to the callback page registered on Sina.
  4. The Azure server sends the code to Sina Authorization Server to get the access token.
    public OAuthResult OAuth2FetchAccessToken(string code)
    {
        Uri baseuri = new Uri(Constants.ApiGetAccessToken);
        Dictionary<string, string> config = new Dictionary<string, string>()
        {
            {"client_id", WebConfigurationManager.AppSettings["appkey"]},
            {"client_secret", WebConfigurationManager.AppSettings["appsecret"]},
            {"redirect_uri", UrlMap.AuthorizeCallbackForSina},
            {"grant_type", "authorization_code"},
            {"scope", "all"},
            {"code", code},
        };
                
        HttpWebRequest request = RequestBuilder.Create(uri, requestType, config); 
        HttpWebResponse response = request.GetResponse() as HttpWebResponse;
        string content = ReadResponseContent(response);
        response.Close();
    
        return JSONHelper.Deserialize<OAuthResult>(content);
    }
  5. The Sina Authorization Server sends the access code to the Azure Server.
Figure 2. Sina Weibo authentication pageclip_image006
Figure 2. Sina Weibo authentication page

Store and remove an access token

Each time a user tries to visit our Azure server, we get the user access token from the Web browser cookie, calculate its SHA-512 hash, and then check the hash from the Azure distributed cache to determine whether this is an authorized request.

SHA512 sha = new SHA512Managed();
byte[] input = Convert.FromBase64String(content);
byte[] output = sha.ComputeHash(input);
string hashed = BitConverter.ToString(output);

Store an access token

The subsection describes how to store signature instead of the access token in the hosting Azure server, and a pattern that helps securely store the access token in Web browser cookies.

Caution: To avoid leaking the user access token in case our Azure server is compromised, we store the signature using the SHA-512 algorithm in the Azure distributed cache.

Note: SHA-512 is a hash algorithm that generates a digital signature. OAuth2.0 uses the digital signature instead of the full credential with each request.

The access token are stored in Web browser cookies. In the code below, the cookies are http-only and we require a secure (HTTPS) connection to access them. Using HTTP means that we can’t get the cookies using JavaScript code directly. Because we use secure (HTTPS) communication, it is safe to transfer access tokens over the Internet.

<system.web>
  <httpCookies 
    httpOnlyCookies="true" 
    requireSSL="true" />
<system.web>

Remove an access token

As shown in the code below, we set the hash expiring time in the Azure distributed cache as the access token expiring time. We also set the cookie expiring time as the access token expiring time. This way, the cookie and the hash expire automatically and therefore we can control the user sign-in state by setting or clearing user cookies.

OAuthResult result = OAuth2FetchAccessToken(code);
CookieUtils.SetCookie(context.Response.Cookies, Constants.AccessToken,
  result.access_token, result.expires_in);
CacheStore.AddTokenHash(tokenHash, TimeSpan.FromSeconds(result.expires_in));

Better performance for storing token hash in Azure distributed catch

For better performance, the Sina app is deployed in three Windows Azure instances behind the load balancer. With the token hash stored in the Azure distributed cache, data synchronization between different Azure instances isn’t needed as all instances can access the same distributed cache.

Trust the Sina domain

As stated in the blog post Use OAuth 1.0 to access the Zotero web service from apps for Office, one more step is needed to verify that Microsoft Word trusts the Sina domain. In the Sina Weibo app, open the manifest XML file and insert the following code.

<AppDomains>
  <AppDomain>https://api.weibo.com</AppDomain>
</AppDomains>

That line instructs the task pane that we trust Sina, and redirects can be opened in the task pane and not in a new browser. If the code isn’t added, Sina will try to open in a new browser instead of in the same task pane, making the Sina Weibo app unusable.

Note: Before we trust a web site, we verify that files we download or that we run from the web site will not damage our computer or data.

References

Attribute

This blog post was written by ecoSystem team SDE Huaqing Liu and content developer Tony Liu. Program manager Yina Arenas and content developer Ricardo Loo Foronda provided valuable feedback.