Search code examples
macosxamarin.macvisual-studio-mac

Login to Microsoft Graph API from Mac (Xamarin)


I'm trying to connect my Mac App (written in c# / Visual Studio for Mac) to the Graph API from Microsoft.

On Windows, I can use the ADAL and perform an operation like:

        PublicClientApplication myApp = new PublicClientApplication(MYID);
        string Scope = "User.ReadBasic.All User.Read";
        var result = await myApp.AcquireTokenAsync(Scope.Split(' '));
        return result.AccessToken;

While the same approach on Mac results in a "not implemented" exception.

I tried several ways like ADFSApi and MicrosoftLiveConnectApi from SimpleAuth but with no luck.

Does anybody know how to perform this Auth? On the other hand, I find very often guides that uses the browser... How can I read the response from the browser from App with the Code to obtain the token?

------------- UPDATE ---------------------

After hours of work, I implemented an alternative flow. Basically when the login button is pressed, I perform a Segue to a Window with only the WebView in it.

public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        var webView = new WebKit.WebView(this.MainView.Frame, "officeWebView", "noName");

        webView.MainFrameUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?state=ltxzlgtghhxgpwuh&client_id=[MYID]&response_type=code&scope=User.ReadBasic.All%20User.Read&redirect_uri=msal[MYID]://auth";
        webView.FinishedLoad += (sender2, e) =>
        {
            var url = ((WebKit.WebView)sender2).MainFrameUrl;
        };

        this.MainView.AddSubview(webView);
    }

At the end of login, the system prompt me a message like "There is no App to open MSAL[MYID] etc. So I add the "bind" in the info.plist

enter image description here

Where actually the URL Scheme is MSAL[MYID].

I implemented the OpenUrls method in the AppDelegate.cs

 public override void OpenUrls(NSApplication application, NSUrl[] urls)
    {
        Console.WriteLine("TEST!!!");
        foreach(var url in urls)
            Console.WriteLine(url);

        base.OpenUrls(application, urls);
    }

But it seems that the OpenUrls is NEVER triggered (never printed a single line, never hit a breakpoint).

What's wrong? And, if I successfully made it this way, how can I move to IMPLICIT grant in order to keep my app-secret secure?


Solution

  • I don't know the exact details for your situation. But you can use a WebKit.WebView

    In my case I use facebook login through my own website and pass the facebook token back through a redirect url.

    Assign FrameLoadDelegate

    webView.FrameLoadDelegate = this;
    webView.MainFrameUrl = starturlhere;
    

    Implement webView:didFinishLoadForFrame:

        [Export ("webView:didFinishLoadForFrame:")]
        public void FinishedLoad (WebKit.WebView sender, WebKit.WebFrame forFrame)
        {
            // page loaded
            System.Diagnostics.Debug.WriteLine("FinishedLoad: "+sender.MainFrameUrl);
    
            var url = new NSUrl(sender.MainFrameUrl);
            var document = sender.MainFrameDocument;
    
            // check if this is the login result 
            // if not do nothing and return (login flow not done)
        }
    

    Extract the required data from either the url or the loaded page

    // UPDATE

    IF not logging in through your own server. Set a redirect url for https://127.0.0.1 (or anything really) and intercept the redirect with one of these (check for the correct url):

            webView.ReceivedServerRedirectForProvisionalLoad += (object sender, WebFrameEventArgs e) => 
            {
                //e.ForFrame.ProvisionalDataSource.Request.Url;
            };
    
            webView.OnSendRequest += (WebView sender, NSObject identifier, NSUrlRequest request, NSUrlResponse redirectResponse, WebDataSource dataSource) => 
            {
                //request.Url;
            };