0

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.

8
  • It seems that the passed access token is not valid. You can check the access token at jwt.ms or jwt.io to validate if token is valid to create the GraphServiceClientobject and contains the graph scope. Commented Oct 28, 2019 at 5:00
  • Could you tell me how you get access token? Commented Oct 28, 2019 at 5:04
  • Hi Jim Xu. Access token is valid. I tested in jwt.io. Commented Oct 28, 2019 at 5:15
  • I am getting access token through swagger. I have added my startup file above. Commented Oct 28, 2019 at 5:17
  • Could you please tell me if you can use the access token to call the graph api in the postman? For more details, please refer to learn.microsoft.com/en-us/graph/use-postman Commented Oct 28, 2019 at 5:48

1 Answer 1

2

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 .

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.