Все еще вошли на сайт MVC, но не можете вызвать веб-API - PullRequest
0 голосов
/ 03 февраля 2019

У меня есть сайт ASP.NET MVC, хост IdentityServer4 и веб-API.

Когда я захожу на сайт MVC, используя внешнего провайдера (Facebook), я вхожу в систему.С сайта MVC я также могу правильно использовать веб-API.

Однако на следующий день я все еще захожу на сайт MVC, но когда я пытаюсь получить доступ к веб-API, я получаю«не авторизованное исключение».

Поэтому, хотя я все еще вошел в систему на сайте MVC, я больше не авторизован для вызова веб-API изнутри сайта MVC.

Мне интересно, как справиться с этой ситуацией и как настроить IdentityServer4.

  • Почему я все еще захожу на сайт MVC днем ​​позже?Как это можно настроить?
  • Почему я до сих пор не могу вызвать веб-API, если я все еще вошел на сайт MVC?
  • Можно ли синхронизировать время истечения?Или как мне справиться с этим?

Приложение MVC настроено так:

 services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc"; 
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = "Cookies";
            options.Authority = mgpIdSvrSettings.Authority;
            options.RequireHttpsMetadata = false;                
            options.ClientId = mgpIdSvrSettings.ClientId;
            options.ClientSecret = mgpIdSvrSettings.ClientSecret; // Should match the secret at IdentityServer
            options.ResponseType = "code id_token"; // Use hybrid flow
            options.SaveTokens = true;                
            options.GetClaimsFromUserInfoEndpoint = true;                
            options.Scope.Add("mgpApi");
            options.Scope.Add("offline_access");                  
        });            

Таким образом, оно использует гибридный поток.

В IdentityServer клиент MVC настроен следующим образом:

new Client
{
     EnableLocalLogin = false,

     ClientId = "mgpPortal",
     ClientName = "MGP Portal Site",
     AllowedGrantTypes = GrantTypes.Hybrid,

     // where to redirect to after login
     RedirectUris = mgpPortalSite.RedirectUris,

     // where to redirect to after logout
     PostLogoutRedirectUris = mgpPortalSite.PostLogoutRedirectUris,

     // secret for authentication
     ClientSecrets = mgpPortalSite.ClientSecrets.Select(cs => new Secret(cs.Sha256())).ToList(),

     AllowedScopes = new List<string>
     {
            IdentityServerConstants.StandardScopes.OpenId,
            IdentityServerConstants.StandardScopes.Profile,
            "mgpApi"
     },

     AllowOfflineAccess = true,                             
     RequireConsent = false,
},

И, наконец, веб-API:

 services.AddAuthentication("Bearer")                
           .AddIdentityServerAuthentication(options =>
            {
                options.Authority = mgpIdSvrSettings.Authority;
                options.RequireHttpsMetadata = false;                    
                options.ApiName = mgpIdSvrSettings.ApiName;
                options.EnableCaching = true;
                options.CacheDuration = TimeSpan.FromMinutes(10);                    
            });

1 Ответ

0 голосов
/ 03 февраля 2019

Существует два типа аутентификации: cookie и канал-носитель.

Там, где cookie-файл хранит вас в системе, маркер канала-носителя не может.Поскольку токен-носитель настроен на истечение в определенный момент, не позволяя изменить время жизни.

Единственный способ получить доступ к ресурсу (api) после истечения срока действия токена-доступа - это позволить пользователю снова войти в систему илизапросить новый токен доступа, используя токен обновления , без необходимости взаимодействия с пользователем.

Вы уже настроили его:

options.Scope.Add("offline_access");

При каждом входе в систему запрос будетхотя бы содержат токен обновления.Храните его в безопасном месте и используйте при необходимости.По умолчанию он настроен только на одноразовое использование.


Вы можете использовать что-то вроде этого кода для обновления токена (поскольку вы фактически не обновляете его, а вместо этого заменяете его).Вам нужно будет включить пакет NuGet «IdentityModel», как видно из примеров из IdentityServer.

private async Task<TokenResponse> RenewTokensAsync()
{
    // Initialize the token endpoint:
    var client = _httpClientFactory.CreateClient();
    var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");

    if (disco.IsError) throw new Exception(disco.Error);

    // Read the stored refresh token:
    var rt = await HttpContext.GetTokenAsync("refresh_token");
    var tokenClient = _httpClientFactory.CreateClient();

    // Request a new access token:
    var tokenResult = await tokenClient.RequestRefreshTokenAsync(new RefreshTokenRequest
    {
        Address = disco.TokenEndpoint,

        ClientId = "mvc",
        ClientSecret = "secret",
        RefreshToken = rt
    });

    if (!tokenResult.IsError)
    {
        var old_id_token = await HttpContext.GetTokenAsync("id_token");
        var new_access_token = tokenResult.AccessToken;
        var new_refresh_token = tokenResult.RefreshToken;
        var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);

        // Save the information in the cookie
        var info = await HttpContext.AuthenticateAsync("Cookies");

        info.Properties.UpdateTokenValue("refresh_token", new_refresh_token);
        info.Properties.UpdateTokenValue("access_token", new_access_token);
        info.Properties.UpdateTokenValue("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture));

        await HttpContext.SignInAsync("Cookies", info.Principal, info.Properties);
        return tokenResult;
    }
    return null;
}

По умолчанию использование токена обновления настроено как одноразовое использование.Обратите внимание, что при сохранении нового токена обновления происходит сбой и вы должны его потерять, тогда единственный способ запросить новый токен обновления - заставить пользователя снова войти в систему.

Также обратите внимание, что срок действия маркера обновления может истечь.


И, сделав один шаг назад, вам нужно будет использовать его, когда токен доступа истек или вот-вот истечет:

var accessToken = await HttpContext.GetTokenAsync("access_token");

var tokenHandler = new JwtSecurityTokenHandler();

var jwtSecurityToken = tokenHandler.ReadJwtToken(accessToken);

// Depending on the lifetime of the access token.
// This is just an example. An access token may be valid
// for less than one minute.
if (jwtSecurityToken.ValidTo < DateTime.UtcNow.AddMinutes(5))
{
    var responseToken = await RenewTokensAsync();
    if (responseToken == null)
    {
        throw new Exception("Error");
    }
    accessToken = responseToken.AccessToken;
}

// Proceed, accessToken contains a valid token.
...