Microsoft Graph API and Xamarin
25 March 2016

Microsoft Graph API and Xamarin

Recently i came up with the need to develop an app who could connect to Office 365 and access to some data:

  • Signed in user profile.
  • Signed in user organization contacts.
  • Signed in user relevant contacts.

You could obtain all this information easily using the Office 365 API, registering your app in the tenant AAD to get all the needed scopes/permissions needed. But, there is the trick... in this case i couldn't register the app on any AAD as the app is not going to know the user tenant. Anyone with an Office365 account should be able to use the app and we need to be able to retrieve all information without push the user to register the app or do any previous work on the Office 365/Azure server side...

There is a lot of information on internet, but is mainly directed to use the Office 365 API in a single tenant environment. Also, Office 365 APIs went throught a lot of name changes, unifications and redefinitions that made the information a bit chaotic.

To be able to use our app with any tenant we need to address two main issues:

  • Obtain a valid Client ID to authenticate our app.
  • Get an API set we can call to gather information.

All this is resolved with the new Microsoft Graph API.

Getting a valid Client ID

The first thing is resolved registering our app within the new App portal for Microsoft Graph: apps.dev.microsoft.com. You simply need to sign in to the portal using your LiveID and create a new app clicking on the "Add an app" button:

Add an app

After the app is created, you need to setup the platform your app is going to work on, mobile application in our case:

configure platform

After this, you need to pick your application client id and redirect url. The Client ID is a unique value identifying your app, but the redirect url for Mobile apps is the same for all apps and can't be changed:

  • urn:ietf:wg:oauth:2.0:oob

Now that we have the app registered, we can start working on the code of our apps.

Signing in to Office 365... without Office 365

Currently there are two versions of the Microsoft Graph API: The stable v1.0 and the beta. To being able to use REST APIs from the beta version you need to get a token from login.microsoftonline.com version 2.0.

  • The v1.0 of the login.microsoftonline.com only allows you to get a token valid for v1.0 methods of Microsoft Graph APIs.

The authentication require two steps:

  1. An Oauth page is shown to the user so could write the user/password.
  2. We need to call another service with the code from the previous step to get the final token.

To address the first step we need to build the url with the correct parameters & permissions for the use of the API. The final url could be something like this:

https://login.microsoftonline.com/common/oauth2/V2.0/authorize?client_id={YOURCLIENTID}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&scope=openid offline_access People.Read

It is composed by:

  • client_id, your app client id, the one we get in the previous step registering the app.
  • redirect_uri, the one from the previous step.
  • response_type, use "code" to get a security code you can later exchange for an access token.
  • scope, the space separated list of scopes we need our access token valid for.

Now we need to show the UI to the user in each platform. In Windows 10 UWP app is easy using the WebAuthenticationBroker component. the code we need to get is stored in the ResponseData property:

public async Task<string> AuthenticateOffice365Async()
{
    var result = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, 
                                          new Uri(LOGIN_URL, UriKind.Absolute), 
                                          new Uri(LOGIN_REDIRECT_URL, UriKind.Absolute));        
    return ExtractAuthenticationCodeFromUrl(result.ResponseData);
}
private string ExtractAuthenticationCodeFromUrl(string url)
{
    string codeString = "";
    //Extract code from the url string in ResponseData.
}

In iOS and Android, we are going to use Xamarin.Auth component. Xamarin.Auth includes several classes for distinct types of OAuth options. We are going to create a new class on top of WebRedirectAuthenticator class of Xamarin.Auth. This is due to the need of extracting the authentication code on the OnPageLoading method of WebRedirectAuthenticator class. 

iOS version:

public class CustomWebRedirectAuthenticator : WebRedirectAuthenticator
{
    private TaskCompletionSource<string> tcs;
    private UIWindow window;

    public CustomWebRedirectAuthenticator(Uri initialUri, Uri redirectUri)
        : base(initialUri, redirectUri)
    {
        tcs = new TaskCompletionSource<string>();
        window = UIApplication.SharedApplication?.KeyWindow;
        AllowCancel = true;
        ShowUIErrors = false;
    }

    public TaskCompletionSource<string> TaskCompletionSource
    {
        get { return tcs; }
        set { tcs = value; }
    }

    public void ShowUI()
    {
        window?.RootViewController?.PresentViewController(GetUI(), true, null);
    }
    public override void OnPageLoading(Uri url)
    {
        base.OnPageLoading(url);
        if (url.AbsoluteUri.StartsWith(REDIRECT_URL))
        {
            ExtractCodeFromUrl(url.AbsoluteUri.ToString());
            CloseUI();
        }
    }
    private void ExtractCodeFromUrl(string url)
    {
        string codeString = "";
        //Extract the code from the URL
        TaskCompletionSource.SetResult(codeString);
    }
    private void CloseUI()
    {
        var navController = (_window?.RootViewController?.ModalViewController as UINavigationController);
        navController?.TopViewController?.DismissModalViewController(true);
    }
}

Android version:

public class CustomWebRedirectAuthenticator : WebRedirectAuthenticator
{
    private TaskCompletionSource<string> tcs;
    private Activity activity;
    public CustomWebRedirectAuthenticator(Uri initialUri, Uri redirectUri)
        : base(initialUri, redirectUri)
    {
        tcs = new TaskCompletionSource<string>();
        activity = Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity;
        AllowCancel = true;
        ShowUIErrors = false;
    }
    public TaskCompletionSource<string> TaskCompletionSource
    {
        get { return tcs; }
        set { tcs = value; }
    }
    public void ShowUI()
    {
        activity?.StartActivity(GetUI(_mainActivity));
    }
    public override void OnPageLoading(Uri url)
    {
        base.OnPageLoading(url);
        if (url.AbsoluteUri.StartsWith(REDIRECT_URL))
        {
            ExtractCodeFromUrl(url.AbsoluteUri.ToString());
        }
    }
    private void ExtractCodeFromUrl(string url)
    {
        string codeString = "";
        //Extract the code from the url
        TaskCompletionSource.TrySetResult(codeString);
    }
}

The code is almost equal on iOS and Android, with only some changes to show or hide the view. As the WebRedirectAuthenticator doesn't work on an async fashion, we use TaskCompletitionSource to use it in async/await way so we can unify the calls to iOS/Android/Windows 10.

Now, after having our authentication code is time to get a brand new Access Token so we can actually use it to make calls to Microsoft graph on behalf of the user.

We need to make a HTTP POST to the token url:

https://login.microsoftonline.com/common/oauth2/V2.0/token

Passing some parameters in a form/url-encoded content:

  • grant_type, a fixed value "authorization_code"
  • client_id, app client id, the same one used to obtain the code.
  • code, the current code retrieved in the previous step.
  • redirect_uri, the url we get registering our app.
HttpClient client = new HttpClient();
var content = new FormUrlEncodedContent( new List<KeyValuePair<string, string>>()
{
    new KeyValuePair<string, string>("grant_type", "authorization_code"),
    new KeyValuePair<string, string>("client_id", CLIENT_ID),
    new KeyValuePair<string, string>("code", oauthCode),
    new KeyValuePair<string, string>("redirect_uri", REDIRECT_URL)
});
var result = await client.PostAsync(CommonSettings.LOGIN_TOKEN_URL, content);
if (result.IsSuccessStatusCode)
{
    string response = await result.Content.ReadAsStringAsync();
    //Deserialize the json returned by the service to get your token!
}

And here we go! now we have a valid access token to use with any of the Microsoft Graph API calls. Only remember to include the correct scopes for each call you want to use with the authorization code request.

Hope this helps you! 

Happy Coding!

Related

9.00 ( 2 reviews)

Comments

  • I like what you guys are up too. This kind of clever work and coverage! Keep up the wonderful works guys I've added you guys to our blogroll.
    2/20/2017 9:31:23 PM Reply
  • I pay a quick visit every day some websites and blogs to read articles or reviews, but this weblog presents feature based posts.
    2/20/2017 2:41:45 AM Reply
  • When I initially commented I clicked the "Notify me when new comments are added" checkbox and now each time a comment is added I get several emails with the same comment. Is there any way you can remove people from that service? Bless you!
    2/19/2017 2:02:23 PM Reply
  • I always spent my half an hour to read this web site's articles or reviews every day along with a mug of coffee.
    2/19/2017 10:58:42 AM Reply
  • I am genuinely grateful to the holder of this site who has shared this wonderful post at at this time.
    2/16/2017 10:50:33 PM Reply

Post a Comment