1

I've two projects in solution. One is for Identity management using asp.net identity generating JWT tokens. In Second project there are API's that are secure and validating token that are generated from identity project but token validation not working.

I'm getting this error when calling api store route in postman. I'm passing token in Authorization header.

An unhandled exception occurred while processing the request. HttpRequestException: Response status code does not indicate success: 404 (Not Found). System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()

IOException: IDX20804: Unable to retrieve document from: '[PII is hidden]'. Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(string address, CancellationToken cancel)

InvalidOperationException: IDX20803: Unable to obtain configuration from: '[PII is hidden]'. Microsoft.IdentityModel.Protocols.ConfigurationManager.GetConfigurationAsync(CancellationToken cancel)

Stack Query Cookies Headers HttpRequestException: Response status code does not indicate success: 404 (Not Found). System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(string address, CancellationToken cancel)

Show raw exception details System.Net.Http.HttpRequestException: Response status code does not indicate success: 404 (Not Found). at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) IOException: IDX20804: Unable to retrieve document from: '[PII is hidden]'. Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(string address, CancellationToken cancel) Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(string address, IDocumentRetriever retriever, CancellationToken cancel) Microsoft.IdentityModel.Protocols.ConfigurationManager.GetConfigurationAsync(CancellationToken cancel)

Show raw exception details System.IO.IOException: IDX20804: Unable to retrieve document from: '[PII is hidden]'. ---> System.Net.Http.HttpRequestException: Response status code does not indicate success: 404 (Not Found). at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) --- End of inner exception stack trace --- at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(String address, IDocumentRetriever retriever, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.GetConfigurationAsync(CancellationToken cancel) InvalidOperationException: IDX20803: Unable to obtain configuration from: '[PII is hidden]'. Microsoft.IdentityModel.Protocols.ConfigurationManager.GetConfigurationAsync(CancellationToken cancel) Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() Microsoft.AspNetCore.Authentication.AuthenticationHandler.AuthenticateAsync() Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, string scheme) Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Show raw exception details System.InvalidOperationException: IDX20803: Unable to obtain configuration from: '[PII is hidden]'. ---> System.IO.IOException: IDX20804: Unable to retrieve document from: '[PII is hidden]'. ---> System.Net.Http.HttpRequestException: Response status code does not indicate success: 404 (Not Found). at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) --- End of inner exception stack trace --- at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(String address, IDocumentRetriever retriever, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync(CancellationToken cancel) --- End of inner exception stack trace --- at Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync(CancellationToken cancel) at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync() at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

This is my Startup.cs class in Identity project.

using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cosmonaut;
using Cosmonaut.Extensions.Microsoft.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Triverse.Identity.Models;
using Triverse.Identity.Services;
using IdentityRole = Microsoft.AspNetCore.Identity.DocumentDB.IdentityRole;

namespace Triverse.Identity
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            var endPointUri = Configuration.GetValue<string>("AppSettings:EndpointUri");
            var primaryKey = Configuration.GetValue<string>("AppSettings:PrimaryKey");
            var databaseId = Configuration.GetValue<string>("AppSettings:DatabaseId");
            var collectionId = Configuration.GetValue<string>("AppSettings:CollectionId");

            var client = new DocumentClient(new Uri(endPointUri), primaryKey);
            services.AddSingleton<IDocumentClient>(client);

            // make sure the database exists!
            var db = client.CreateDatabaseQuery().Where(d => d.Id == databaseId).AsEnumerable().FirstOrDefault()
                     ?? client.CreateDatabaseAsync(new Database { Id = databaseId }).Result;

            var databaseLink = db.SelfLink;

            services.AddIdentityWithDocumentDBStores<ApplicationUser, IdentityRole>(
                dbOptions =>
                {
                    dbOptions.DocumentUrl = endPointUri;
                    dbOptions.DocumentKey = primaryKey;
                    dbOptions.DatabaseId = databaseId;
                    dbOptions.CollectionId = collectionId;
                },
                identityOptions =>
                {
                    identityOptions.User.RequireUniqueEmail = true;
                });

            var cosmosSettings = new CosmosStoreSettings(databaseId, endPointUri, primaryKey);
            services.AddCosmosStore<ApplicationUser>(cosmosSettings);

            services.AddScoped<IAccountRepository, AccountRepository>();
            services.AddTransient<ITokenService, TokenService>();

            services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(cfg =>
                {
                    cfg.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidIssuer = Configuration["Tokens:Issuer"],
                        ValidAudience = Configuration["Tokens:Audience"],
                        IssuerSigningKey =
                            new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"])),
                        ValidateLifetime = true
                    };

                    cfg.Events = new JwtBearerEvents
                    {
                        OnAuthenticationFailed = context =>
                        {
                            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                            {
                                context.Response.Headers.Add("Token-Expired", "true");
                                context.Response.Headers.Add("access-control-expose-headers", "Token-Expired");
                            }

                            return Task.CompletedTask;
                        }
                    };
                });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseAuthentication();

            app.UseMvc();
        }
    }
}

This is my appsettings.json in Identity project.

"Tokens": {
    "Key": "4343@!#ewewq",
    "Issuer": "http://localhost:44376/",
    "Audience": "http://localhost:44385/",
    "ExpiryMinutes": "55",
    "ValidateLifetime": true
  }

This is my Startup.cs class in Store project.

using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cosmonaut;
using Cosmonaut.Extensions.Microsoft.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Store.API.Services;

namespace Store.API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            var endPointUri = Configuration.GetValue<string>("AppSettings:EndpointUri");
            var primaryKey = Configuration.GetValue<string>("AppSettings:PrimaryKey");
            var databaseId = Configuration.GetValue<string>("AppSettings:DatabaseId");

            var client = new DocumentClient(new Uri(endPointUri), primaryKey);
            services.AddSingleton<IDocumentClient>(client);

            var db = client.CreateDatabaseQuery().Where(d => d.Id == databaseId).AsEnumerable().FirstOrDefault()
                     ?? client.CreateDatabaseAsync(new Database {Id = databaseId}).Result;

            var databaseLink = db.SelfLink;

            var cosmosSettings = new CosmosStoreSettings(databaseId, endPointUri, primaryKey);
            services.AddCosmosStore<Models.Store>(cosmosSettings);

            services.AddScoped<IStoreRepository, StoreRepository>();

            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

            }).AddJwtBearer(options =>
            {
                options.Authority = "http://localhost:44376/";
                options.RequireHttpsMetadata = false;
                options.Audience = "http://localhost:44385/";
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseAuthentication();

            app.UseMvc();
        }
    }
}

This is my controller method in Store project.

[HttpGet]
[Authorize]
public async Task<IActionResult> GetStores()
{
  var stores = new
  {
     Id = 1,
     Name = "T-Shirt",
     Price = "120.00"
  };

  return Ok(stores);
}

This is my JWT token generated from Identity project.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzaGFAZG9tYWluLmNvbSIsImp0aSI6IjFjYjNkNjA2LWI4MGQtNGNlZC1hMWFjLThlYmUzNzc1ZGViOSIsIlVuaXF1ZUlkIjoiZjU1ZTM2MWQtYjFkYy00MDg4LTlmYjQtMDg3ZTI4OTFjNWI1IiwidW5pcXVlX25hbWUiOiJzaGFAZG9tYWluLmNvbSIsImZpcnN0TmFtZSI6IlNoYXduIiwibmJmIjoxNTY0OTkwMjA1LCJleHAiOjE1NjQ5OTM1MDUsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQzNzYvIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo0NDM4NS8ifQ.ZBK8Fi14QUc9ObZx7ojg7LPcl8Qs2vrQyhZi7Dbk4Gg
11
  • Your "Store" API is trying to internally call http://localhost:44376/.well-known/openid-configuration and the call is failing. You can enable PII logs with this IdentityModelEventSource.ShowPII = true; - add it in your startup (remove it for prod). Commented Aug 5, 2019 at 8:28
  • How can i fix this? I'm using asp.net identity. Commented Aug 5, 2019 at 8:36
  • Are you hosting this locally? Enable PII first, and then you have to verify that the URL is accessible at least locally. Commented Aug 5, 2019 at 8:38
  • I've enable IdentityModelEventSource.ShowPII = true; this in startup class. URL is not accessible still. I'm not using identity servier, I'm using asp.net identity. Commented Aug 5, 2019 at 8:42
  • 2
    So what exactly is the problem in my code? Commented Aug 5, 2019 at 12:03

1 Answer 1

1

Asp.NET Identity is not intended for this use case all by itself. You might want to look something like Identity Server which extends the functionality of Identity. It allows you to validate tokens in several ways,

  1. It has an endpoint for validating tokens
  2. It exposes a well known configuration endpoint that provide (along with a lot of other things) the public key of RSA certificate used to sign the token. So if you want, you can verify the signature yourself.
Sign up to request clarification or add additional context in comments.

3 Comments

Is Identity Server support is by default in Asp.Net Core 3?
Identity Server 4 is a component that can be added to .net core. I believe that it is already working with the 3.0 preview releases but I have not tried it myself. You can check it out here, github.com/IdentityServer/IdentityServer4
Any alternatives to IS 4 now that they are starting to charge now? Or do you know if net 6 identity now supports this?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.