Niranjan
Niranjan

Reputation: 2229

Unable to call Graph API in .net core

net core application. I am trying to call Graph API in my application. Below is my code. This is the policy I applied on top of Controller.

[Authorize(Policy = "APGroupsOnly")] 

Below policy I added in startup.

services.AddAuthorization(options =>
{
    options.AddPolicy("APGroupsOnly", policy =>
           policy.Requirements.Add(new GroupsCheckRequirement("YourGroupID")));
});

I am trying to hit apis from swagger. Below is my swagger config.

"ClientId": "my client id",
"ClientSecret": "my client secrete",
"AuthorizationUrl": "https://login.microsoftonline.com/myid/oauth2/authorize",
"TokenUrl": "https://login.microsoftonline.com/myid/oauth2/token"

Below is my MSGraphService.cs

public async Task<User> GetMeAsync(string accessToken)
{
    User currentUserObject;

    try
    {
        PrepareAuthenticatedClient(accessToken);
        currentUserObject = await graphServiceClient.Me.Request().GetAsync();
     }
     catch (ServiceException e)
     {
         Debug.WriteLine("We could not fetch details of the currently signed-in user: " + $"{e}");
         return null;
     }

    return currentUserObject;
}

private void PrepareAuthenticatedClient(string accessToken)
{
    if (graphServiceClient == null)
    {
        // Create Microsoft Graph client.
        try
        {
            graphServiceClient = new GraphServiceClient("https://graph.microsoft.com/.default",
                new DelegateAuthenticationProvider(
                    async (requestMessage) =>
                    {
                        await Task.Run(() =>
                        {
                            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
                        });
                    }));
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"Could not create a graph client {ex}");
        }
    }
}

Below is my GroupsCheckHandler

public class GroupsCheckHandler : AuthorizationHandler<GroupsCheckRequirement>
{
    private IHttpContextAccessor _httpContextAccessor;
    private readonly IMSGraphService graphService;
    public GroupsCheckHandler(IHttpContextAccessor httpContextAccessor, IMSGraphService MSGraphService)
    {
        _httpContextAccessor = httpContextAccessor;
        this.graphService = MSGraphService;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, GroupsCheckRequirement requirement)
    {
        var accessToken = _httpContextAccessor.HttpContext.Request.Headers["Authorization"];
        User me = await graphService.GetMeAsync(accessToken);
    }
}

Whenever I check execute, I get the following error.

We could not fetch details of the currently signed-in user: Status Code: Unauthorized Microsoft.Graph.ServiceException: Code: InvalidAuthenticationToken Message: CompactToken parsing failed with error code: 80049217

Below is my startup file.

 public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
            azureActiveDirectoryOptions = configuration.GetSection("AzureAd").Get<AzureActiveDirectoryOptions>();
            swaggerUIOptions = configuration.GetSection("Swagger").Get<SwaggerUIOptions>();
        }

        public IConfiguration Configuration { get; }

        private readonly AzureActiveDirectoryOptions azureActiveDirectoryOptions;
        private readonly SwaggerUIOptions swaggerUIOptions;
        //
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddTransient<IMSGraphService, MSGraphService>();
            services
               .AddAuthentication(o =>
               {
                   o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;

               })
               .AddJwtBearer(o =>
               {
                   o.Authority = azureActiveDirectoryOptions.Authority;

                   o.TokenValidationParameters = new TokenValidationParameters
                   {

                       ValidAudiences = new List<string>
                       {
                          azureActiveDirectoryOptions.AppIdUri,
                          azureActiveDirectoryOptions.ClientId
                       },
                       ValidateIssuer = true,
                       ValidateAudience = true,
                       ValidIssuer = "https://myorg.onmicrosoft.com/oauth2/default",
                       RoleClaimType = ClaimTypes.Role
                   };
               });

            services.AddMvc(options =>
            {

                var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
                options.Filters.Add(new AuthorizeFilter(policy));
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1); ;

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });

                c.AddSecurityDefinition("oauth2", new OAuth2Scheme
                {
                    Type = "oauth2",
                    Flow = "implicit",
                    AuthorizationUrl = swaggerUIOptions.AuthorizationUrl,
                    TokenUrl = swaggerUIOptions.TokenUrl,
                    Scopes = new Dictionary<string, string>
                    {
                        {"Read", "13469a45-a2ea-45a1-96e7-6580f57b6e30/.default" }
                    }
                });
                c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
                {
                        { "oauth2", new[] { "readAccess", "writeAccess" } }
                });
            });

            services.AddAuthorization(options =>
            {
                options.AddPolicy("APGroupsOnly", policy =>
                       policy.Requirements.Add(new GroupsCheckRequirement("YourGroupID")));
            });
            services.AddScoped<IAuthorizationHandler, GroupsCheckHandler>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.RoutePrefix = "swagger";
                c.OAuthClientId(swaggerUIOptions.ClientId);
                c.OAuthClientSecret(swaggerUIOptions.ClientSecret);
                c.OAuthRealm(azureActiveDirectoryOptions.ClientId);
                c.OAuthAppName("Swagger");
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
                c.OAuthAdditionalQueryStringParams(new Dictionary<string, string>() { { "resource", azureActiveDirectoryOptions.AppIdUri } });
            });
            app.UseAuthentication();
            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }

Can someone help me to fix this issue? Any help would be appreciated. Thanks.

Upvotes: 0

Views: 1424

Answers (1)

Nan Yu
Nan Yu

Reputation: 27588

Swagger is used to test your web api , so you can add scope to access your web api which protected by Azure AD :

Scopes = new Dictionary<string, string>
{      
    { "api://XXXXX/accessApi","api://XXXXX/accessApi"}
}

Clicking Authorize button in swagger and after user authenticated and get access token for your web api , you can test your web api in swagger . But the access token is for accessing web api that the Audience is your web api's name/url , it can't be used to make Microsoft Graph api calls . They are different resource so you need to obtain two tokens .

If you web api needs to make an authenticated request to the downstream web API (Microsoft Graph ) on behalf of user , you can use OAuth 2.0 On-Behalf-Of flow .

Another choice is your web api call Microsoft Graph with their own identity - use OAuth 2.0 client credentials grant flow to get token for accessing Microsoft Graph API . And here is code sample which using Microsoft Graph sdk .

Upvotes: 2

Related Questions