Один IdentityServer для многих API - PullRequest
0 голосов
/ 05 апреля 2020

У меня есть проект IdentiyServer с этой конфигурацией:

public static class Config
{
    public static IEnumerable<ApiResource> Apis =>
        new List<ApiResource>
        {
            new ApiResource("api1", "My API 1"),
            new ApiResource("api2", "My API 2"),
            new ApiResource("api3", "My API 3"),
            new ApiResource("api4", "My API 4")
        };

    public static IEnumerable<Client> Clients =>
        new List<Client>
        {
            new Client
            {
                ClientId          = "client",
                AllowedGrantTypes = GrantTypes.ClientCredentials,    // no interactive user, use the client id/secret for authentication
                ClientSecrets     = {new Secret("secret".Sha256())}, // secret for authentication
                AllowedScopes     = {"api1"}                         // scopes that client has access to
            },
            new Client
            {
                ClientId          = "client-da-focus",
                AllowedGrantTypes = GrantTypes.ClientCredentials,
                ClientSecrets     = {new Secret("secret-da-focus".Sha256())},
                AllowedScopes     = {"api2", "api3"}
            },
            new Client
            {
                ClientId          = "client-da-carpark",
                AllowedGrantTypes = GrantTypes.ClientCredentials,
                ClientSecrets     = {new Secret("secret-da-carpark".Sha256())},
                AllowedScopes     = {"api4"}
            }
        };
}

Вот запуск:

public class Startup
{
    public IWebHostEnvironment Environment { get; }

    public Startup(IWebHostEnvironment environment)
    {
        Environment = environment;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        var builder = services.AddIdentityServer()
                              .AddInMemoryApiResources(Config.Apis)
                              .AddInMemoryClients(Config.Clients);

        builder.AddDeveloperSigningCredential();
    }

    public void Configure(IApplicationBuilder app)
    {
        if (Environment.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseIdentityServer();
    }
}

У меня есть другой проект API с контроллером Identity. Вот первый запуск:

publi c class Startup {publi c Startup (конфигурация IConfiguration) => Configuration = configuration;

    public IConfiguration Configuration { get; }

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

        services.AddAuthentication("Bearer")
                .AddJwtBearer("Bearer", options =>
                 {
                     options.Authority = Configuration["identity:discovery:document"];
                     options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                     {
                         ValidateIssuer = true,
                         ValidAudiences = new List<string>
                         {
                             "api1",
                             "api2",
                             "api3",
                             "api4"
                         }
                     };
                 });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

И контроллер:

[Route("identity")]
[Authorize]
public class IdentityController : ControllerBase
{
    [HttpGet]
    public IActionResult Get() => new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}

Теперь я предполагаю, что поскольку все четыре ValidAudience добавлены и любой клиент, вызывающий API контроллера идентификации, получу и все в порядке.

Вместо этого я получаю 401 .

Мой третий проект, который является клиентом, настроен следующим образом:

App.cs:

public class App
{
    private readonly IConfigurationRoot _config;
    private readonly ILogger<App>       _logger;

    public App(IConfigurationRoot config, ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<App>();
        _config = config;
    }

    public async Task Run()
    {
        var appConfig = _config.GetSection("identity").Get<Identity>();

        // discover endpoints from metadata
        var client = new HttpClient();

        var disco = await client.GetDiscoveryDocumentAsync(appConfig.Discovery.Document);
        if (disco.IsError)
        {
            _logger.LogError($"Discovery Error: {disco.Error}");
            return;
        }

        // request token
        var tokenResponse = await client.RequestClientCredentialsTokenAsync(
                                new ClientCredentialsTokenRequest
                                {
                                    Address      = disco.TokenEndpoint,
                                    ClientId     = appConfig.Token.Request.ClientId,
                                    ClientSecret = appConfig.Token.Request.ClientSecret,
                                    Scope        = appConfig.Token.Request.Scope
                                });

        if (tokenResponse.IsError)
        {
            _logger.LogError($"Token Error: {tokenResponse.Error}");
            return;
        }

        Console.WriteLine(tokenResponse.Json);
        Console.WriteLine("\n");

        // call api
        var apiClient = new HttpClient();
        apiClient.SetBearerToken(tokenResponse.AccessToken);

        var response = await apiClient.GetAsync(appConfig.Api.Url);
        if (!response.IsSuccessStatusCode)
        {
            _logger.LogWarning($"Api Response: {response.StatusCode}");
        }
        else
        {
            var content = await response.Content.ReadAsStringAsync();
            Console.WriteLine(JArray.Parse(content));
            Console.WriteLine("\n");
        }

        Console.ReadLine();
    }
}

У меня правильно настроены URL-адреса в настройках :

{
  "identity": {
    "discovery": {
      "document": "https://localhost:44377"
    },
    "token": {
      "request": {
        "clientId": "client-da-focus",
        "clientSecret": "secret-da-focus",
        "scope": "api2"
      }
    },
    "api": {
      "url": "https://localhost:44380/identity"
    }
  },
  ...
}

Обратите внимание, что я собираюсь использовать только определенный API (# 2).

Когда App.cs пытается var disco = await client.GetDiscoveryDocumentAsync(appConfig.Discovery.Document);, он просто остается там неопределенно долго. После отладки я заметил 401.

Почему сервер IdentityServer4 не авторизует вызов API?

...