msgraph-sdk-dotnet icon indicating copy to clipboard operation
msgraph-sdk-dotnet copied to clipboard

PreAuthorizedApplications and Oauth2PermissionScopes are not created anymore using Microsoft.Graph 5.0.0-preview.10

Open Rookian opened this issue 3 years ago • 0 comments

Describe the bug I am trying to create an app registration and a corresponding service principal. I noticed that PreAuthorizedApplications and Oauth2PermissionScopes are ignored when calling PatchAsync.

To Reproduce

var graphClient = new GraphServiceClient(new AzureCliCredential());
var (servicePrincipal, application) = await graphClient.CreateOrUpdateApplication("test123");

public static class GraphServiceClientExtensions
{
    private const string ManageResourceGroups = "ResourceGroups.Create";
    public static async Task<ServicePrincipal> CreateOrUpdateServicePrincipal(this GraphServiceClient client,
        Application application, ServicePrincipal servicePrincipal)
    {
        var servicePrincipalResult = await client.TryGetServicePrincipalExists(application.DisplayName);
        if (!servicePrincipalResult.Exists)
            return await client.ServicePrincipals.PostAsync(servicePrincipal);

        servicePrincipal.Id = servicePrincipalResult.ServicePrincipal.Id;
        await client.ServicePrincipals[servicePrincipalResult.ServicePrincipal.Id].PatchAsync(servicePrincipal);
        return servicePrincipal;
    }

    public static async Task<(ServicePrincipal servicePrincipal, Application application)> CreateOrUpdateApplication(this GraphServiceClient client, string applicationName)
    {
        var application = (await client.Applications
                              .GetAsync(x => x.QueryParameters.Filter = $"displayname eq '{applicationName}'")).Value
                          .SingleOrDefault() ??
                          await client.Applications.PostAsync(new Application { DisplayName = applicationName });

        var permissionId = Guid.NewGuid().ToString();

        if (!application.Api.PreAuthorizedApplications.Any() && !application.Api.Oauth2PermissionScopes.Any())
        {
            application.Api = new ApiApplication
            {
                PreAuthorizedApplications = new List<PreAuthorizedApplication>(),
                Oauth2PermissionScopes = new List<PermissionScope>()
            };
        }


        if (!application.Api.Oauth2PermissionScopes.Exists(x => x.Value == "user_impersonation"))
        {
            application.Api.Oauth2PermissionScopes.Add(new()
            {
                Id = permissionId,
                AdminConsentDescription = "AdminConsentDescription",
                AdminConsentDisplayName = "AdminConsentDisplayName",
                IsEnabled = true,
                Type = "User",
                UserConsentDescription = "UserConsentDescription",
                UserConsentDisplayName = "UserConsentDisplayName",
                Value = "user_impersonation"
            });
        }
        else
        {
            permissionId = application.Api.Oauth2PermissionScopes.Single(x => x.Value == "user_impersonation").Id;
        }

        if (!application.AppRoles.Exists(x => x.Value == ManageResourceGroups))
        {
            application.AppRoles = new List<AppRole>();
            application.AppRoles.Add(new()
            {
                Id = Guid.NewGuid().ToString(),
                DisplayName = "Manage Resource Groups",
                Description = "Manage Resource Groups",
                IsEnabled = true,
                Origin = "Application",
                Value = ManageResourceGroups,
                AllowedMemberTypes = new List<string> { "Application", "User" },
            });
        }

        application.IdentifierUris = new List<string> { $"api://{application.AppId}" };

        await client.Applications[application.Id].PatchAsync(application);

        // TODO PreAuthorizedApplications does not provisioning anymore 
        // Maybe it is a bug in the preview version
        application.Api.PreAuthorizedApplications = new List<PreAuthorizedApplication>
        {
            new()
            {
                // Azure CLI
                AppId = "04b07795-8ddb-461a-bbee-02f9e1bf7b46",
                DelegatedPermissionIds = new List<string> { permissionId }
            }
        };
        await client.Applications[application.Id].PatchAsync(application);

        var servicePrincipal = await client.CreateOrUpdateServicePrincipal(application, new ServicePrincipal
        {
            DisplayName = application.DisplayName,
            AppId = application.AppId,
            AppRoleAssignmentRequired = true,
            Tags = new List<string> { "HideApp" }
        });

        return (servicePrincipal, application);
    }

    public class ServicePrincipalResult
    {
        public ServicePrincipalResult(bool exists, ServicePrincipal? servicePrincipal)
        {
            Exists = exists;
            ServicePrincipal = servicePrincipal;
        }

        [MemberNotNullWhen(true, nameof(ServicePrincipal))]

        public bool Exists { get; }
        public ServicePrincipal? ServicePrincipal { get; }
    }

    public static async Task<ServicePrincipalResult> TryGetServicePrincipalExists(this GraphServiceClient client,
        string displayName)
    {
        var principals = (await client.ServicePrincipals.GetAsync(x =>
                x.QueryParameters.Filter = $"displayName eq '{displayName}'")
            ).Value;

        return new ServicePrincipalResult(principals.Any(), principals.SingleOrDefault());
    }
}

Expected behavior Authorized client applications should show Azure Cli (04b07795-8ddb-461a-bbee-02f9e1bf7b46) with scope api://{AppId}/user_impersonation in Azure portal or when I retrieve the application from Graph API PreAuthorizedApplications and Oauth2PermissionScopes should not be empty.

Additional information I noticed that when I create new instances for Api, PreAuthorizedApplications and Oauth2PermissionScopes, then at least Oauth2PermissionScopes seem to work correctly.

application.Api = new ApiApplication
{
  PreAuthorizedApplications = new List<PreAuthorizedApplication>(),
  Oauth2PermissionScopes = new List<PermissionScope>()
};

Rookian avatar Aug 16 '22 08:08 Rookian