IdentityServer by Minimumprivilege在Azure上无法正常运行 [英] Identity Server by leastprivilege doesn't work properly on Azure

查看:102
本文介绍了IdentityServer by Minimumprivilege在Azure上无法正常运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现遵循OAUTH2/OIDC协议的体系结构.为了做到这一点,我有一个客户端的STS(Identity Server v3,至少由privilege),ASP.NET WebApi和ASP.NET MVC应用程序.我的目标是将STS和REST服务托管在Azure上,以便不同的客户端可以将它们用作公共服务.到目前为止,一切都很好.在我决定添加一个使用重定向流程之一(授权代码流程)的新客户端之前,一切似乎都工作顺利且完美.我想利用它提供的刷新令牌选项.我想为该客户提供短暂访问令牌(10分钟),并让他使用刷新令牌来获取新令牌.这就是代码中的样子:

I am trying to implement an architecture that follows the OAUTH2/OIDC protocol. In order to do that, I have STS(Identity Server v3 by leastprivilege), ASP.NET WebApi and ASP.NET MVC application for a client. My goal was to have the STS and REST service hosted on Azure so different clients can use them as public services. So far so good. Everything seemed to work smoothly and perfectly before I decided to add a new client that uses one of the redirection flows - Authorization Code flow. I wanted to take advantage of the refresh token option that it offers. I wanted to serve short life access tokens(10 minutes) to that client and make him use a refresh token in order to obtain new tokens. This is how it all looks in code:

STS:

new Client 
{
    ClientId = "tripgalleryauthcode",
    ClientName = "Trip Gallery (Authorization Code)",
    Flow = Flows.AuthorizationCode, 
    AllowAccessToAllScopes = true,
    RequireConsent = false,

    RedirectUris = new List<string>
    {
        Tripgallery.Constants.TripgalleryMvcAuthCodePostLogoutCallback
    },           

    ClientSecrets = new List<Secret>()
    {
        new Secret(Tripgallery.Constants.TripgalleryClientSecret.Sha256())
    },

    // refresh token options
    AccessTokenType = AccessTokenType.Jwt,
    AccessTokenLifetime = 600,
    RefreshTokenUsage = TokenUsage.OneTimeOnly, // Every time generates new refresh token. Not only access token.
    RefreshTokenExpiration = TokenExpiration.Sliding,
    SlidingRefreshTokenLifetime = 1296000,

    PostLogoutRedirectUris = new List<string>()
    {
        Tripgallery.Constants.TripgalleryPostLogoutCallback
    }
}

Mvc应用程序(客户端):

private ObjectCache _cache;
private readonly string tokensCacheKey = "Tokens";

public HomeController()
{
    _cache = MemoryCache.Default;
}

// GET: Home
public ActionResult Index()
{
    var authorizeRequest = new AuthorizeRequest(Constants.BoongalooSTSAuthorizationEndpoint);

    var state = HttpContext.Request.Url.OriginalString;

    var url = authorizeRequest.CreateAuthorizeUrl(
    "tripgalleryauthcode",
    "code",
    "openid profile address tripgallerymanagement offline_access",
    Constants.TripgalleryMvcAuthCodePostLogoutCallback,
    state);

    HttpContext.Response.Redirect(url);
    return null;
}

public async Task<ActionResult> StsCallBackForAuthCodeClient()
{
    var authCode = Request.QueryString["code"];

    var client = new TokenClient(
    Constants.TripgallerySTSTokenEndpoint,
    "tripgalleryauthcode",
    Constants.TripgalleryClientSecret
    );

    var tokenResponse = await client.RequestAuthorizationCodeAsync(
    authCode,
    Constants.TripgalleryMvcAuthCodePostLogoutCallback
    );

    this._cache[this.tokensCacheKey] = new TokenModel()
    {
        AccessToken = tokenResponse.AccessToken,
        IdToken = tokenResponse.IdentityToken,
        RefreshToken = tokenResponse.RefreshToken,
        AccessTokenExpiresAt = DateTime.Parse(DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToString(CultureInfo.InvariantCulture))
    };

    return View();
}

public ActionResult StartCallingWebApi()
{
    var timer = new Timer(async (e) =>
    {
        var cachedStuff = this._cache.Get(this.tokensCacheKey) as TokenModel;
        await ExecuteWebApiCall(cachedStuff);
    }, null, 0, Convert.ToInt32(TimeSpan.FromMinutes(20).TotalMilliseconds));

    return null;
}

private async Task ExecuteWebApiCall(TokenModel cachedStuff)
{
    // Ensure that access token expires in more than one minute
    if (cachedStuff != null && cachedStuff.AccessTokenExpiresAt > DateTime.Now.AddMinutes(1))
    {
        await MakeValidApiCall(cachedStuff);
    }
    else
    {
        // Use the refresh token to get a new access token, id token and refresh token
        var client = new TokenClient(
            Constants.TripgallerySTSTokenEndpoint,
            "tripgalleryauthcode",
            Constants.TripgalleryClientSecret
        );

        if (cachedStuff != null)
        {
            var newTokens = await client.RequestRefreshTokenAsync(cachedStuff.RefreshToken);

            var value = new TokenModel()
            {
                AccessToken = newTokens.AccessToken,
                IdToken = newTokens.IdentityToken,
                RefreshToken = newTokens.RefreshToken,
                AccessTokenExpiresAt =
                    DateTime.Parse(
                        DateTime.Now.AddSeconds(newTokens.ExpiresIn).ToString(CultureInfo.InvariantCulture))
            };

            this._cache.Set(this.tokensCacheKey, (object)value, new CacheItemPolicy());

            await MakeValidApiCall(value);
        }
    }
}

问题是,如果出于某种原因,如果我将STS托管在Azure上,如果我决定在访问令牌过期后20分钟或更长时间使用刷新令牌,则会收到错误消息.无论我的刷新令牌寿命为15天.

The problem is that if I have the STS hosted on Azure, for some reason, if I decide to use the refresh token in 20 or more minutes after the access token was expired I get an error. No matter that my refresh token life time is 15 days.

这是STS生成的日志:

w3wp.exe Warning: 0 : 2017-04-06 12:01:21.456 +00:00 [Warning] AuthorizationCodeStore not configured - falling back to InMemory
w3wp.exe Warning: 0 : 2017-04-06 12:01:21.512 +00:00 [Warning] TokenHandleStore not configured - falling back to InMemory
w3wp.exe Warning: 0 : 2017-04-06 12:01:21.512 +00:00 [Warning] ConsentStore not configured - falling back to InMemory
w3wp.exe Warning: 0 : 2017-04-06 12:01:21.512 +00:00 [Warning] RefreshTokenStore not configured - falling back to InMemory
w3wp.exe Information: 0 : 2017-04-06 12:01:22.371 +00:00 [Information] Start token request
w3wp.exe Information: 0 : 2017-04-06 12:01:22.418 +00:00 [Information] Client secret id found: "tripgalleryauthcode"
w3wp.exe Information: 0 : 2017-04-06 12:01:22.418 +00:00 [Information] Client validation success
w3wp.exe Information: 0 : 2017-04-06 12:01:22.418 +00:00 [Information] Start token request validation
w3wp.exe Information: 0 : 2017-04-06 12:01:22.433 +00:00 [Information] Start validation of refresh token request
w3wp.exe Warning: 0 : 2017-04-06 12:01:22.574 +00:00 [Warning] "Refresh token is invalid"
 "{
  \"ClientId\": \"tripgalleryauthcode\",
  \"ClientName\": \"Trip Gallery (Authorization Code)\",
  \"GrantType\": \"refresh_token\",
  \"RefreshToken\": \"140cfb19405a6a4cbace29646751194a\",
  \"Raw\": {
    \"grant_type\": \"refresh_token\",
    \"refresh_token\": \"140cfb19405a6a4cbace29646751194a\"
  }
}"
w3wp.exe Information: 0 : 2017-04-06 12:01:22.590 +00:00 [Information] End token request
w3wp.exe Information: 0 : 2017-04-06 12:01:22.590 +00:00 [Information] Returning error: invalid_grant
w3wp.exe Information: 0 : 2017-04-06 12:01:29.465 +00:00 [Information] Start discovery request
w3wp.exe Information: 0 : 2017-04-06 12:01:29.512 +00:00 [Information] Start key discovery request

在我的本地计算机上运行STS的情况相同.我可以使用刷新令牌来获取新令牌.

The same case with the STS running on my local machine works as expected. I can get the new tokens with my refresh token.

已删除: 问题确实是 Fred Han-MSFT 指出的.我需要为刷新令牌实现持久性存储.实现它真的很容易.这是我的方法:

RESLOVED: The issue really was what Fred Han - MSFT pointed out. I needed to implement persistent store for my refresh tokens. It is really easy to achieve it. This is how I did it:

身份服务器的Startup.cs :

var idServerServiceFactory = new IdentityServerServiceFactory()
                                .UseInMemoryClients(Clients.Get())
                                .UseInMemoryScopes(Scopes.Get());

//...

// use custom service for tokens maintainance
var customRefreshTokenStore = new CustomRefreshTokenStore();
idServerServiceFactory.RefreshTokenStore = new Registration<IRefreshTokenStore>(resolver => customRefreshTokenStore);

var options = new IdentityServerOptions
{
    Factory = idServerServiceFactory,

    // .....

}

idsrvApp.UseIdentityServer(options);

CustomRefreshTokenStore.cs

public class CustomRefreshTokenStore : IRefreshTokenStore
{
    public Task StoreAsync(string key, RefreshToken value)
    {
        // code that uses persitant storage mechanism
    }

    public Task<RefreshToken> GetAsync(string key)
    {
        // code that uses persitant storage mechanism
    }

    public Task RemoveAsync(string key)
    {
        // code that uses persitant storage mechanism
    }

    public Task<IEnumerable<ITokenMetadata>> GetAllAsync(string subject)
    {
        // code that uses persitant storage mechanism
    }

    public Task RevokeAsync(string subject, string client)
    {
        // code that uses persitant storage mechanism
    }
}

推荐答案

w3wp.exe警告:0:20​​17-04-06 12:01:21.456 +00:00 [警告]未配置AuthorizationCodeStore-退回到InMemory

w3wp.exe Warning: 0 : 2017-04-06 12:01:21.456 +00:00 [Warning] AuthorizationCodeStore not configured - falling back to InMemory

w3wp.exe警告:0:20​​17-04-06 12:01:21.512 +00:00 [警告] TokenHandleStore未配置-退回到InMemory

w3wp.exe Warning: 0 : 2017-04-06 12:01:21.512 +00:00 [Warning] TokenHandleStore not configured - falling back to InMemory

w3wp.exe警告:0:20​​17-04-06 12:01:21.512 +00:00 [警告]未配置ConsentStore-退回到InMemory

w3wp.exe Warning: 0 : 2017-04-06 12:01:21.512 +00:00 [Warning] ConsentStore not configured - falling back to InMemory

w3wp.exe警告:0:20​​17-04-06 12:01:21.512 +00:00 [警告]未配置RefreshTokenStore-退回到InMemory

w3wp.exe Warning: 0 : 2017-04-06 12:01:21.512 +00:00 [Warning] RefreshTokenStore not configured - falling back to InMemory

似乎您将数据存储/维护在内存中,如果将数据托管在负载均衡器后面具有多个实例的Azure网站上,则可能会引起问题.您可以尝试将数据存储在其他数据存储中,而不是在内存中.

It seem that you store/maintain data in memory, which could be the cause of issue if you host it on Azure website with multi-instances behind the load balancer. You could try to store data in other data store instead of the in-memory storage.

_cache = MemoryCache.Default;

_cache = MemoryCache.Default;

此外,您还可以通过Web API应用程序中的内存存储和检索tokensCacheKey,这在Azure多实例Web场环境中不能很好地工作.请将数据存储在外部存储中,例如Azure存储,数据库或Redis缓存.

Besides, you store and retrieve tokensCacheKey via memory in your Web API application, which will not work well in Azure multi-instance web-farm environment. Please store the data in external storage, such as Azure storage, database or Redis cache.

这篇关于IdentityServer by Minimumprivilege在Azure上无法正常运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆