I'm trying to implement a provider hosted app with the purpose of adding list items to sharepoint online sites from a scheduled job (console app) or windows service. So I've created a provider hosted app in Visual studio 2012, and managed to get necessary tokens and call SharePoint from the app web's start page. What I'm struggling With is using token helper from a console app, I get 400 Bad Request. I haven't found any info out there that suggests how refresh tokens and app only tokens can be combined, and especially no examples on no-web scenarioes.
This is what I've tried:
App Manifest
- Checked Allow the app to make app-only Calls to SharePoint
- Added Site Collection Scope With FullControl Permission
Default.aspx.cs Page_Load in MyAppWeb:
string contextTokenString = TokenHelper.GetContextTokenFromRequest(Page.Request);
SharePointContextToken contextToken = TokenHelper.ReadAndValidateContextToken(contextTokenString, Request.Url.Authority);
string refreshToken = contextToken.RefreshToken; // persisted to be used in console app
string realm = contextToken.Realm; // persisted to be used in console app
string targetPrincipalName = contextToken.TargetPrincipalName; // persisted to be used in console app
// All of the remaining code works perfectly
OAuth2AccessTokenResponse r1 = TokenHelper.GetAccessToken(refreshToken, targetPrincipalName, targetHost, realm);
OAuth2AccessTokenResponse r2 = TokenHelper.GetAppOnlyAccessToken(targetPrincipalName, targetHost, realm);
using (ClientContext clientContext = TokenHelper.GetClientContextWithAccessToken(sharepointUrl, r1.AccessToken))
{
clientContext.Load(clientContext.Web, web => web.Title);
clientContext.ExecuteQuery();
Response.Write("<p>1. ------>" + clientContext.Web.Title + "</p>");
Response.Flush();
}
using (ClientContext clientContext = TokenHelper.GetClientContextWithAccessToken(sharepointUrl, r2.AccessToken))
{
clientContext.Load(clientContext.Web, web => web.Title);
clientContext.ExecuteQuery();
Response.Write("<p>2. ------>" + clientContext.Web.Title + "</p>");
Response.Flush();
}
Program.cs in MyAppService:
string sharepointUrl = "https://mytenant.sharepoint.com";
string refreshToken = "the refresh token persisted by Default.aspx";
string realm = "the realm persisted by Default.aspx";
string targetPrincipalName = "the Office 365 guid persisted by Default.aspx";
string targetHost = new Uri(sharepointUrl).Authority;
// Both of the following two lines fail with 400: bad request
string accessToken = TokenHelper.GetAccessToken(refreshToken, targetPrincipalName, targetHost, realm).AccessToken;
string accessToken = TokenHelper.GetAppOnlyAccessToken(targetPrincipalName, targetHost, realm).AccessToken;
I was able to use the following code from Program.cs with an access token obtained from Default.aspx, but that expired after 12 hours.
string accessToken = "Access token persisted by Default.aspx";
using (ClientContext clientContext = TokenHelper.GetClientContextWithAccessToken(sharepointUrl, accessToken))
{
clientContext.Load(clientContext.Web, web => web.Title);
clientContext.ExecuteQuery();
string title = clientContext.Web.Title;
Console.WriteLine("------> " + title);
}
I must also confess that I have sligthly modified the TokenHelper class I copied from the AppWeb project to the console project to support reading ClientId and ClientSecret from App.config instead of Web.Config. Basically I changed the code to use ConfigurationManager instead of WebConfigurationManager.