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 locationException: 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:
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?
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.