Search code examples
asp.net-coreoauth-2.0access-tokenidentityserver4openid-connect

IdentityServer4 - How to Call One ApiResource from Another ApiResource


I have a spa application setup that is protected with IdentityServer4 using the Hybrid Authorization flow with OpenId Connect.

This spa application is able to call multiple ApiResources, as the logged in user, by requesting the access_token via

HttpContextAccessor.HttpContext.GetTokenAsync("access_token")

This is working great for multiple ApiResources. For example, my Spa application can call ApiResource1, ApiResource2, and ApiResource3 without any problems.

However, I have added a new ApiResource (i.e. ApiResource4) and I need to be able to call it from ApiResource1. However, when I try to call

HttpContextAccessor.HttpContext.GetTokenAsync("access_token")

from ApiResource1, the access_token is null so the call fails.

How can I make a call from ApiResource1 to ApiResource2 using the logged in User's token?

Maybe a better way of asking is, how to I get the logged in users access_token from ApiResource1?

My SPA application is setup like this:

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

                    options.Authority = Configuration.GetSection("IdentityServerOptions").GetValue<string>("BaseUrl");
                    options.RequireHttpsMetadata = true;

                    options.ClientId = Configuration.GetSection("IdentityServerOptions").GetValue<string>("ClientId");
                    options.ClientSecret = Configuration.GetSection("IdentityServerOptions").GetValue<string>("ClientSecret");

                    options.SaveTokens = true;
                    options.GetClaimsFromUserInfoEndpoint = true;

                    options.Scope.Add("ApiResource1");
                    options.Scope.Add("ApiResource2");
                    options.Scope.Add("ApiResource3");
                    options.Scope.Add("ApiResource4");
                    options.ResponseType = "code id_token";
                });

My ApiResources are setup like this:

services.AddAuthentication("Bearer")
                .AddIdentityServerAuthentication(options =>
                {
                    options.Authority = Configuration.GetSection("IdentityServer").GetValue<string>("BaseUrl");
                    options.RequireHttpsMetadata = true;
                    options.ApiName = _AppName;
                });

Solution

  • The HttpContext.GetTokenAsync("access_token") extension method only works with Cookie Authentication. It will try to extract the token that is stored in the cookie. This is not available when you have an API using JWT authentication.

    You have two options.

    1. Use extension grant delegation as explained in the Identity Server docs http://docs.identityserver.io/en/release/topics/extension_grants.html#example-simple-delegation-using-an-extension-grant. This option is more complex to implement but it provides greater security.

    2. Extract and forward the JWT from the Authorization header. This option is as simple as getting the header from the Request and setting it on the headers of the HttpClient before making the API request.