Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SharePoint online: can a Windows Client application use OAuth for authentication?

We build a Windows client application for SharePoint online with SharePoint Client Object Model. We want to use OAuth to authentication for this Windows Client application, but we didn't find a way to do it; and the docs on MSDN is ambiguous.

This article gives an example, however, when I create the new application with the link https://<TENANT>.sharepoint.com/_layouts/appregnew.aspx, the option of "An app running on a client machine" is disabled, is there a setting in SharePoint online site to enable this?

like image 243
Matt Avatar asked Nov 11 '13 16:11

Matt


People also ask

Why OAuth Cannot be used for authentication?

Let's start with the biggest reason why OAuth isn't authentication: access tokens are not intended for the client application. When an authorization server issues an access token, the intended audience is the protected resource. After all, this is what the token is providing access to.

How does OAuth work in SharePoint?

In SharePoint, the OAuth authentication and authorization flow for a provider-hosted, low-trust, add-in involves a series of interactions among your add-in, SharePoint, the authorization server, and the browser at runtime. The authorization server in this scenario is Microsoft Azure Access Control Service (ACS).

Can OAuth be used for authentication?

OAuth is an authentication protocol that allows you to approve one application interacting with another on your behalf without giving away your password.


1 Answers

I got this working after a lot of trying

I guess it's not the most wonderfull code but here it is:

/// <summary>
/// Sets needed values
/// </summary>
/// <param name="clientId">The ClientId from the application</param>
/// <param name="redirectUri">The RedirectUri where the browser has to be send.</param>
/// <param name="resource">The source you want to access</param>
public OneDriveConnection(string clientId, string clientSecret, string redirectUri, string resource)
{
    this._clientId = clientId;
    this._redirectUri = Uri.EscapeDataString(redirectUri);
    this._resource = Uri.EscapeDataString(resource);
    this._clientSecret = clientSecret;
}

Next I create a browser where the user is prompted to log in:

/// <summary>
/// Authorizes the application
/// </summary>
public void Authorize()
{
    /* EXAMPLE: GET https://login.windows.net/common/oauth2/authorize
        * ?response_type=code
        * &client_id=acb81092-056e-41d6-a553-36c5bd1d4a72
        * &redirect_uri=https://mycoolwebapp.azurewebsites.net
        * &resource=https:%2f%2foutlook.office365.com%2f
        * &state=5fdfd60b-8457-4536-b20f-fcb658d19458 */

    string baseUri = "https://login.windows.net/common/oauth2/authorize";
    string authorizationUri = string.Format(baseUri
        + "?response_type=code"
        + "&client_id={0}"
        + "&redirect_uri={1}"
        + "&resource={2}"
        + "&state={3}", this._clientId, this._redirectUri, this._resource, "5fdfd60b-8457-4536-b20f-fcb658d19458");

    // Create the form
    Form webBrowserForm = new Form();
    webBrowserForm.MaximizeBox = false;
    webBrowserForm.MinimizeBox = false;
    webBrowserForm.Size = new System.Drawing.Size(580, 890);
    webBrowserForm.Text = "Webbrowser";
    webBrowserForm.FormBorderStyle = FormBorderStyle.FixedDialog;
    webBrowserForm.StartPosition = FormStartPosition.CenterScreen;

    // Create the WebBrowser
    WebBrowser webBrowser = new WebBrowser();
    webBrowser.Width = 580;
    webBrowser.Height = 890;
    webBrowser.Location = new System.Drawing.Point(0, 0);
    webBrowser.ShowPageSetupDialog();

    // Hook event to the webBrowser
    webBrowser.Navigated += webBrowser_Navigated;

    // Show the webBrowser and form to the user
    webBrowserForm.Controls.Add(webBrowser);
    webBrowserForm.Show();

    // Navigate to the authorizationUri
    webBrowser.Navigate(authorizationUri);
}

Here I check if there is a code to execute the GetTokenInformation method:

/// <summary>
/// When the url has code in it and contains a session_state get the code and do the GetTokenInformation
/// </summary>
private void webBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
    if (e.Url.AbsoluteUri.Contains("code=") && e.Url.AbsoluteUri.Contains("session_state"))
    {
        string[] splited = e.Url.AbsoluteUri.Split(new char[] { '=', '&' });
        _code = splited[1];

        if (!string.IsNullOrWhiteSpace(_code)
            && !string.IsNullOrWhiteSpace(_redirectUri)
            && !string.IsNullOrWhiteSpace(_clientId))
        {
            GetTokenInformation(_code, _redirectUri, _clientId, _clientSecret);
        }
        else
        {
            _connected = false;
        }
    }
}

In the GetTokenInformation method I get the TokenInformation which I put in a TokenInformation class using the Newtonsoft.Json dll

/// <summary>
/// This method gets tokeninformation: access_token, token_type, expires_in, resource, refresh_token, scope, id_token
/// </summary>
/// <param name="code">Code from the authorize request</param>
/// <param name="redirectUri">Reply url for your application</param>
/// <param name="clientId">Your applications client id in Windows Azure Directory</param>
/// <param name="clientSecret">Your applications client secret</param>
private void GetTokenInformation(string code, string redirectUri, string clientId, string clientSecret)
{
    // Get the token information that is set above in the constructor with the help of the clientId, clientSecret and code and as well as the redirectUri without it you can't connect to it otherwise it will crash if you don't do it like that
    string baseUri = "https://login.windows.net/common/oauth2/token";
    string parameters = string.Format("grant_type=authorization_code"
        + "&code={0}"
        + "&redirect_uri={1}"
        + "&client_id={2}"
        + "&client_secret={3}", code, redirectUri, clientId, clientSecret);
    string response = HttpPost(baseUri, parameters);

    if (!string.IsNullOrWhiteSpace(response))
    {
        _tokenInformation = JsonConvert.DeserializeObject<TokenInformation>(response);
        _connected = true;
    }
    else
    {
        _connected = false;
    }
}

And here is my TokenInformation class using the Newtonsoft.Json dll:

[JsonObject(MemberSerialization.OptIn)]
class TokenInformation
{
    [JsonProperty(PropertyName = "access_token")]
    public string AccessToken { get; set; }

    [JsonProperty(PropertyName = "token_type")]
    public string TokenType { get; set; }

    [JsonProperty(PropertyName = "expires_in")]
    public int ExpiresIn { get; set; }

    [JsonProperty(PropertyName = "expires_on")]
    public int ExpiresOn { get; set; }

    [JsonProperty(PropertyName = "resource")]
    public string Resource { get; set; }

    [JsonProperty(PropertyName = "refresh_token")]
    public string RefreshToken { get; set; }

    [JsonProperty(PropertyName = "scope")]
    public string Scope { get; set; }

    [JsonProperty(PropertyName = "id_token")]
    public string IdToken { get; set; }
}

This is where I found the requests which I needed to make to connect to SharePoint/Office365: link

like image 59
Brampage Avatar answered Sep 25 '22 16:09

Brampage