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?
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.
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).
OAuth is an authentication protocol that allows you to approve one application interacting with another on your behalf without giving away your password.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With