IdentityServer4: HttpContext GetToken пусто, но токен доставлен с сервера - PullRequest
0 голосов
/ 26 апреля 2019

Теперь мне удалось настроить приложение IdentityServer и отдельное клиентское приложение MVC, где IdentityServer использует пользовательскую базу данных приложения MVC. Приложение MVC используется для регистрации пользователей, входа в систему, выхода из системы и вызова дополнительных API.

Я могу войти в приложение MVC, а также получить верный токен от IdentityServer. Но сейчас у меня две проблемы:

  1. Токен в HttpContext имеет значение null, хотя IdentityServer успешно выдает токен. Но оба следующих варианта не работают, когда я пытаюсь получить доступ к токену из HttpContext:
var accessToken = await HttpContext.GetTokenAsync("access_token");
var accessToken = await HttpContext.GetTokenAsync(IdentityConstants.ExternalScheme, "access_token");

Может быть причина в том, что я запускаю оба проекта с помощью "запуска по сети", а не с IIS в VS2017?

  1. Где или как сохранить токен для клиента для будущих вызовов API? В идеале токен отправляется автоматически при каждом запросе. Я забыл конфигурацию здесь?

Вот так выглядят мои конфигурации:

Приложение IdentityServer:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    var builder = services.AddIdentityServer(options =>
    {
        options.Events.RaiseErrorEvents = true;
        options.Events.RaiseInformationEvents = true;
        options.Events.RaiseFailureEvents = true;
        options.Events.RaiseSuccessEvents = true;
    })
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApis())
        .AddInMemoryClients(Config.GetClients())
        .AddAspNetIdentity<ApplicationUser>();

    if (Environment.IsDevelopment())
    {
        builder.AddDeveloperSigningCredential();
    }
    else
    {
        throw new Exception("need to configure key material");
    }
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseStaticFiles();
    app.UseIdentityServer();
    app.UseMvcWithDefaultRoute();
}

Config.cs

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
    };
}

public static IEnumerable<ApiResource> GetApis()
{
    return new List<ApiResource>
    {
        new ApiResource("api1", "My API")
    };
}

public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        new Client
        {
            ClientId = "mvc",
            ClientName = "MVC Client",
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },

            RedirectUris           = { "http://localhost:5002/signin-oidc" },
            PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

            AllowedScopes =
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                "api1"
            },

            AllowOfflineAccess = true
        }
    };
}

Клиентское приложение MVC:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddDefaultIdentity<IdentityUser>()
        .AddDefaultUI(UIFramework.Bootstrap4)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    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 = "mvc";
            options.ClientSecret = "secret";
            options.ResponseType = "code id_token";

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

            options.Scope.Add("api1");
            options.Scope.Add("offline_access");

            options.ClaimActions.MapJsonKey("website", "website");
        });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseAuthentication();

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseMvcWithDefaultRoute();
}

Тестовый контроллер

public async Task<IActionResult> LoginTest()
{
    var client = new HttpClient();
    var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
    if (disco.IsError)
    {
        Console.WriteLine(disco.Error);
    }

    var tokenClient = new TokenClient(disco.TokenEndpoint, "mvc", "secret");
    var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("myusername", "mypassword");

    if (tokenResponse.IsError)
    {
        Console.WriteLine(tokenResponse.Error);
    }

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


    //var accessToken = await HttpContext.GetTokenAsync("access_token");
    var accessToken = await HttpContext.GetTokenAsync(IdentityConstants.ExternalScheme, "access_token");

    return View();
}

tokenResponse успешно заполнен:

info: IdentityServer4.Events.DefaultEventService[0]
{
"Name": "Token Issued Success",
"Category": "Token",
"EventType": "Success",
"Id": 2000,
"ClientId": "mvc",
"ClientName": "MVC Client",
"Endpoint": "Token",
"SubjectId": "08be5c35-5593-49f4-999b-e5ddd694f2e9",
"Scopes": "openid profile api1 offline_access",
"GrantType": "password",
"Tokens": [
  {
    "TokenType": "refresh_token",
    "TokenValue": "****55a0"
  },
  {
    "TokenType": "access_token",
    "TokenValue": "****YuFw"
  }
],
"ActivityId": "0HLMAGC28PO61:00000001",
"TimeStamp": "2019-04-26T20:09:27Z",
"ProcessId": 21208,
"LocalIpAddress": "::1:5000",
"RemoteIpAddress": "::1"
}

Я новичок в IdentityServer, поэтому я разработал SPA с WebAPI и сохранил JWT в локальном хранилище. Я думаю, что есть более удобный способ сделать это с клиентом MVC и IdentityServer.

1 Ответ

0 голосов
/ 29 апреля 2019

Когда вы зарегистрировали свое промежуточное программное обеспечение OIDC с

options.ResponseType = "code id_token";

вам не нужен контроллер входа в ваше клиентское приложение. Промежуточное программное обеспечение перенаправит вашего пользователя на страницу входа, также известную как Авторизовать конечную точку IdentityServer. Просто добавьте атрибут Authorize к любому защищенному контроллеру, и внутри у вас должно быть:

string accessToken = await HttpContext.GetTokenAsync("access_token");
string idToken = await HttpContext.GetTokenAsync("id_token");

В вашем примере метод LoginTest() - это прямой вызов конечной точки токена, который не использует промежуточное программное обеспечение OIDC и, таким образом, не регистрирует пользователя (сеанс пользователя не создан), а просто проверяет предоставленные вами учетные данные и создает их. жетоны. Чтобы использовать все функции протокола, вам необходимо изменить

AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

до

AllowedGrantTypes = GrantTypes.Hybrid, //or .Code

и все

...