i am learning ASP.NET and IndentityServer4 I made a server that generates a token for me according to the tutorial, I get it both from the postman and from the controller of my "client" but when I try to make a request to the "backend" where authorization is required, I get an error 401, I don't understand what I did wrong
IdentityServer
public static class Configuration
{
public static IEnumerable<ApiScope> ApiScopes => new List<ApiScope>
{
new ApiScope("NotesWebAPI", "Web API")
};
public static IEnumerable<IdentityResource> IdentityResources =>
new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
public static IEnumerable<ApiResource> ApiResources =>
new List<ApiResource>
{
new ApiResource("NotesWebAPI", "Web API", new []{ JwtClaimTypes.Name })
{
Scopes = { "NotesWebAPI" }
}
};
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "client_id",
ClientSecrets = { new Secret("client_secret".ToSha256()) },
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes =
{
"NotesWebAPI",
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
}
},
};
}
Starttup IndetityServer
public class Startup
{
IConfiguration AppConfiguration { get; }
public Startup(IConfiguration config)
{
AppConfiguration = config;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<AppUser, IdentityRole>(config =>
{
config.Password.RequiredLength = 6;
config.Password.RequireDigit = false;
config.Password.RequireNonAlphanumeric = false;
config.Password.RequireUppercase = false;
config.Password.RequireLowercase = false;
})
.AddEntityFrameworkStores<AuthDbContext>()
.AddDefaultTokenProviders();
services.AddDbContext<AuthDbContext>(options =>
options.UseSqlServer(
AppConfiguration.GetConnectionString("DefaultConnection")));
services.AddIdentityServer()
.AddAspNetIdentity<AppUser>()
.AddInMemoryApiResources(Configuration.ApiResources)
.AddInMemoryIdentityResources(Configuration.IdentityResources)
.AddInMemoryApiScopes(Configuration.ApiScopes)
.AddInMemoryClients(Configuration.Clients)
.AddDeveloperSigningCredential();
services.ConfigureApplicationCookie(config =>
{
config.Cookie.Name = "Notes.Identity.Cookie";
//config.LoginPath = "/Auth/Login";
//config.LogoutPath = "/Auth/Logout";
});
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(env.ContentRootPath, "Styles")),
RequestPath = "/styles"
});
app.UseRouting();
app.UseIdentityServer();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
my "backend"
private IConfiguration Configuration { get; }
public Startup(IConfiguration config)
{
Configuration = config;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(config =>
{
config.AddProfile(new AssemblyMappingProfile(Assembly.GetExecutingAssembly()));
config.AddProfile(new AssemblyMappingProfile(typeof(INotesDbContext).Assembly));
});
services.AddApplication();
services.AddPersistence(Configuration);
services.AddControllers();
services.AddCors(config =>
{
config.AddPolicy("DefaultPolicy",
builder =>
builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, config =>
{
config.TokenValidationParameters = new TokenValidationParameters
{
ClockSkew = TimeSpan.FromMinutes(1),
ValidateAudience = false
};
config.Authority = "https://localhost:5001";
config.Audience = "NotesWebAPI";
//config.Audience = "https://localhost:5001";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCustomExceptionHandler();
app.UseRouting();
app.UseHttpsRedirection();
app.UseCors("DefaultPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
"client" controller
[Route("[action]")]
public async Task<IActionResult> GetNotes()
{
var authClient = _httpClientFactory.CreateClient();
var discoverDocument = await authClient.GetDiscoveryDocumentAsync("http://localhost:5000");
var tokenResponse = await authClient.RequestClientCredentialsTokenAsync(
new ClientCredentialsTokenRequest
{
Address = discoverDocument.TokenEndpoint,
ClientId = "client_id",
ClientSecret = "client_secret",
Scope = "NotesWebAPI",
});
var requestClient = _httpClientFactory.CreateClient();
requestClient.SetBearerToken(tokenResponse.AccessToken);
var responce = await requestClient.GetAsync("http://localhost:1321/api/note");
if (!responce.IsSuccessStatusCode)
{
ViewBag.Message = responce.StatusCode.ToString();
return View();
}
var message = await responce.Content.ReadAsStringAsync();
ViewBag.Message = message;
return View();
}
UPDATED: If I send a request through Postman then it goes through, it doesn't go through only the "client" request
CodePudding user response:
One thing you can do to troubleshoot API authentication errors is to enable this flag:
.AddJwtBearer(opt =>
{
...
opt.IncludeErrorDetails = true;
Then when it fails, it will add the WWW-Authenticate response header, that can give you some clues about the problem:
HTTP/1.1 401 Unauthorized
Date: Sun, 02 Aug 2020 11:19:06 GMT
WWW-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid"