msgraph-sdk-powershell
msgraph-sdk-powershell copied to clipboard
Adding the ability to surface the access token used by the connection
Discussed in https://github.com/microsoftgraph/msgraph-sdk-powershell/discussions/2010
Originally posted by glennvanrymenant May 15, 2023 Would it be possible to add the ability to surface the access token used by the connection (Connect-MgGraph).
I prefer to call the Graph API directly (using Invoke-MgGraphRequest rather than the specific cmdlets) but ideally I would like call the Graph API directly with Invoke-WebRequest using the access token of the Graph SDK connection.
Read: I want to piggy-back on the auth part of the module (mainly when leveraging delegated permissions as using the auth of the module is easier than writing and maintaining your own authorization code flow functions).
My concern about exposing the access token is that someone is then going to use it, and then call some API that has conditional access and it will fail. At that point we are going to create a support request because the user will not know why it failed. Or they may have a CAE token and that token could get invalidated at any time.
As tempting as it is to simply take tokens and use them elsewhere, that is a solution that is going to fail more and more over time. This is especially true as we add support for Windows Broker and we start getting Pop tokens https://learn.microsoft.com/en-us/entra/msal/dotnet/advanced/proof-of-possession-tokens
@CarolKigoonya you may want to stick with official stuff, such as the Microsoft.Graph.*** modules, but if you're willing to go with MSGraphPSEssentials, you can accomplish what you're after. It would be via New-MSGraphAccessToken
, however it only works with app-only (client credential, with certificate) or device code flow for delegated.
https://github.com/JeremyTBradshaw/MSGraphPSEssentials/wiki/Get-an-app-only-access-token https://github.com/JeremyTBradshaw/MSGraphPSEssentials/wiki/Get-an-access-token-via-Device-Code-flow https://github.com/JeremyTBradshaw/MSGraphPSEssentials/wiki/Refresh-an-access-token https://github.com/JeremyTBradshaw/MSGraphPSEssentials/wiki/Store-tokens-as-PSCredential's-in-secure-XML
One other thing about Access Tokens - I've been told many times by various people from Microsoft - do not look inside access tokens! I personally am not vouching that you do, but stating that guidance here to avoid any lashings. Sidenote - I do think that Get-MgContext simply looks inside the access token but it is their own thing (MS'), so fair play. MSGraphPSEssentials has a function - ConvertFrom-JWTAccessToken that will let you see inside, until the day comes that Access Tokens are encrypted, which apparently is on the way.
It is for this reason (last paragraph) that you'll find an extra property on the access token object that comes out of New-MSGraphAccessToken - "issued_at". The purpose of that is to let you use Get-AccessTokenExpiration to figure out if it's time to request a new access token (i.e., current one due to expire soon).
In typical DevOps scenarios users need to automate configuration changes across Microsoft 365, Azure and even other clouds such as AWS and GCP (through workload identity federation). They may use various tooling such as Terraform, Pulumi, etc.
Graph PowerShell exposing the access token allows devs to use Graph PS as the primary connection point and then share the access token to other CLIs and tooling in the workflow.
Currently, without this functionality we cannot rely on Graph PowerShell as the foundation on which to build DevOps pipelines. This will hamper productivity and lead to workarounds and users searching for alternate methods for working with Graph in PowerShell.
FYI here is a list of CLIs and apps that expose the access token today.
Microsoft first party tooling
- Azure CLI → az account get-access-token
- Azure PowerShell → Get-AzAccessToken
- MSAL.PS → Get-MsalToken
- Graph Explorer Get Access Token
Third party tooling
- PnP PowerShell → Get-PnPAccessToken
- M365 CLI m365 util accesstoken get
Third party tooling like Terraform's Azure Active Directory Provider support using Azure CLI to handle the authentication since there is a published API for Terraform to access the session's token. Not having a Get-MgAccessToken API would make it harder to build solutions that depend on Graph PS.
@merill I appreciate what you are trying to achieve. I want to avoid building a solution where customers call Get-MgAccessToken once and then store and reuse that token for every request they make. How can we build a cmdLet that would lead people down the path of calling the cmdlet to get a token on every request they make?
Most devs are familiar with the MSAL pattern of invoking getAccessToken before making an API call with a token.
It would be best to reuse the same pattern instead of introducing a new pattern just for Graph Powershell.
The doc for the cmdlet can share the recommended pattern.
Also the error message when using an expired token is self-explanatory.
@merill assuming the expired token is self-explanatory comment is geared towards me. I do get that, just wanted to include a way to avoid the wasted request/response. Albeit, it's not a whole lot of waste.
@JeremyTBradshaw I was commenting in general.
The MSAL library already does this out of the box. It checks the expiry time and only makes a call to AzureAD when the token is about to expire. We don't need each dev to write code checking for token expiry.
The recommended MSAL pattern for devs is to call getAccessToken before every Graph call. MSAL takes care of caching and token renewal.
This is what the MSAL doc says "MSAL caches a token after it's been acquired. Your application code should first try to get a token silently from the cache before attempting to acquire a token by other means." https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-acquire-cache-tokens
Please diregard.. I've now seen the other issue, #2010.
@darrelmiller I understand your concern regarding the use of the token against other API's but as @merill already pointed out, there are several other MS-backed initiatives that already facilitate it - I read a blog post the other day where they (ab)use Azure PowerShell to request an access token and use it for the Graph SDK... - and I personally think that the events where the access token would be used for anything else than the Graph API would be rather rare.
I'm a bit intrigued by your mention of CAE as I always assumed (perhaps incorrectly) that this would never apply in a development context, I can see the added value for revocation (i.e.: critical event evaluation) but how would you accommodate for a (possibly) lifetime-lengthened access token in your code? And how does that rhyme with your other comment about ensuring a new token is requested for every call?
For the latter, this could be added (if not already) to the cmdlets on the SDK (including the Invoke-MgGraphRequest) and when developers purposefully retrieve a token separately, is their responsibility to follow best practices and guidelines just as if they were acquiring a token through any other means.
@JeremyTBradshaw perhaps you can educate me on why the device code flow is so often abused by MS for delegated auth flows? AFAIK, the device code flow is intended for scenarios that involve input-constrained devices and the proper auth flow to use would be the auth code flow with PKCE, correct?
@glennvanrymenant I wish I could, but honestly it's over my head what you've asked. I created the MSGraphPSEssentials module to help myself (initially) use Graph from PowerShell and Device Code is the one delegated flow that doesn't involve pulling in DLL's and browser stuff. It allowed me to keep my module tiny and simple.
Maybe you could say I also butchered it haha. But I am not MS employee/affiliate.
@JeremyTBradshaw please don't sell yourself short. I hear you regarding the dependencies, I also strive to write my PS code without them (modules, .NET classes/methods, ...) and it's true that auth code does require interaction with a browser but it's still the best option (AFAIK) in a delegated scenario. At least device code is still a better than ROPC!
Would also like to see the MS.Graph powershell module follow the same patterns as the other first party Microsoft Azure modules. As @merill has already pointed out.
What is the status of this request? Looks orphaned 🤔.
Hoping someone can progress on this. There are many tools out there in the community currently stuck with using MSAL.PS because its to hard to move away from it. Please get some work started to build the Get-MgAccessToken cmdlet. @darrelmiller @petrhollayms
I don't understand why we (the customers and community) have to go through these discussions with every single team that manages a CLI or PowerShell module.
As @merill mentioned, many other Microsoft tools already support this. @darrelmiller please consider exposing this capability like so many other tools already do.
Thank you!
I would also like to advocate for this change, as it would drastically help improve some of my pipelines. Currently, https://github.com/SCOMnewbie/PSMSALNet works quite well, but I would prefer to use a supported, MS-backed, module :)
Someone on Twitter pointed me to this here:
$data = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/me" -Method GET -OutputType HttpResponseMessage
$data.RequestMessage.Headers.Authorization.Parameter
This indeed works.
Would that not only get the token with the claims for what you asked for? If I want to do something else, I'd need a different token.
Either way, I second (fifth, sixth whatever) this request. I am in the exact same situation now. I can auth with Azure and then use Get-AzToken, but "it's not the right context for that". I don't want to go to the Azure context to get the token for my Entra context.
How about this method. Ask Entra for a token by making a POST to:
[Uri]"https://login.microsoftonline.com/$(Get-TenantId)/oauth2/token"
My challenge becomes that we need a "resource" in the body. This works well when asking for a token to, for example, call an API.
@petrhollayms, @merill since this is still open and hasn't been closed, is there any progress you can share? Can we expect the module to support generating/exposing tokens, or do we continue to rely on workarounds to grab the token?