pnp / cli-microsoft365

Manage Microsoft 365 and SharePoint Framework projects on any platform
https://aka.ms/cli-m365
MIT License
917 stars 324 forks source link

New feature: using M365 cli access token in local dev #6253

Open jhholm opened 2 months ago

jhholm commented 2 months ago

If you are developing against SharePoint API outside of browser context, you need to create an application registration with a self signed certificate and add application permissions via Entra Admin center. See https://pnp.github.io/pnpjs/getting-started/#authentication. And in production you would actually need to do this in a proper way via Managed Identity. I see this as a tedious development experience and a hurdle to for a new developer.

If you're working with Graph API, you can actually use the token provided by Az CLI in your local development. The token provided by Az CLI was working with SharePoint API about two years ago, but stopped working at some point last year if I recall correctly. Az CLI uses by default a first party Microsoft app registration if I'm not mistaken.

So I decided to take a look at how @azure/identity works with AzureCliCredentials and noticed it's a simple CLI wrapper. Then I blatantly copy pasted the implementation to work with m365 cli and have been using this in my own projects for the time being.

I've currently implemented this only in TypeScript. See my sample here to test this in your environment.

So technically it's a wrapper against m365 util accesstoken get, and is piggybacking on the idea that most developers usually have PnP Management Shell app registration already done and thus if you have m365 cli access, you have an easy way to develop with your own credentials.

I see this as a feature that would make local testing and development much more developer friendly. I think there would be use for this as a pnp project, but I am not sure where and how should this be packaged.

jhholm commented 2 months ago

As mentioned above, my code is in purpose copied from the implementation made in @azure/identity. It should make it easier to follow that the implementation provides the same functionality

https://github.com/jhholm/m365clicredentials-example/blob/main/src/library/m365cliCredential.ts https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/identity/identity/src/credentials/azureCliCredential.ts

So far the only dependencies are @azure/core-auth and jwt-decode. Decoding the token is needed, as 'm365 util accesstoken get' doesn't provide the expiration date for the token as a sting/number.

waldekmastykarz commented 2 months ago

Thanks for the suggestion. One caveat with using the Azure CLI app reg is that you can't grant additional scopes to it, because it's a first-party app managed by Microsoft. As such, it would only work with a small subset of commands that we offer in CLI for Microsoft 365. Let us give it a though what's the best way to proceed.

waldekmastykarz commented 2 months ago

We've been discussing something similar already in https://github.com/pnp/cli-microsoft365/issues/6067

jhholm commented 2 months ago

I think there is a small misunderstanding. I am merely using Azure CLI as an example how I can gain access to Graph API and other Azure services right now when developing locally without a certificate. I don't see #6067 as a similar issue.

I was hoping that we could provide a NPM package that allows a developer to use M365 CLI similarly to do local development without a certificate, albeit not with application permissions. This has not been trivial to do outside of browser context. The part that I am looking to implement from my repo is under https://github.com/jhholm/m365clicredentials-example/tree/main/src/library.

I believe it's quite common to already have PnP Management Shell app registration and I don't see that we need to do any permission changes there right now as it by default has quite broad user permissions. As there are other ways of authenticating and logging in with M365 CLI, this solution would need testing alternative ways as well.

The packaging question in my opionion is, should this be included as a package under pnpjs or this project.

jhholm commented 2 months ago

As this feature is more of a developer enhancement and is not actually providing any additional functionality to the CLI itself, the pnpjs project might be the best place for the package to reside in. They are already providing a helper wrapper @pnp/azidjsclient.

As this is strongly linked on how 'm365 util accesstoken get' is implemented, there is still some collaboration and discussion needed. I would appreciate if the command could provide the expiration date as well, but this could cause some breaking changes.

waldekmastykarz commented 2 months ago

Not quite sure I understand the benefit here. The way I see it, if you use the az CLI identity, you don't need to create your own app reg/consent the PnP Management Shell app reg. However, like I mentioned earlier, az CLI has a fixed, and in the context of CLI for Microsoft 365, a limited set of scopes which would let you use only several commands from everything that CLI for Microsoft 365 has to offer. Could you please tell us more about what use cases you'd use it for so that we can better understand your request?

jhholm commented 2 months ago

Yeah, I think we are still talking about different things here.

I'm not actually suggesting a new feature or an authentication method to M365 CLI. I am suggesting and a new PnP library (npm package) that would use M365 CLI to authenticate. I provided a sample repository that has an Azure Function with to Http triggers: one is using Az CLI to authenticate against Graph API, and the SharePoint one is using my wrapper for M365 CLI. Additionally PnPJS is used.

Example use case:

  1. I'm an Azure Function developer
  2. I need to create an Azure Function that needs to get data from SharePoint API
  3. I have M365 CLI and PnP Management Shell app reg installed (with default permissions)
  4. I can use Managed Identity with application permissions while running the code in Azure (this is supported by SharePoint)
  5. While developing the function locally I can use M365 CLI identity with delegated permissions
  6. As a developer I do not need to create any app registrations or any self signed certificates
  7. As a developer I only need local user access to SharePoint and M365 CLI

Even though application and delegated permissions are not the same, they are close enough for a developer in my opinion. This just makes your developer experience simple. No need to create any self signed certificates or app registrations.

We both agree - we cannot use Az CLI identity. We can't control it as it's a first party app and additionally it doesn't allow you to call SharePoint APIs anymore. As a developer you can use Az CLI identity to call Graph API or Azure services.

As a SharePoint developer you should be able to use M365 CLI identity. By default M365 CLI is using PnP Management Shell app reg that has a broad selection of delegated permissions. You are also free to use other ways of authenticating M365 CLI.

Open issues/questions

  1. Would this be more suitable as a PnPJS package as this is not actually a CLI feature
  2. I think there might be a use case to provide additional data (expiration time) on m365 util accesstoken get
  3. Depending on the project where this lands, logging and testing needs to be implemented
  4. As I mentioned before, I've blatantly ripped the code line by line from @azure/identity, thus we need to ensure I didn't break any copyright
waldekmastykarz commented 2 months ago

Ah, now I get it! Sorry for taking so long to wrap my head around it. It would be basically an equivalent of the AzureCliCredential from the azure/identity SDK, correct? Just to check if I understand it correctly: you'd like to use it with the PnPjs library, right?

Your proposal makes absolute sense! We've got a couple of urgent things that we need to implement first, but we'll definitely pick this up. The way I'm thinking about it now, is to implement it in two pieces:

  1. A generic CLI for Microsoft 365 credential that can be used with any SDK that builts on top of azure identity
  2. PnPjs wrapper, equivalent to @pnp/azidjsclient.

Thanks again for the suggestion and your patience!

waldekmastykarz commented 2 months ago

Hey @juliemturner, @patrick-rodgers, check out the above proposal to provide a wrapper similar to @pnp/azidjsclient for logging in using the CLI for Microsoft 365 in PnPjs. We'll pick it up, unless you'd like to do it yourself :)

waldekmastykarz commented 2 months ago

@jhholm I just had a look at your example, it's a great starting point! Thanks for researching it! Since we'll need to create two new repos in the PnP org, someone from our team will pick it up so that we can do the necessary setup, ok? We'll definitely build on top of your work and of course credit you! 👏

jhholm commented 2 months ago

Sure. Just let me know if you need anything from me. I should be reachable via Discord.

Two repositories? Not sure if I follow, but I do agree this requires more planning/organizing and is not just a simple pull request by me.

For now I would see these tasks to come out of this:

I suppose you have a good idea where to package this and how to move forward. I think the easiest way would be to add it as a package in pnpjs monorepo. Keep in mind that there are use cases where you would be using this without pnpjs and perhaps with an older version of pnpjs. In my wildest dreams this could even be officially supported and part of @azure/identity.

waldekmastykarz commented 2 months ago

Two repositories? Not sure if I follow, but I do agree this requires more planning/organizing and is not just a simple pull request by me.

I think you're right and one, with just the CLI for Microsoft 365 credential would be enough. Then, in PnPjs, you could use it as a credential, like you showed in your sample. I thought we'd need a wrapper for PnPjs, but thinking about it some more, I don't think it's necessary.

juliemturner commented 2 months ago

If you create a new credential that @azure/identity will recognize then it has nothing to do with PnPjs directly so I don't think we need to be involved. Our wrapper simply takes an Azure Identity built credential, you can build that however you want. Seems like, based on this suggestion, the developer would have to build a custom pipeline instead of using the default but it should still work.