PnP Sample Core.CloudService problem

MVP

I'm trying to follow the instructions on https://github.com/OfficeDev/PnP/tree/master/Samples/Core.CloudServices in order to set up a web service than can talk to an Office 365 tenant.

 

I'm trying to follow the instructions there, but I am using Visual Studio 2015 and the instructions seem to be only partially updated for 2015.  Either that or I don't have a Visual Studio component set up correctly.  Specifically, I get to the step where it says to click on the "Convert to App for SharePoint Project" sub-menu item of the "Convert" menu item  in the context menu on the web role project.  However, I don't have a "Convert" menu item. 

 

Is this something that changed in VS2015 or am I missing a VS2015 component?  If it's changed in VS2015, what's the new way to do this?   @Vesa Juvonen, you made the last updates to the Readme 17 days ago. Can you please tell me what I am doing wrong?

Thanks!

15 Replies

This is an old sample created back in the days when Azure Web Apps and Web Jobs were not yet well developed and broadly used. Nowadays I would recommend using Azure Web Apps instead of a web role and Azure Web Jobs instead of worker roles...when you do there's no additional plumbing you need to do to make it work with Azure.

Like Bert mentioned, we do recommend using WebJobs or WebAPIs / web services to talk to Office 365 nowadays. You do not need to go through this level of setup due these enhanced capabilities in Azure. If you are just trying to have a web service, whcih talks to Azure, you can simply implement it for example as a WebAPI, whcih then uses app-only permissions to gain access to SharePoint.

 

Happy to provided additional details, if you specificy the needed scenario. 

OK, now I have a WebApi Azure web app, and I have registered it in Azure AD. I have a client ID and client secret. I plan to use app-only authentication, not user authentication. I want to follow the process described in https://graph.microsoft.io/en-us/docs/authorization/app_only. I assume I am not the first person to do this. Is there sample code in C# that implements the process described in that article? My webservice is being called with a parameter that is the ID of an email message. I want to use the Graph API for emails to get the subject and attachment from the message whose ID was passed in.

You can for example follow the guidance from PnP Partner Pack (starter kit for partners) at https://github.com/OfficeDev/PnP-Partner-Pack/blob/master/Documentation/Manual-Setup-Guide.md for enabling app-only with Azure. You'll need to do few steps, but the PnP Partner Pack documentation is great step-by-step process on doing required steps.

Vesa,

Thank you.  I followed the Partner Pack instructions at https://github.com/OfficeDev/PnP-Partner-Pack/blob/master/Documentation/Manual-Setup-Guide.md.  There were a few things I ran into:

  1. The documentation uses screen shots and instructions for the old azure portal.  I had to use manage.windowsazure.com to upload the certificate and set the app setting.  I couldn't figure out where to do that in portal.azure.com.  It would be great if the docs could be updated to reflect using the current Azure Portal.
  2. When I needed to add the Key Credentials to the manifest json file, I got an error, as described here: https://social.msdn.microsoft.com/Forums/azure/en-US/7c8bc608-7e9e-4075-9a18-1982ad4679c4/unable-to-... . As you can see, my fix was to upload a version of the manifest where I had the keyCredentials cleared out, then upload one where it was set.
  3. Also, the instructions in the Partner Pack contain the sentence:"There, you will have to configure a setting called WEBSITE_LOAD_CERTIFICATES with a value of ***. "  Given the associated picture, I believe the sentence should end with quote-star-quote ("*") instead of star-star-star (***).

Hope that helps other people.

Now I still have to write the code that gets the token and uses it to talks to the O365 APIs that I am interested in.

 


@Vesa Juvonen wrote:

You can for example follow the guidance from PnP Partner Pack (starter kit for partners) at https://github.com/OfficeDev/PnP-Partner-Pack/blob/master/Documentation/Manual-Setup-Guide.md for enabling app-only with Azure. You'll need to do few steps, but the PnP Partner Pack documentation is great step-by-step process on doing required steps.


 

Hi Michael,

 

If your main interest is understanding how to authenticate using "app-only" with Azure AD then I would recommend checking this web cast: https://channel9.msdn.com/blogs/OfficeDevPnP/PnP-Web-Cast-Introduction-to-Authentication-Manager-in-....

 

The PnP core library has classes that make it easy to do what you want.

Bert,

Thank you! That video helped a lot.  After watching that, I figured out that once I added the SharePointPnPCoreOnline nuget package to my web service, I could use AuthenticationManager.GetAzureADAppOnlyAuthenticatedContext(...) to get a ClientContext.

 

A question about that method.  I uploaded the certificate to the web service by adding it to the SSL section of its configuration web page in Azure (in the new portal). To reference that certificate, since I don't know what the file path is, I can reference it from the certificate store, right? So my call looks like GetAzureADAppOnlyAuthenticatedContext(
urlToTeamSite, ClientID,
"mytenant.onmicrosoft.com", StoreName.My, StoreLocation.LocalMachine, PfxThumb);.  Is the Certificate store name and location the correct enum values?

 

Also, is the siteURL, the URL of a specific team site, or the site collection's root site? In other words, is it https://tenant.sharepoint.com/sites/sitecollectionrootsite, or can it be https://tenant.sharepoint.com/sites/sitecollectionrootsite/subsiteX/subsiteY?

 

Thanks,

Michael

I've got another question about using App-only authentication with Azure AD.  Using GetAzureADAppOnlyAuthenticatedContext() is great for getting a ClientContext, but a ClientContext only helps me with using SharePoint CSOM.  In the particular web service I am writing, it recieves the messageID of an email that is in Exchange Online.  I want to get the subject and attachment from that email message (and then store it in SharePoint).  I believe I can get the email details and content using the Graph REST API, but I need to send a bearer token in the Authorization header in the HTTP request when I do that.  Can the AuthenticationManager help me with that?  If not, what's the right way to get that?  Is lines 43-48 of https://github.com/richdizz/MyO365BackgroundProcess/blob/master/MyO365BackgroundProcess/Program.cs a good example to follow?

 

Thanks,

Michael

Hi Michael,

 

You create a clientcontext for the site you need, if that's a sub site you want to work against then you should create the clientcontext using the sub site url.

 

Regarding the certificate: it's up your implementation to handle this, storing it in the local machine certificate store is definitely a valid option.

 

 

Hi Michael,

 

Once you've a clientcontext object you can always grab the bearer token using the ExecutingWebRequest handler on the ClientContext object

 

 

        private void Cc_ExecutingWebRequest(object sender, WebRequestEventArgs e)
        {
            // Capture the OAuth access token since we want to reuse that one in our REST requests
            this.accessToken = e.WebRequestExecutor.RequestHeaders.Get("Authorization").Replace("Bearer ", "");
        }

 

@Bert Jansen,

Here's my code to create the ClientContext:

ClientContext cc = AM.GetAzureADAppOnlyAuthenticatedContext(
                    "http://psclistens1.sharepoint.com/sites/something/n2s/MailTest", ClientID,
                    "psclistens1.onmicrosoft.com", StoreName.My, StoreLocation.LocalMachine, PfxThumb);

When it runs, I get the error:

"Could not load type 'Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate' from assembly 'Microsoft.IdentityModel.Clients.ActiveDirectory, Version=3.13.4.878, Culture=neutral, PublicKeyToken=31bf3856ad364e35'."

 

Any suggestions on troubleshooting this? I know my team site url is correct.  The Client ID matches what is registered in AAD.  Should the tenant ID be something.onmicrosoft.com or something.sharepoint.com or a GUID? The thumbprint matches what is listed under SSL in the App Service in Azure. 

 

Thanks,

Michael

Correction to my source code above - the site URL should start with HTTPS.

Realizing that I am running this locally initially, I did try the the other method signature:

 

ClientContext cc = AM.GetAzureADAppOnlyAuthenticatedContext(spWebUrl, ClientID, O365Tenant,
                @"C:\Users\mblumenthal\patha\pathb\etc...\Certificate\PSCGroupLLC.pfx",
                certPassword);

I get the same error.

 

Can you try to downgrade the Microsoft.IdentityModel.Clients.ActiveDirectory package to a version 2.x version? Assuming your using the latest version 3.x right now...if not then we'll need to search further.

That's correct, I have v 3.13.4 of Microsoft.IdentityModel.Clients.ActiveDirectory currently. I will change it over to v2.28.1, since that it the newest of the 2.x choices.

Downgrading that Nuget package to the highest 2.x patch level, v2.28.1, worked. Now I can get a client context, use it to get the title property of a SPO web, and grab the auth token from the client context.  I use that auth token to create a graphClient. However, when try to use the graphClient, I get a Microsoft.Graph.ServiceException.  I'm struggling with how to troubleshoot that.

 

if (this.accessToken != string.Empty)
            {
                GraphServiceClient graphClient = new GraphServiceClient(
                            "https://graph.microsoft.com/v1.0",
                            new DelegateAuthenticationProvider(
                           async (requestMessage) =>
                                {
                                    requestMessage.Headers.Authorization =
                                    new AuthenticationHeaderValue("bearer", accessToken);
                                }));
                var currentgraphObject = await graphClient.Organization.Request().GetAsync();
                string orgName = currentgraphObject.ToString();

                if (orgName != null)
                {
                    Debug.WriteLine("Got org: " + orgName);
                }

                
            }

In the code above, it fails on the line:

 

 

var currentgraphObject = await graphClient.Organization.Request().GetAsync();

I've also tried the following line instead resulting in the same error.

var mailbox = await graphClient.Users["SpecificAccount@psclistens.com"].Request().GetAsync();

Any advice on troubleshooting this would be appreciated.

Thanks,

Michael