1

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:

  • MyAPI - exposes two "scopes" (in "Expose an API") - one for regular users, one for admins
  • MyApp - use the MyAPI as "API permission"

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?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • options.CallbackPath = "/localhost:51233/signin-oidc"; does not look like a valid URL. You should have it with HTTP or HTTPS and it must exactly match in your Identity provider.. – Tore Nestenius Jun 22 '21 at 11:25
  • @ToreNestenius: **I tried!** and got a message: "System.ArgumentException: 'The path in 'value' must start with '/'. (Parameter 'value')' " ...... – marc_s Jun 22 '21 at 11:29
  • See this question https://techcommunity.microsoft.com/t5/apps-on-azure/aadsts50011-the-reply-url-specified-in-the-request-doesn-t-match/m-p/1612361 (About the UseForwardedHeaders) – Tore Nestenius Jun 22 '21 at 11:56
  • also see https://stackoverflow.com/questions/50011686/aadsts50011-the-reply-url-specified-in-the-request-does-not-match-the-reply-url – Tore Nestenius Jun 22 '21 at 11:57
  • @ToreNestenius: thanks - but none of these seem to help in my case :-( – marc_s Jun 22 '21 at 12:02
  • are you running localhost in the cloud? or locally on your dev machine? – Tore Nestenius Jun 22 '21 at 12:07
  • No, locally on my dev box – marc_s Jun 22 '21 at 12:15
  • Have you tried using ""/signin-oidc" as the redirect URL? – Tore Nestenius Jun 22 '21 at 12:33
  • @ToreNestenius: I am trying that right now - but whatever I do - I keep running into that "Correlation failed" error ..... – marc_s Jun 22 '21 at 13:10
  • The Correlation sounds like that the client instance that initiated the authentication (openid connect handler) is not the same as the one that handles it on the way back (the call to /signin-oidc) This must be the same. Are you sure about that? – Tore Nestenius Jun 22 '21 at 13:36
  • @ToreNestenius: I only have one single web app - so yeah, I'm pretty sure the initiator and the callback handler are one and the same app .... – marc_s Jun 22 '21 at 13:43
  • Are you using HTTPS? It seems that some cookies might get lost on the way. "This error happens on the client and typically means that the correlation cookie is not set - or for some reasons has not been sent. You need to check your network traces if you can find a reason why the cookie got "lost". " – Tore Nestenius Jun 22 '21 at 13:46
  • In the initial authentication request to AzureID, you will find the redirect-Uri parameter, this must be exactly the same as the one you register in AzureID. Is that ok? – Tore Nestenius Jun 22 '21 at 13:48
  • also, are you sure you only got one AddOpenIDConnect in your startup class? see https://github.com/aspnet/Security/issues/1510 – Tore Nestenius Jun 22 '21 at 13:50

2 Answers2

1

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.

Tore Nestenius
  • 16,431
  • 5
  • 30
  • 40
0

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

Iria
  • 433
  • 1
  • 8
  • 20
  • The port is correct in the Azure App Registration and my code - was just a typo in my post here – marc_s Jun 22 '21 at 11:19
  • ANd I also turned off the implicit grant settings - still I get the same error ... – marc_s Jun 22 '21 at 11:20
  • when you set the callback, you do "/...", I think you should remove / – Iria Jun 22 '21 at 12:09
  • I can't - since I got this error: ""System.ArgumentException: 'The path in 'value' **must start with '/'**. (Parameter 'value')' "" – marc_s Jun 22 '21 at 12:15