Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get-AzAccessToken should also allow specifying scopes #14085

Closed
petehauge opened this issue Feb 1, 2021 · 27 comments
Closed

Get-AzAccessToken should also allow specifying scopes #14085

petehauge opened this issue Feb 1, 2021 · 27 comments
Assignees
Labels
customer-reported feature-request This issue requires a new behavior in the product in order be resolved. needs-author-feedback More information is needed from author to address the issue. P1 question The issue doesn't require a change to the product in order to be resolved. Most issues start as that
Milestone

Comments

@petehauge
Copy link

Description of the new feature

The Get-AzAccessToken commandlet is awesome! Unfortunately I can't use it for my scenario because it's missing being able to set the "Scopes" in the authentication flow.

The feature request would be to enable passing scopes to the commandlet and could be done in two different ways:

Add -Scope as a parameter:
Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com" -Scope "Directory.readwrite.all"

Enable Resource URL to include scopes:
Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com//Directory.readwrite.all"

Scenario

I wanted to mention the scenario in which I want to use this feature: I am using the MicrosoftTeams PowerShell module (currently in preview). There is something supported by the API that's not yet supported by the module, so for 1 particular scenario, Adding tabs to an existing teams channel, I need to call the Rest API instead of a commandlet. The code looks like the following, but the Access Token part is the only bit of the code that doesn't work.

# First, see if the Lab Services Team app is already installed
$labServicesTeamsApp = Get-TeamsAppInstallation -TeamId $groupId -AppId $labServicesTeamsAppId
if (-not $labServicesTeamsApp) {
    Add-TeamsAppInstallation -TeamId $groupId -AppId $labServicesTeamsAppId
}

# Config file doesn't currently have tab name, should we add it?  Just assuming "General" tab for now
$tabDisplayName = "General"
$channel = Get-TeamChannel -GroupId $groupId | Where-Object {$_.DisplayName -ieq $tabDisplayName}

# -------------------------------------------------------------
# Code to add the app to the tab in the team
$apiUrl = "https://graph.microsoft.com/v1.0/teams/$groupId/channels/$($channel.Id)/tabs"

$apiBody = @"
{
    "displayName": "Azure Lab Services",
    "teamsApp@odata.bind" : "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/$labServicesTeamsAppId",
      "configuration": {
        "entityId": "AzureLabs",
        "contentUrl" : "https://labs.azure.com/subscriptions/$subscriptionId/resourcegroups/$resourceGroupName/providers/microsoft.labservices/labaccounts/$labAccountName/labs?host=Teams",
        "removeUrl" : "",
        "websiteUrl" : "https://labs.azure.com/subscriptions/$subscriptionId/resourcegroups/$resourceGroupName/providers/microsoft.labservices/labaccounts/$labAccountName/labs?referrer=Teams&tenantId=$tenantId&groupId=$groupId"
    }
}
"@

# Need TeamsTab.ReadWrite.All scope for this token
# TODO:  This line of code doesn't work - access token doesn't have the right scopes!
$accessToken = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com").Token

$tabdetails = Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} `
                                    -Uri $apiUrl `
                                    -ContentType 'application/json' `
                                    -Method POST `
                                    -Body $apiBody -Verbose


$generaltabdetails = Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} `
                                    -Uri $apiUrl `
                                    -ContentType 'application/json' `
                                    -Method GET
@petehauge petehauge added feature-request This issue requires a new behavior in the product in order be resolved. needs-triage This is a new issue that needs to be triaged to the appropriate team. labels Feb 1, 2021
@ghost ghost added question The issue doesn't require a change to the product in order to be resolved. Most issues start as that customer-reported and removed needs-triage This is a new issue that needs to be triaged to the appropriate team. labels Feb 1, 2021
@dingmeng-xue dingmeng-xue added the P1 label Feb 4, 2021
@dingmeng-xue dingmeng-xue added this to the Backlog milestone Feb 4, 2021
@dingmeng-xue
Copy link
Member

We will evaluate this feature request.

@thalluripk
Copy link

Any progress on this?

@dingmeng-xue dingmeng-xue added the needs-team-attention This issue needs attention from Azure service team or SDK team label Sep 2, 2021
@dingmeng-xue dingmeng-xue added Breaking Change Release This PR contains breaking change and removed needs-team-attention This issue needs attention from Azure service team or SDK team labels Sep 27, 2021
@HMassi87
Copy link

HMassi87 commented Mar 7, 2022

@dingmeng-xue I can see that this moved to "Breaking Change Release" about 5 months ago, does this means that it will be included in the next release?
This would be a game changer for a lot of automations where people are trying to avoid App Secrets

@dingmeng-xue
Copy link
Member

dingmeng-xue commented Mar 7, 2022

@HMassi87 , that label means feature will introduce breaking change potentially. We haven't planned it because we are still understanding the benefit to users and potential impact.

Back to original requirement, Get-AzAccessToken and Invoke-AzRestMethod both have already supported resource id of MSGraph. Scope seems not required because MSGraph only granted high privileged delegate role to Azure PowerShell. It means client should not/cannot do more than signed in account.

@HMassi87
Copy link

HMassi87 commented Mar 7, 2022

@dingmeng-xue
Thanks for the quick reply! I think that the biggest problem most of us are finding with this approach is that we can't use this module to authenticate and gain the same level of access that we have on Graph explorer for example.

I can't do anything on Intune using managed identities
I use this code for example to authenticate either with the local account or with the managed identity depending on where I am running it

if(!$Global:AZConnection){
    try{
        if($nonInteractive){
            Write-Output "Logging in with MI"
            $Null = Connect-AzAccount -Identity -ErrorAction Stop
            Write-Output "Logged in as MI"
        }else{
            Write-Output "Logging in to Azure AD"
            $Global:AZConnection = Connect-AzAccount -ErrorAction Stop
            Write-Output "Logged in to Azure AD with $($Global:AZConnection.Context.Account.id)"
        }
    }catch{
        Throw $_
    }
}
$Token = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com/").Token

But the only scopes that I get from this are
"scp": "AuditLog.Read.All Directory.AccessAsUser.All email openid profile"

I can even expose my API and create my own scopes and I am able to get a token for those scopes using something like
$MyGraphToken = Get-AzAccessToken -ResourceUrl "api://{API-GUID}"
but not for the MS Graph API scopes that I assigned to the API like DeviceManagementManagedDevices.Read.All or any other scope what forces me to create an App Secret although everything I need to do could be done using delegated permissions

@dingmeng-xue
Copy link
Member

Hi @petehauge and @HMassi87 , After verifying the scope in my environment, I realized your requirement cannot meet even we changed the code. Please look into #18080.

Azure PowerShell is the first party app. For MSGraph API, All required permissions are granted by MSGraph in advance. AuditLog.Read.All Directory.AccessAsUser.All email openid profile are all granted. You cannot get Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com/Directory.readwrite.all" if client id is Azure PowerShell.

Your requirement can be met when Azure PowerShell becomes 3rd party app. It is on our roadmap. I'm hesitating to merge the PR. What's your thought?

@petehauge
Copy link
Author

Hi @dingmeng-xue - good question on this one, if it doesn't cover my original scenario that I'm not sure I can use it. I have a question though, based on your other comment (Azure Powershell becoming 3rd party app) - if that happens, does that mean that we will need your code in the PR 18080 anyway to enable the scenario? If so, seems like it's still worthwhile to merge? Thanks!

@dingmeng-xue
Copy link
Member

Hi @dingmeng-xue - good question on this one, if it doesn't cover my original scenario that I'm not sure I can use it. I have a question though, based on your other comment (Azure Powershell becoming 3rd party app) - if that happens, does that mean that we will need your code in the PR 18080 anyway to enable the scenario? If so, seems like it's still worthwhile to merge? Thanks!

Scope contains 2 parts, resourceId and permission. Azure PowerShell accepts any resource id and automatically appends permission .default. It creates wrong scope when user specifies permission as part of resource id. This change is required.

MSAL accepts array of scopes during authentication. So you can see strict syntax should be

Get-AzAccessToken -Scope <string[]>

or

Get-AzAccessToken -ResourceUrl <string> [-Permission <string[]>]

Support above requires lots of code change because existing code is not designed for multiple scopes/permissions. If user cannot get benefit immediately, I'd like put it on hold for a while.

@petehauge
Copy link
Author

I see, thanks @dingmeng-xue - my original issue was accessing the Teams API (since I'm logged in against the same tenant) - but if that won't work with this change than I don't have a preference on merging since I can't use the work yet.

In particular, I want to be able to get an access token with the following scope:
TeamsTab.ReadWrite.All

Thanks!

@mabster
Copy link

mabster commented May 13, 2022

I can't do anything on Intune using managed identities

@HMassi87 I think I'm having the same issue you are.

I've added two application scopes to my managed identity (Enterprise Application):

  • DeviceManagementManagedDevices.Read.All
  • DeviceManagementServiceConfig.ReadWrite.All

... yet when the App Service running as that managed identity calls Get-AzAccessToken, those two permissions do not appear as scopes. All the others I have added (e.g. Directory.Read.All, TeamMember.ReadWrite.All) appear, but not these two.

I know I'm risking drifting off topic, but is there something about the Intune Graph endpoints that's making this happen? )Feel free to hit me up on Twitter (@mabster) if you'd prefer to take the conversation out of GitHub.)

If adding a -Scope parameter to Get-AzAccessToken could solve this issue and somehow force those scopes into the token, that would be awesome.

@mabster
Copy link

mabster commented May 15, 2022

OK project owners, now I'm really confused.

On Friday I disabled all the scheduled jobs in my Azure App Service that are calling Get-AzAccessToken and left the service idling over the weekend.

This morning, Get-AzAccessToken does indeed return a token with the scopes I had added to my managed identity last week.

Does Get-AzAccessToken cache somehow? Why was it not returning these new scopes as soon as I added them to the MSI? Is it using a refresh token to keep the current (now incomplete) token "current" somehow?

@dingmeng-xue
Copy link
Member

@mabster , you are right that there is a kinds of cache. After login (access token login), client will get primary refresh token and protect it leveraging MSAL (the secret of SP is not the case). The expiration of primary refresh token is 90 days. Get-AzAccessToken redeem access token to specific endpoint (ARM by default) using refresh token.

Azure PowerShell use .default as permission. But whole logic is controlled by AzureAD and MSAL. I cannot confirm that your case is a bug or by design. If you have concern, please create a new Github issue and share us your steps.

@JustinGrote
Copy link

Out of curiosity, the Graph API "default" application supports adding dynamic scopes that aren't part of the original specification with admin approval. Maybe similar could be added for connect-azaccount -scopes?

image

@dingmeng-xue
Copy link
Member

@JustinGrote @mabster ,

When Connect-AzAccount on behalf a user, user cannot see "approval required" or "permission request" page because Azure PowerShell is the first party app and different from the third party app such as MSGraph PowerShell module. For MSGraph features, as third party app, msgraph module can gradually get consent from user. However, as first party app, Azure PowerShell doesn't support gradual consent. It has Directory.AccessAsUser.All permission which means "Allows the app to have the same access to information in the directory as the signed-in user." It's impossible to get an access token which has limited permission. In addition, user cannot use Azure PowerShell to access API which requires high privileged permission not covered by Directory.AccessAsUser.All.

When Connect-AzAccount as an app without user, the authentication flow is https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow. When client gets token, "the value passed for the scope parameter in this request should be the identifier (app ID URI) of the resource you want, affixed with the .default". "This value informs the Microsoft identity platform endpoint to include in the access token all the app-level permissions the admin has consented to." It seems client cannot return an access token with partial permissions.

Please let me know if you have further question.

@JustinGrote
Copy link

@dingmeng-xue thank you for taking your valuable time for a detailed explanation, it is appreciated, and I hope others find it useful as well.

@dingmeng-xue
Copy link
Member

This feature will be discussed again when Azure PowerShell can work using the 3rd party app id. Closing this request now. Your thought is welcome.

@dingmeng-xue dingmeng-xue added the needs-author-feedback More information is needed from author to address the issue. label Jun 27, 2022
@ghost ghost added the no-recent-activity There has been no recent activity on this issue. label Jul 4, 2022
@ghost
Copy link

ghost commented Jul 4, 2022

Hi, we're sending this friendly reminder because we haven't heard back from you in a while. We need more information about this issue to help address it. Please be sure to give us your input within the next 7 days. If we don't hear back from you within 14 days of this comment the issue will be automatically closed. Thank you!

@ghost ghost closed this as completed Jul 19, 2022
@HMassi87
Copy link

HMassi87 commented Oct 11, 2022 via email

@ghost ghost removed the no-recent-activity There has been no recent activity on this issue. label Oct 11, 2022
@ehrnst
Copy link
Contributor

ehrnst commented Feb 1, 2023

I found this thread when trying to request a specific app role from a custom app registration using MSI. If you assign the MSI multiple app roles, the token will always include all roles. This seems like a similar problem to the delegated scopes.

@StevieLamb
Copy link

This feature will be discussed again when Azure PowerShell can work using the 3rd party app id. Closing this request now. Your thought is welcome.

Hi @dingmeng-xue, I came to this thread through investigation of the same symptoms as the OP.

I just had 2 questions, and I'm hoping you will be able to answer?

  1. when you say
    "...It has Directory.AccessAsUser.All permission which means "Allows the app to have the same access to information in the directory as the signed-in user...""
    am I correct in inferring that, if the executing user (MSI or interactive user) has high privileges, those will be the available permissions for subsequent access using the token?
  2. regarding
    "This feature will be discussed again when Azure PowerShell can work using the 3rd party app id"
    Is the move of Azure PowerShell to a 3rd party app on the roadmap?

Many thanks

@dingmeng-xue
Copy link
Member

@StevieLamb , For question #1, I cannot confirm MSI. My understanding is it is service account. About interactive user, you are right that access token has high privilege from user.

For question #2, let's ping @dcaro , @Alex-wdy , @isra-fel

@StevieLamb
Copy link

StevieLamb commented Jul 17, 2023 via email

@dingmeng-xue
Copy link
Member

About MSI, Azure doesn't recommend user to use its token in anywhere. So, it's not easy to get access token. You can just use normal service account. Create service principal, grant sufficient permission, login Azure PowerShell with that service principal, get access token, and use it.

@StevieLamb
Copy link

StevieLamb commented Jul 17, 2023

About MSI, Azure doesn't recommend user to use its token in anywhere. So, it's not easy to get access token. You can just use normal service account. Create service principal, grant sufficient permission, login Azure PowerShell with that service principal, get access token, and use it.

I've never encountered that recommendation; where is it documented please?
Edit: it appeared ot me - and many others, apparently - that the purpose of Get-AzAccessToken -ResourceType MSGraph was precisely this. It's superfluous in other contexts, such as directly acquiring a token via e.g. MSAL.PS

@dingmeng-xue
Copy link
Member

Managed identity (MSI) links to an azure resource. If you use its token and execute script somewhere else, it is against the principle of MSI. But, definitely, you can use it for the time being.

@StevieLamb
Copy link

But, definitely, you can use it for the time being

This is good to know, thanks for confirming, @dingmeng-xue

Managed identity (MSI) links to an azure resource. If you use its token and execute script somewhere else, it is against the principle of MSI

This is still a little bit confusing to be honest, in terms of what is meant by "somewhere else".
Fortunately - based on the above - it may not be a pressing thing to understand!

But clarity would be good: for when the "for the time being" part of the above statement is no longer true, and particularly if that change is not well-publicised.
Please do feel free to redirect me if there's a more suitable person/team & location to continue this discussion?

The only use case I'm interested in is:

  • using a system-assigned MSI within an Azure Automation account's Runbook
  • accessing MSFT resources with that MSI, where authorisation is
    -- using Azure RBAC as far as possible, and
    -- using MS Graph API permissions where Azure RBAC is not an option (such as accessing anything within Microsoft 365)

In the main, the confusion is what MSFT intend that we should use for AuthN and AuthZ to Microsoft 365 resources.
If a RunAs account is deemed so insecure that the model is being deprecated, I'm finding it hard to understand how manually creating a service principal via e.g. an App Registration would be any better?

Authentication in both cases relies on either a client secret or certificate, and authorisation is assigned the same way - as far as I can tell! - i.e. using either the App Registration's API permissions tab in the Identity portal, or associated PowerShell. So any weaknesses inherent in using one would seem to apply to the other.

I do appreciate your time so far, thanks.
Little it saddened that we haven't heard from your colleagues about a roadmap item yet.

@dcaro
Copy link
Contributor

dcaro commented Jul 22, 2023

@StevieLamb using a 3rd party app for Azure PowerShell has several consequences, one of them being that an admin would need to grant permission to the app. This would create a massive breaking change in the behavior of the client that would impact every organization using Azure PowerShell.
We regularly evaluate this change but so far, our assessment of the benefits vs risks is not in favor of making this change.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
customer-reported feature-request This issue requires a new behavior in the product in order be resolved. needs-author-feedback More information is needed from author to address the issue. P1 question The issue doesn't require a change to the product in order to be resolved. Most issues start as that
Projects
None yet
Development

No branches or pull requests

9 participants