pnp / cli-microsoft365

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

Add support for signing in using multiple accounts #3587

Closed waldekmastykarz closed 7 months ago

waldekmastykarz commented 2 years ago

Add support for signing in using multiple accounts. MSAL supports this capability natively so we can build on top of it.

m365 connection list specs

Show the list of available connections

Usage

m365 connection list [options]

Options

No options

Examples

Returns a list of available connections:

m365 connection list

m365 connection use specs

When signed in with multiple identities, switch to another connection

Usage

m365 connection use [options]

Options

Option Description
-n, --name <name> The name of the connection to switch to. Can be found by running m365 connection list.

Remarks

Note: We currently have two caches: 1) The MSAL token cache which is saved to a file .cli-m365-msal.json and b) Our own cache which saves the auth.service object and is saved to .cli-m365-tokens.json. Because we are now able to log into multiple accounts, we should save the localAccountId, as a link between both caches. If we do it like this, the caching can just remain the same. The list of connections can be saved to a separate json file.

Examples

Switch to another connection by a default connection name:

m365 connection use --name '0bb7cb89-7fae-4775-a01a-c372cc167371_64e87598-07a8-4fa8-a926-862410eeec84'

Switch to another connection by a custom connection name:

m365 connection use --name 'my connection'

m365 connection set specs

When signed in with multiple identities, update a specified connection

Usage

m365 connection set [options]

Options

Option Description
-n, --name <name> The name of the connection to update. Can be found by running m365 connection list.
--newName <newName> The new name of the connection.

Examples

Update a connection with a new name

m365 connection remove --name '0bb7cb89-7fae-4775-a01a-c372cc167371_64e87598-07a8-4fa8-a926-862410eeec84' --newName 'my connection'

m365 connection remove specs

When signed in with multiple identities, remove a connection

Usage

m365 connection remove [options]

Options

Option Description
-n, --name <name> The name of the connection to remove to. Can be found by running m365 connection list.

Examples

Remove a connection by a default connection name:

m365 connection remove --name '0bb7cb89-7fae-4775-a01a-c372cc167371_64e87598-07a8-4fa8-a926-862410eeec84'

Remove a connection by a custom connection name:

m365 connection remove --name 'my connection'

Discussed in https://github.com/pnp/cli-microsoft365/discussions/3453

Originally posted by **oweiler** June 29, 2022 In my quest to get rid of all basic auth usage inside my Microsoft 365 tenant, I happened upon this project as a potential solution. Most of my basic auth usage revolves around sending and receiving emails in exchange which this project provides. I'm now looking to extend this functionality to multiple Azure AD identities/exchange mailboxes but use them all within a single linux login. Using basic auth, this was relatively simple: keep protected files with usernames/passwords as resources to curl to get and send mail from/to that mailbox. With cli-microsoft365, reading about persistent connections, I'm not sure it's that simple? One way I could see doing what I need is to keep a version of .cli-m365-msal.json and .cli-m365-tokens.json for each mailbox/M365 identity I want to manipulate but this has the obvious drawback that I can only perform operations on a single identity at a time. I looked into the options provided by the m365 login command but they all seem to end up in a place where the .cli-m365-msal.json and .cli-m365-tokens.json point to a single Azure AD identity. What's the right way to handle multiple Azure AD identities from a single login/user on a linux system?
martinlingstuyl commented 12 months ago

Alternatively, you'd call identity set specifying your identity and the name/id you specified is not available in MSAL cache, I suppose the command and hence the script would fail acting as a failsafe.

Good point on this. In scripts, the script could continue executing on failure of identity set. Meaning it could execute actions using the previously selected identity.

If identity set fails, we should therefore probably set the cli to an identityless state, to avoid this side effect.

martinlingstuyl commented 11 months ago

I've created another issue to describe enhanced prompting functionality that we'd need on this issue as well:

https://github.com/pnp/cli-microsoft365/issues/5560

Adam-it commented 11 months ago

@martinlingstuyl since you are already working on this one I will align the tags if that's fine with you 👍

martinlingstuyl commented 11 months ago

I certainly am! Ill probably create a PR next week...

martinlingstuyl commented 10 months ago

Ok @pnp/cli-for-microsoft-365-maintainers, @waldekmastykarz and I have discussed multi-accounts in some more detail. And we'd like to propose a change to the specs:

The idea is that we don't implement identity set and identity list as new commands, but include the functionality in existing commands.

Also we'd want to propose renaming m365 login and m365 logout to m365 connect and m365 disconnect. (keeping login and logout as aliasses)

Central in our discussion was the idea of having connections with m365 instead of identities or accounts.. A connection encompasses more than an identity, as it can have certain properties, like the manner of authenticating. (certificate, secret, etc) and its identity information, like Id and name. Which we already employ. connect and disconnect seem to fit better with the thing we're trying to accomplish.

In this new setup, you can use m365 connect for the initial connection, but also to switch to another connection:

# Connect to m365 by signing in using the default deviceCode flow

m365 connect

# Switch to the connection that's signed in with martin@blimped.nl

m365 connect --identityName "martin@blimped.nl"

# Disconnect from m365

m365 disconnect

# Disconnect a specific connection

m365 disconnect --identityName "martin@blimped.nl"

Listing connected identities

To get at the identityId and identityName you can run m365 status It will include an array of all identities. But just the Id and name properties.

prompting in interactive mode

An optional extra would be that running m365 connect with no options, may show a prompt where you can choose another connection or the intent to connect using another identity.

m365 connect
You have multiple active connections to Microsoft 365.  Do you want to switch to one of these identities or connect using a new identity? Select one of the options.
> martin@blimped.nl
> martin@i4-you.com
> My Custom Application
> Connect using a new identity

What would you say of such a change? And @waldekmastykarz, reading it written out like this, do you still agree?

waldekmastykarz commented 10 months ago

Great summary and still fully on board @martinlingstuyl 💪

Adam-it commented 10 months ago

hmmm not sure 🤔. I understand the concept and I like the idea and the simplicity and also the fact we would not have the additional identity commands. My only concern is that the naming is very similar to the PnP PowerShell Connect command that allows the user to connect to a specific SP Site. For us is plain simple and makes sens. For someone new that is used to PnP PS and now would be using CLI for M365 for the first time it will be very misleading I guess. With the login and logout we didn't have this problem as the naming was totally different.

good brainstorm @martinlingstuyl, @waldekmastykarz 👏 I feel like we are on the right track here 👍 I just don't quite feel the connect word... Buuuut on the other hand I don't have a better idea for now 😅

martinlingstuyl commented 10 months ago

My only concern is that the naming is very similar to the PnP PowerShell Connect command that allows the user to connect to a specific SP Site

I understand what you mean. However, the likeness is quickly gone once you realize there is no SharePoint site URL option.

It then becomes more like 'm365 connect': I'm connecting to m365!

martinlingstuyl commented 10 months ago

Other input @pnp/cli-for-microsoft-365-maintainers?

Jwaegebaert commented 10 months ago

Nice work with the research on this topic. I'm also not really keen on changing m365 login to m365 connect. This is one of the most used commands and changing the name of this would be a huge breaking change, although you're keeping it as an alias. I understand that we can do changes over time to command names but this feels like we're changing for the sake of introducing a new breaking change. I'm not quite sure why we should rename this.

About the idea of introducing a new option to --identityName is a solid plan. this would make the commands login, logout, and status a complete package to quickly manage and change your identities. All I can say is, ship it 😄

martinlingstuyl commented 10 months ago

I'm not quite sure why we should rename this.

So you're saying you don't yet see the logic, or the necessity for this?

You could say that If we add identityId and identityName to the login command, it becomes less logical to call the command login, as we've already logged in. We're not signing in again. It's slightly more logical to say you are "connecting to microsoft 365 using an identity that we already know". m365 connect is slightly more generic and does not express the intent to go throught the login flow perse, like m365 login does.

But maybe it's a bit 'poteto potaato'. @milanholemans, @appieschot, what are your views on this?

(In v8 we would stop supporting login of course. But to ease the process of transitioning, it's not a bad idea if we show a warning message when people do use m365 login, aka: 'use m365 connect to login to m365 instead')

milanholemans commented 10 months ago

I'm not really convinced to rename these two commands, to be honest. Maybe it makes more sense to rename it to connect/disconnect (not sure about that yet), but in my opinion login and login are just as clear. And for new people much easier to understand. To me, it looks like a quite drastic breaking change. Instead of m365 connect --identityName abc, I would prefer m365 login --connectionName abc or something like that.

It would be a great enhancement if you could pass a name (not sure if this should be optional or required) for a connection. When you have different customers, giving your connection a unique name is much easier. Instead of connecting to m365 connect --identityName milan.holemans@contoso.onmicrosoft.com I rather run m365 connect --identityName contoso to switch identity.

Adam-it commented 10 months ago

Not to mention the number of sample scripts we would need to update when v8 is out... like all of them 😅, at least in our repo where we try to keep up to date with scripts with the latest major version.

What if we keep both? login and connect? That's probably the worst option right 😅 as it does not solve the problem of naming and creates more confusion 😉

Jwaegebaert commented 10 months ago

I'm also thinking of all the sample scripts. We always have to keep them up to date with major versions but changing the login command would impact them all. From a naming perspective, it makes sense but then the approach of @milanholemans could also be a route to take. Changing the option name to connectionName rather than identityName.

+1 on the suggestion to allow passing a connection name. That would make it even easier to switch.

waldekmastykarz commented 10 months ago

To clarify the rationale behind the proposed rename. It's anything but renaming for the sake of rename. The last thing we want to do is to break our users and we've got some good first hand experience of what impact a breaking change in a dependency has, especially at scale.

We want to change so that the action sound logical when you read it. m365 login --connectionName .... Are we saying that you're logging in with a connection? That doesn't make any sense, doesn't it? Also, we're not naming connections. We have identities which have an ID and a name (upn or app name). Connection is a combination of identity and a way to authenticate (cert, device code, secret, MSI, etc), along with some state information such as refresh- and access tokens.

m365 login --identityName xyz - but you've already logged in with that identity and are merely activating it, confusing, no?

We can always go back to m365 identity set --name xyz, but isn't that complicating it, comparing to m365 connect --identityName xyz?

Yes, renaming login to connect is a breaking change that would affect everyone. But I think the main question is will it make life easier going forward?

I appreciate your perspective and let's keep 'em coming. This would be a serious change so let's weigh all the options before we commit so that we're sure we're on the right path.

milanholemans commented 10 months ago

We want to change so that the action sound logical when you read it. m365 login --connectionName .... Are we saying that you're logging in with a connection? That doesn't make any sense, doesn't it?

I agree that when you read it, connect would make more sense yes. But I think something like m365 login --connection abc or something like that is just as clear. You interpret it like "I'm logging into CLI with an existing connection". Anyhow, I can live with it when we rename the command, but in my opinion, for new users login is more clear than connect. On the other side you have quite some PS modules that use connect to login, e.g. Connect-PnPOnline, Connect-SPOService, Connect-ExchangeOnline, Connect-AzureAD, ... So, in that case, I can accept the rename if we decide to do this.

Also, we're not naming connections. We have identities which have an ID and a name (upn or app name). Connection is a combination of identity and a way to authenticate (cert, device code, secret, MSI, etc), along with some state information such as refresh- and access tokens.

Yes, but in my opinion, it would be cool if we could give every connection a custom name to easily switch. As mentioned before, it's like giving a name to your credentials in Windows credential manager. That makes it easy to target a credential rather than typing the entire email.

Instead of:

m365 connect --identity milan.holemans@contoso.onmicrosoft.com
m365 connect --identity admin.milan@contoso.onmicrosoft.com

The following will be more user-friendly:

m365 connect --identity contoso
m365 connect --identity "contoso admin"

Besides that, if the identity is only a UPN or app name, we might run into trouble. For multi-tenant Azure AD apps using application permissions, it's possible that 2 or more connections would have the same app name.

Adam-it commented 10 months ago

...for new users login is more clear than connect...

Unfortunately, I kinda have the same feeling 🤔. Basically, I am also fine with renaming even if this means a lot (.. ok some) work with refactoring the scripts. My comment was only to point out that we should think what is best for the ned user (this is not always the most logical option as it may turn out 😅).

Also you may 'connect' to many CLIs but only 'login' to the best one 😉

martinlingstuyl commented 10 months ago

Besides that, if the identity is only a UPN or app name, we might run into trouble. For multi-tenant Azure AD apps using application permissions, it's possible that 2 or more connections would have the same app name.

In that case it will show our disambiguation prompt.

milanholemans commented 10 months ago

In that case it will show our disambiguation prompt.

How will you differentiate? By showing the tenant ID? That doesn't say a lot according to me.

martinlingstuyl commented 10 months ago

That's same challenge for all disambiguating prompts.

It is possible to change that I guess.

martinlingstuyl commented 10 months ago

To answer some of the points that are made:

how do you guys feel about this, after having thought about it a bit. Do I have your blessing in implementing it this way?

Jwaegebaert commented 10 months ago

Personally, still not keen on renaming it to connect. If we change it, I suggest starting it off as an alias and awaiting some feedback from the community. Just so we can gain some other perspective from folks who are using it intensely.

Giving the identities an alias would be a nice enhancement but like you're suggestion @martinlingstuyl. That shouldn't be implemented straight from the bat but would be a nice addition for a separate issue.

martinlingstuyl commented 10 months ago

If we change it, I suggest starting it off as an alias and awaiting some feedback from the community

I don't think that's really an option. m365 login --identityId "<guid>" is just quite unclear. We'd best think about the most logical direction forward that fits all the purposes we see the CLI used for. I believe we should either move to the connect-concept or build it as m365 identity set. (A shorter alternative could also be m365 switch. )

As said before: for people coming from PowerShell commandlets, (which is most of our users), m365 connect will definitely make sense as well as m365 login does. I know some other CLI's use login, like az login, but then in their implementation of multi-account they have exactly the command sprawl that we're trying to avoid: az account show / az account list / az account set etc etc

I believe using m365 connect / m365 disconnect / m365 status we keep a clear single set of commands for working with auth.

Adam-it commented 10 months ago
  • We could also leave m365 login as a permanent alias, (beyond v8) if you're worried about new users. For this command I'd not mind if we do. We may of course point to m365 connect as the primary route.

TBH this is quite unclear but works for me 😅. at least for now. That way we may add the connect and root users to this path but still keep the old login as safe option

  • In case the user needs to disambiguate the required identity from a list of identities with the same name, currently we write ID's to the screen (as we do for all disambiguation prompts). It's true that does not always help a user very much. For the identity disambiguation prompt this would be the same. Maybe we can discuss separately how we can show a more useful selection.

yes let's create a separate issue for it. I would love to take it up 👍 and improve this 👍

martinlingstuyl commented 8 months ago

I've updated the specs @pnp/cli-for-microsoft-365-maintainers

Adam-it commented 8 months ago

I've updated the specs @pnp/cli-for-microsoft-365-maintainers

Awesome, I love how we combined both connect command and login command. 🤩. @martinlingstuyl, @waldekmastykarz awesome work 👏, huge congrats 🎉, just look at the amount of comments on this issue 😲. It wasn't easy but we are finally there 👍. Let's ship it 🚀