marc_s
marc_s

Reputation: 755287

Getting access token to call an API from my ASP.NET Core 5 MVC web app

I'm trying to wrap my head around how to properly authenticate a user using Azure Active Directory and OpenID Connect in my ASP.NET Core 5 app, and get the necessary access token so I can make REST requests an to API.

I have two App Registrations in Azure AD:

In my ASP.NET Core 5 MVC app, I have my OpenID Connect set up in my startup:

        services.AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddOpenIdConnect(options =>
            {
                options.SignInScheme = "Cookies";
                options.Authority = Configuration["AzureAdConfig:Authority"];
                options.RequireHttpsMetadata = true;
                options.ClientId = Configuration["AzureAdConfig:ClientId"];
                options.ClientSecret = Configuration["AzureAdConfig:ClientSecret"];
                // options.GetClaimsFromUserInfoEndpoint = true;
                options.ResponseType = OpenIdConnectResponseType.Code;
                options.UsePkce = true;
                options.Scope.Add("offline_access");
                options.Scope.Add("profile");
                options.Scope.Add("api://e968735b-d85e-4359-b364-a9637e4a920d/myapi.data.api");
                options.SaveTokens = true;
            });

This appears to work - I can definitely access my authenticated user in the home controller's Index method, and I see all the usual, expected user claims - but I don't see any access token to call "MyAPI".

In my MVC app, I have a class to access the API, which uses HttpClient - something like this:

HttpClient _httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(baseUrl);

_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
// but where and how can I get this access token I need to call "MyApi" ? 
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

I cannot seem to figure out how to get the access token, either from the original OpenID Connect call, or some other way, in order to provide is as the Bearer (token) auth token to the HttpClient making those calls.

Any ideas? I've studied sooo many blog post already, and many have outdated information, which makes it really tricky to find proper, current, and working guidance on this....

Update

I tried Tore's answer, and first I get an error:

Exception: Correlation failed.
Unknown location

Exception: An error was encountered while handling the remote login. Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()

which seems to have been caused by me not having specified a CallbackPath in my OIDC options.

So now I do:

options.CallbackPath = "/localhost:51233/signin-oidc";

and even though that exactly matches one of the defined "Redirect URL" in my Azure App Registration:

enter image description here

now I'm getting this error:

AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application: '

Also tried with https://localhost:51233... but that didn't make any difference. Any further ideas?

Upvotes: 3

Views: 12767

Answers (2)

Tore Nestenius
Tore Nestenius

Reputation: 19971

To call the API you can use:

var accessToken = await HttpContext.GetTokenAsync("access_token");

var authheader = new AuthenticationHeaderValue("Bearer", accessToken);


var client = new HttpClient();


var authheader = new AuthenticationHeaderValue("Bearer", accessToken);
client.DefaultRequestHeaders.Authorization = authheader;

var content = await client.GetStringAsync("https://localhost:7001/api/payment");

ViewBag.Json = JObject.Parse(content).ToString();
return View();

The use of options.SaveTokens = true; (In AddOpenIDConnect) will save all the tokens in the user cookie and then you can access it using:

var accessToken = await HttpContext.GetTokenAsync("access_token");

Sample code to get all the tokens if provided:

            ViewBag.access_token = HttpContext.GetTokenAsync("access_token").Result;
            ViewBag.id_token = HttpContext.GetTokenAsync("id_token").Result;
            ViewBag.refresh_token = HttpContext.GetTokenAsync("refresh_token").Result;
            ViewBag.token_type = HttpContext.GetTokenAsync("token_type").Result;    //Bearer
            ViewBag.expires_at = HttpContext.GetTokenAsync("expires_at").Result;    // "2021-02-01T10:58:28.0000000+00:00"

Are you using HTTPS? then you might have a problem with that, because if HTTPS is terminated outside your application, you run HTTPS on the public internet but internally you use HTTP. I typically host my applications as Azure Container Instances instead, because then I can send the HTTPS traffic directly to my container/application.

Upvotes: 2

Iria
Iria

Reputation: 497

I think that I know the answer and it is very simple! I have made this mistake too many times, so I fully understand it.

If you pay attention to what you have wrote, the port that is surronded by green ink is 51233, but later you have 51223, the last one obviously is not going to work because it doesn't match.

Also, it says 'implicit grant settings enabled', that's actually another error. In this case you have to consider setting another different flow like shown in https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-implicit-grant-flow in your client

Upvotes: 0

Related Questions