64

I'm trying to upgrade a project from .Net core 1.1 to .Net core 2.0 there's a lot of breaking changes.

One of the things I'm currently having an issue with is that HttpContext.Authentication is now obsolete.

I've been trying to figure out how to get the Access token for the current request. I need to make a call to another API which requires a bearer token.

Old Method .Net core 1.1

[Authorize]
public async Task<IActionResult> ClientUpdate(ClientModel client)
{
    var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token");
    return View();
}

Method .Net core 2.0

This is not working becouse context isnt registered.

[Authorize]
public async Task<IActionResult> ClientUpdate(ClientModel client)
{
    var accessToken = await context.HttpContext.GetTokenAsync("access_token"); 
    return View();
}

Unable to resolve service for type 'Microsoft.AspNetCore.Http.HttpContext'

I tried registering it but that doesnt work either

public ConsoleController(IOptions<ServiceSettings> serviceSettings, HttpContext context) 

In startup.cs

services.TryAddSingleton<HttpContext, HttpContext>();

Update:

This returns null

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

Startup.cs ConfigureServices

I wouldn't be surprised if it was something in the startup as there were a lot of breaking changes here as well.

services.Configure<ServiceSettings>(Configuration.GetSection("ServiceSettings"));
//services.TryAddSingleton<HttpContext, HttpContext>();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddMvc();
services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie()
        .AddOpenIdConnect(options =>
        {
            options.Authority = "http://localhost:5000";
            options.ClientId = "testclient";
            options.ClientSecret = "secret";
            options.ResponseType = "code id_token";
            options.RequireHttpsMetadata = false;
            options.GetClaimsFromUserInfoEndpoint = true;
        });

Startup.cs Configure

loggerFactory.AddDebug();
if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseBrowserLink();
}
else
{
    app.UseExceptionHandler("/Home/Error");
}
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});
Mohamad Mousheimish
  • 1,555
  • 2
  • 16
  • 44
DaImTo
  • 88,623
  • 26
  • 153
  • 389
  • You don't have to register `HttpContext`. It's already available in your controller classes just like in 1.1. – Brad Apr 11 '18 at 07:23
  • @Brad thats what i thought but i am not able to access it please describe what you mean. – DaImTo Apr 11 '18 at 07:24
  • Is your controller inheriting from class `Controller`? – Brad Apr 11 '18 at 07:27
  • If `GetAccessToken()` returns null it's likely an issue in your authentication config. How have you configured authentication in Startup? – Brad Apr 11 '18 at 07:29
  • 1
    ClientUpdate is in the same ConsoleController so i would have to say yes? – DaImTo Apr 11 '18 at 07:30
  • @Brad that wouldn't surprised me at all. edit with startup stuff. I am connecting to a local Identityserver4 project. – DaImTo Apr 11 '18 at 07:33
  • That doesn't show your authentication configuration. – Brad Apr 11 '18 at 11:35
  • authentication configuration? – DaImTo Apr 11 '18 at 11:37
  • @DaImTo https://stackoverflow.com/questions/63971562/how-to-create-project-in-google-console-cloud-platform-using-php-lib-programmati here is my ques, replated to your skills, please ans. i will be thankful to you – Bob Kris Sep 19 '20 at 17:58

8 Answers8

98

.Net core 2.1 to access JWT bearer token

var accessToken = Request.Headers[HeaderNames.Authorization];
Tobias
  • 1,920
  • 2
  • 23
  • 48
Chpn Dave
  • 1,181
  • 1
  • 8
  • 4
  • How is it possible that my header is empty? It passes my `[Authorize]` Attribute, though... Is it possible that it's in a header called `Cookie`? – El Mac Feb 20 '19 at 07:17
  • 3
    @El Mac apparently, I was missing `options.SaveTokens = true` in the `AddOpenIdConnect` method inside `Startup.ConfigureServices`. https://stackoverflow.com/a/50623141/2632991 – El Mac Feb 20 '19 at 07:32
  • @ElMac depends on where you store the token in the request. The token should be in the Authorization header for the above code to work. – Enrico Feb 28 '19 at 12:57
  • 3
    This doesn't return just the token. It also include the auth scheme from the header ("Bearer "), so if you want just the token you have to extract it – Thomas Levesque Nov 23 '19 at 23:23
  • 1
    Is there any way to NOT hard-code the `"Authorization"` string? Is that string defined somewhere as a constant or enum? – Tobias Feb 04 '20 at 10:42
  • 1
    @Tobias [Are there any constants for the default HTTP headers?](/questions/11037004/are-there-any-constants-for-the-default-http-headers) – Wes Toleman Apr 07 '20 at 07:33
  • 1
    @WesToleman Great, thanks! I updated the answer to use `HeaderNames.Authorization` instead of the hard-coded string `"Authorization"`. Unfortunately, in my case this doesn't help because I'd like to use it with an attribute (`[FromHeader(Name = HeaderNames.Authorization)]`) which is not supported because `HeaderNames.Authorization` is not a `const` but a `static readonly`. :-( – Tobias Apr 08 '20 at 08:48
  • 5
    Where does the `Request` come from? – manymanymore May 20 '20 at 20:24
  • @Tobias you can use it like this [FromHeader(Name = nameof(HeaderNames.Authorization))] :) – Eugene Chybisov Aug 20 '20 at 16:56
  • @EugeneChybisov That would work, yes, but from a design perspective it does not make sense. It just uses the name of the variable instead of its content. It only works because name & content are equal in this case. But you want to use the content here. So I would rather hard-code "Authorization" than to use the variable name of the `Authorization` constant. ;-) – Tobias Aug 21 '20 at 19:52
  • @manymanymore `Request` is property inside [ControllerBase](https://docs.microsoft.com/en-us/dotnet/api/Microsoft.AspNetCore.Mvc.ControllerBase.Request?view=aspnetcore-5.0&viewFallbackFrom=netcore-5.0) in .NET Core – Sanderr Nov 01 '21 at 15:10
32

if you want the pure token this can help you in .net core 3.1

var _bearer_token = Request.Headers[HeaderNames.Authorization].ToString().Replace("Bearer ", "");

and remember you need to add this using

using Microsoft.Net.Http.Headers;
Diego
  • 439
  • 4
  • 4
30

It ended up being a configuration issue. There needs to be a link between AddAuthentication and AddOpenIdConnect in order for it to read the cookie into the headers.

services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
            .AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
            {
                options.SignInScheme = "Cookies";

                options.Authority = "http://localhost:5000";
                options.RequireHttpsMetadata = false;

                options.ClientId = "testclient";
                options.ClientSecret = "secret";
                options.ResponseType = "code id_token";
                options.SaveTokens = true;
                options.GetClaimsFromUserInfoEndpoint = true;

                options.Scope.Add("testapi");
                options.Scope.Add("offline_access");
            });

Controller

    [Authorize]
    public async Task<IActionResult> Index()
    {
        var accessToken = await HttpContext.GetTokenAsync("access_token");
        return View();
    }

Access token is now populated.

Note: I ended up digging it out of this project Startup.cs

DaImTo
  • 88,623
  • 26
  • 153
  • 389
  • 1
    You don't need `IHttpContextAccessor` in your controller. It's already there as the `HttpContext` property. – Brad Apr 11 '18 at 11:34
  • 2
    It was! Your configuration was incorrect as this answer clearly suggests. You would have been getting `NullReferenceException` if `HttpContext` wasn't populated. And this is what i meant by authentication configuration! – Brad Apr 11 '18 at 23:11
23

In Controller, the token can be retrieved by reading Request.Headers dictionary:

 var accessToken = Request.Headers["Authorization"];

At other classes where HttpContext is not available, there token can be retrieved using HttpContextAccessor after injecting into services collection ( A little change from Azharuddin answer)

Register the service instance in Startup method like

public void ConfigureServices(IServiceCollection services)
{

 services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
 ...
}

And inject the dependency in your controller like

private IHttpContextAccessor _httpContextAccessor;
public ClientController(IHttpContextAccessor httpContextAccessor)
{
     _httpContextAccessor = httpContextAccessor;
}

And retrieve the access token in your action like

[Authorize]
public async Task<IActionResult> ClientUpdate(ClientModel client)
{
    var accessToken = _httpContextAccessor.HttpContext.Request.Headers["Authorization"];

    ..........//Some other code
    return View();
}
user1672994
  • 9,221
  • 1
  • 16
  • 31
4

Startup.cs:

 public void ConfigureServices(IServiceCollection services)
 {
    ...
     services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    ...
 }

Controller Constructor:

private IHttpContextAccessor _httpContextAccessor;
public ClientController(IHttpContextAccessor httpContextAccessor)
{
     _httpContextAccessor = httpContextAccessor;
}

[Authorize]
public async Task<IActionResult> ClientUpdate(ClientModel client)
{
    var accessToken = await _httpContextAccessor.HttpContext.GetTokenAsync("access_token");
    return View();
}

This should work

Mohamad Mousheimish
  • 1,555
  • 2
  • 16
  • 44
  • Works for me with var token = HttpContext.Request.Headers["Authorization"][0]; @user1672994 made a small typo Authorziation should be Authorization – Enrico Feb 28 '19 at 12:53
1

You need to specify the external schema to retrieve the token.

var accessToken = await HttpContext.GetTokenAsync(IdentityConstants.ExternalScheme, "access_token");
William Magno
  • 434
  • 4
  • 6
0

Real thanks, this is perfect !

I had this work, but with our azure tenant dedicated authority. Simply replace ****** with your tenant name.

options.Authority = "https://login.microsoftonline.com/******.onmicrosoft.com";

You also can use tenant id. Simply insert your tenant id after https://login.microsoftonline.com/

options.Authority = "https://login.microsoftonline.com/be0be093-****-****-****-5626e83beefc";
Albo
  • 1
  • 1
-2

it should be HttpContext.GetTokenAsync("access_token").Result.ToString();

bi2mon
  • 1
  • 1