77

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,641
  • 3
  • 16
  • 48
Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449

10 Answers10

114

.Net core 2.1 to access JWT bearer token

var accessToken = Request.Headers[HeaderNames.Authorization];
Tobias
  • 2,089
  • 2
  • 26
  • 51
Chpn Dave
  • 1,350
  • 2
  • 9
  • 5
  • 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
  • 6
    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://learn.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
54

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
  • 659
  • 5
  • 5
34

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

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • 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
  • 1
    Where did you get `access_token` name? Is it some well-known propertly? Can you please share reference to the docs? – Stanislav Berkov Jun 29 '22 at 13:42
  • Yes access token is standard. Its part of the response. from authorization. – Linda Lawton - DaImTo Jun 29 '22 at 14:17
29

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
  • 10,509
  • 1
  • 19
  • 32
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,641
  • 3
  • 16
  • 48
  • 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
2

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

var accessToken = await HttpContext.GetTokenAsync(IdentityConstants.ExternalScheme, "access_token");
William Magno
  • 502
  • 5
  • 6
2
string accessToken = Request.Headers[HeaderNames.Authorization].ToString().Replace($"{JwtBearerDefaults.AuthenticationScheme} ", String.Empty);
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
0

My project targeting .Net 7

string? jwtToken = HttpContext.Request.Headers.Authorization.FirstOrDefault()?.Split(" ").Last();
Husam Ebish
  • 4,893
  • 2
  • 22
  • 38
-5

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

bi2mon
  • 1
  • 1