Skip to content

Proposal: Find-MgGraphPermission command to enable use of application API #704

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

Closed
adamedx opened this issue Jun 17, 2021 · 4 comments
Closed

Comments

@adamedx
Copy link

adamedx commented Jun 17, 2021

Find-MgGraphPermission Command

Problem

The Microsoft Graph PowerShell SDK application requires users to have domain knowledge of both the semantics and syntax of Microsoft Graph API permissions used to authorize access to the API. There are two key permissions scenarios that present a challenge for users:

  1. How do I find the values to supply to the permission-related parameters of commands like New-MgApplication and other application and consent related commands? Commands that allow the user to specify permissions like User.Read in order to grant those permissions to an application or ensure that the application requests those permissions offer a parameter to identify the permission(s) in question. While the user will be familiar with those permissions through their documented names like User.Read that are also used in many other user-facing contexts, the commands require that they be referred to not through the human readable name but by using a GUID identifier. Unfortunately, the identifiers are not documented, so there is no way for a user to tranlsate the intention of say granting an application User.Read to parameters that can be supplied to an MS Graph PowerShell SDK command-line. The workaround is for the user to perform the task using the Azure Portal which does allow the user to specify the human-readabl name, and once the application is configured other commands that read the configured permissions could return the GUIDs, allowing the user to "reverse-engineer" the friendly-name to GUID mappings.
  2. What permissions should I ask for when I invoke the mandatory Connect-MgGraph command? To use Microsoft Graph PowerShell SDK to access Microsoft Graph, users must sign in to an Azure Active Directory application using the Connect-MgGraph command. Users must explicitly specify the permissions required for the API they are accessing when issuing the command. If a user fails to include required permissions when issuing the Connect-MgGraph command and those permissions have not already been consented to the application / user, subsequent commands that access the APIs authorized by those permissions will fail.
  3. Currently PowerShell commands and scripts, including those implemented with Microsoft Graph PowerShell SDK itself have no way of validating user input that refers to permissions or providing "auto-complete" user experiences to help users accurately supply input to commands

Note that these cases are listed in priority order -- the first in particular is considered a blocking issue. The other two still have significant impact on user experience, but there are some less-productive workarounds that users may employ.

Solution

All three issues can be resolved through a solution that addresses the first issue, which is itself a blocking issue

Find-MgGraphPermission

function Find-MgGraphPermission {
    [cmdletbinding(positionalbinding=$false)]
    param(
        [parameter(position=0, parametersetname='searchspec')]
        $PermissionSearchString,

        [parameter(parametersetname='searchspec')]
        [switch] $Exact,

        [ValidateSet('Any', 'Delegated', 'Application')]
        [string] $PermissionType
        )
            }

Example 1: Find permissions related to a given domain

PS> Find-MgGraphPermission mailbox

   PermissionType: Delegated

PermissionType  Consent Name                      Description
--------------  ------- ----                      -----------
Delegated       User    MailboxSettings.Read      Allows the app to the read user's mailbox settings. Does...
Delegated       User    MailboxSettings.ReadWrite Allows the app to create, read, update, and delete user's...

   PermissionType: Application

PermissionType Consent Name                      Description
-------------- ------- ----                      -----------
Application    Admin   MailboxSettings.Read      Allows the app to read user's mailbox settings without a...
Application    Admin   MailboxSettings.ReadWrite Allows the app to create, read, update, and delete user's...

Example 2: Find the identifier for a specific permission

PS> Find-MgGraphPermission User.Read | Format-List

   PermissionType: Delegated

Id             : e1fe6dd8-ba31-4d61-89e7-88639da4683d
PermissionType : Delegated
ConsentType    : User
Name           : User.Read
Description    : Allows users to sign-in to the app, and allows the app to read the profile of signed-in users.
                 It also allows the app to read basic company information of signed-in users.

Implementation

To successfully execute, the command must be able to retrieve the following information:

  • The list of all delegated and app-only permissions
  • For each retrieved permission the following are needed:
    • The friendly name of the permission, i.e. User.Read
    • The permission identifier guid that may be used to grant consent through the application API
    • The kinds of consent that are allowed, i.e. consent by the signed-in user, or consent by by an administrator
    • Whether the consent is for the application only or delegated by the user to the application
    • Some basic description of the permission

Fortunately, it turns out that Microsoft Graph can actually find out the permissions it supports from... itself.

Using Microsoft Graph to get Microsoft Graph Permissions

The following approach described earlier on StackOverflow may be used to get both delegated and application permissions:

  1. Sign in to any AAD organization
    • Note that this means the approach here will not work for Microsoft Accounts (more later)
    • TODO: What permission is needed on sign-in?
  2. Make a REST request to MS Graph to get the service principal for the Microsoft Graph application -- an example of such a request using Microsoft Graph PowerShell is as follows:
$msgraphServicePrincipal = Invoke-MgGraphRequest -method GET 'https://graph.microsoft.com/v1.0/servicePrincipals?filter=appId eq ''00000003-0000-0000-c000-000000000000''' | select-object -expandproperty value
  1. Parse the permissions from the serviceprincipal:
    a. Obtain all of the delegated permissions by enumerating the elements of the oauth2PermissionScopes property of the aforementioned service principal
    b. Obtain all of the application permissions by enumerating the elements of the appRoles property of the service principal
    c. Note that these parsed permissions include the friendly name, the id of the permission such that it may be used win APIs that require permission id's (e.g. app creation / modification APIs), descriptions for user and application consent, and other information. Note that the elements of the appRoles and oauth2PermissionScopes properties have similar but not identical structure
  2. Once the permissions are obtained, they may be searched based on user input, e.g. a user may want all permissions with mail in the name. Or they way want a permission with an exact name (e.g. User.Read exactly and not User.Read.All). They may even want to perform a "reverse" lookup by id instead of name. Perhaps they want to have the search include the descriptions in case the topic of the permission is not always described solely by the name.

Considerations

  • We should avoid making a REST request on every invocation of the command -- it likely only needs to be done once per PowerShell session. At most we should just cache it in memory after the first successful retrieval, or have a very long expiration on the cache. A force parameter is also an option to ensure the latest data.
  • We will likely need to have a static fallback built into the command for the following reasons:
    • There may be instances where the command is used but Connect-MgGraph has not been invoked (e.g. parameter completion of Connect-M gGraph itself). So we still need to give a good, if not perfect answer.
    • If you connect using a Microsoft Account rather than an AAD account, then it does not appear we can query the MS Graph service principal, and hence cannot obtain the permissions
    • Even if you are signing in to an AAD account, you may not have sufficient permissions to read the Microsoft Graph service principal

Fallback logic

When obtaining permissions data, the behavior should be as follows:

  • If the data were retrieved earlier from the directory (not the static file), the cached version should be used
  • Otherwise, we should try to get the data from the directory
    • If we retrieve data successfully from the directory, cache it so it can be used in future invocations
    • If we cannot get it for any reason, return back static file data
  • Exception: if the user specifies the online parameter, then try to get it from the directory only -- if we can't, the command should fail
    • If successful, the cached data should be updated
    • Failure here should not impact the state of the cached data -- it should not be cleared or otherwise reset if we fail

References

AB#9941

@ghost ghost added the ToTriage label Jun 17, 2021
@peombwa peombwa added this to the Backlog milestone Jun 17, 2021
@adamedx
Copy link
Author

adamedx commented Jun 18, 2021

@peombwa , should it be Find-MgPermission or Find-MgGraphPermission?

@peombwa
Copy link
Member

peombwa commented Jun 18, 2021

@peombwa , should it be Find-MgPermission or Find-MgGraphPermission?

This is good question. We should go with Find-MgGraphPermission to avoid potential conflict with a /permissions API in the future. This will also align nicely with existing commands such as Invoke-MgGraphRequest and Connect-MgGraph. See the discussion here on why we settled for Connect-MgGraph.

@peombwa
Copy link
Member

peombwa commented Jun 18, 2021

cc\ @mairissi, @ddyett, @darrelmiller, @georgend, feel free to offer your feedback on this proposal.

@adamedx
Copy link
Author

adamedx commented Aug 16, 2021

Closing this issue since the new command is merged -- thank you very much @FehintolaObafemi , @peombwa , @darrelmiller , @maisarissi , @msewaweru !

@adamedx adamedx closed this as completed Aug 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants