Working with SharePoint Workflow inside Outlook add-in – Part 2

This is second in a two part series where I will show you how we can work with a SharePoint Approval workflow within the familiar environment of Outlook. You can read the first part here.

Last week we saw how we can retrieve entire email content in Read Mode of Outlook add-in using Exchange Web Services (EWS). This post talks about how we can integrate that information with SharePoint Online to work on workflows from inside Outlook environment.

The first step in the process is to create an application in the Azure AD instance linked to your SharePoint Online tenant. (Fyi, Microsoft provides a free trial of Azure for developers.)

We will navigate to our Active Directory inside Azure Management Portal and navigate to Applications tab. From there we click on “Add" button and select “Add an application my organization is developing”. Give it a name and select “Web Application” from the choices and hit next. Provide a Sign-On URL and App ID URI. The sign on URL in our case will be where you have hosted your Outlook web application. The App ID can be anything unique. Clicking on checkbox will create our application.

image

After your application is created, it will appear in the Applications list. Click on it and navigate to Configure tab. Here we will find few properties which are important for our application. Make a note of Client ID and generate an App Key either for 1 or 2 year. Also enter a Reply URL which is the URL where the Azure AD instance will navigate to provide the Authentication Token.

We will then need to click on “Add Application” and choose “Office 365 SharePoint Online” application. We will provide the application appropriate rights which in this case would be “Read and write items in lists in all site collections”. After you are done your application configuration will look something like this.

image

Now that we are done with all the configuration (whew!) we will go back to our code. In the web.config of our Outlook web application, we will add the following appSettings.

 <add key="ida:AuthorizationUri" value="https://login.windows.net" />
<add key="ida:ClientID" value="ad039897-blablabla" />
<add key="ida:AppKey" value="supersecretappkey" />

 

The Client ID and AppKey that we have generated in previous steps need to populated here.

Next we will add code in our Home Controller to actually use this information to authenticate with Azure AD and use SharePoint Online APIs. As we will be already authenticated when we are inside OWA, the authentication process will be completely seamless to the end user.

Note: For best user experience, I suggest to keep the View of Home controller blank so that the authentication process is completely hidden from the end user.

 private readonly string _clientId = ConfigurationManager.AppSettings["ida:ClientID"];
private readonly string _appKey = ConfigurationManager.AppSettings["ida:AppKey"];
private readonly string _authorizationUri = ConfigurationManager.AppSettings["ida:AuthorizationUri"];
private const string ServiceResourceId = "https://<tenant>.sharepoint.com";
 public async Task<ActionResult> Index(string et, string code, string error, string error_description)
{
 if (error != null) return RedirectToAction("Error", new { error, error_description });

  var authContext = new AuthenticationContext(_authorizationUri + "/common", true);

   // Check if you received an authorization code, if not you need to sign in
  if (code == null)
   {
       // Get the authorization URL (https://login.windows.net/common/oauth2/authorize)
        var redirectUri = authContext.GetAuthorizationRequestURL(ServiceResourceId, _clientId, new Uri(Request.Url.AbsoluteUri.Split('?')[0]), UserIdentifier.AnyUser, string.Empty);

       return Redirect(redirectUri.ToString());
    }

   // If the code is empty, show the user the error page
   if (String.IsNullOrEmpty(code)) return RedirectToAction("Error", new { error = "AuthToken", error_description = "The authorization token was not retrieved." });

    // If a code is retrieved, the access token can be generated
    var clientCredentials = new ClientCredential(_clientId, _appKey);
   var authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(code, new Uri(Request.Url.AbsoluteUri.Split('?')[0]), clientCredentials);
   return Redirect("/AppRead/Home/Home.html?auth=" + authResult.AccessToken);
}

Note how we are navigating to the Home.html page along with the Auth Token acquired by AAD. This token is then extracted and used for subsequent calls to work with SharePoint workflows.In this example, I have hardcoded the List name and item name as this is a POC. You can improve the code to dynamically fetch these values.

 [ActionName("spQuery")]
public ApprovalApi.Models.ServiceResponse PostSPQuery(ServiceRequest request)
{
_request = request;
ApprovalApi.Models.ServiceResponse response = new Models.ServiceResponse();
ClientContext clientContext = new ClientContext("https://<tenant>.sharepoint.com/");
clientContext.AuthenticationMode = ClientAuthenticationMode.Anonymous;
clientContext.FormDigestHandlingEnabled = false;
clientContext.ExecutingWebRequest +=
  delegate(object oSender, WebRequestEventArgs webRequestEventArgs)
   {
       webRequestEventArgs.WebRequestExecutor.RequestHeaders["Authorization"] =
            "Bearer " + request.SPAuthToken;
    };
ListCollection lists = clientContext.Web.Lists;
List selectedList = clientContext.Web.Lists.GetByTitle("Tasks");
ListItem item = selectedList.GetItemById(2);
clientContext.Load<ListCollection>(lists);
clientContext.Load<List>(selectedList);
clientContext.Load(item);
clientContext.ExecuteQuery();
if (request.Approve)
{
   item["Completed"] = true;
   item["PercentComplete"] = 1;
    item["Status"] = "Approved";
    item["WorkflowOutcome"] = "Approved";
   response.isApproved = true;
}
else
{
   item["Completed"] = false;
  item["PercentComplete"] = 0;
    item["Status"] = "Rejected";
    item["WorkflowOutcome"] = "Rejected";
   response.isApproved = false;
}
item.Update();
clientContext.ExecuteQuery();
response.isError = false;
return response;
}

 

The UI of our Outlook web app will look something like this.

image

User can now click on Approve or Reject button from inside Outlook to deal with workflow tasks!

This was a quick look at how easy it is to integrate SharePoint and Office apps add-ins to improve the user experience. If you have any question or comment, please hit the comments section below.

As usual, the code is attached to this post.

ApprovalApp.zip

Comments

  • Anonymous
    August 23, 2015
    Great article - lots of potential here for extending the sample into something very useful . . . and very informative about interacting with the Office online and desktop environment. I'd love to hear more about how we might tie the data from this Outlook add-in to something server-side to track responses or connect up to a workflow, for example. Would love to see some information about how you might extend the sample. Thanks!

  • Anonymous
    August 25, 2015
    Is there a way to build and app that will use context user's credentials to work with SharePoint? As granting permissions to the app via Azure AD is compromising security, as far as I can see.

  • Anonymous
    October 09, 2017
    Hello , Thanks for sharing this information. I am trying to build a Outlook add-in similar to the one you created using VS 2017 web template. I have added some web API classes and routing class to the new add in we project . I am not sure how to add the web api as a reference in the js file . I am unable to get the web API URL . In you approval app can you tell me how did you set up your project, did you first create web API project and then add the second project.