2

OAUTH2/OIDC 프로토콜을 따르는 아키텍처를 구현하려고합니다. 이를 위해 STS (Identity Server v3 by leastprivilege), ASP.NET WebApi 및 클라이언트 용 ASP.NET MVC 응용 프로그램이 있습니다. 제 목표는 STS와 REST 서비스를 Azure에서 호스팅하여 다른 클라이언트가 공용 서비스로 사용할 수 있도록하는 것이 었습니다. 여태까지는 그런대로 잘됐다. 리디렉션 흐름 중 하나 인 인증 코드 흐름을 사용하는 새 클라이언트를 추가하기로 결정하기 전에 모든 것이 원활하고 완벽하게 작동하는 것처럼 보였습니다. 내가 제공하는 새로 고침 토큰 옵션을 활용하고 싶었습니다. 나는 그 클라이언트에게 짧은 생명 접속 토큰 (10 분)을 제공하고 새로운 토큰을 얻기 위해 새로 고침 토큰을 사용하게하고 싶었다.Azure에서 Identity Server by leastprivilege가 제대로 작동하지 않습니다.

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 일 이어도 상관 없습니다.

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 내 로컬 컴퓨터에서 실행과 같은 경우이 예상 작품으로 다음 STS에 의해 생성 된 로그는

enter image description here

. 새로 고침 토큰으로 새 토큰을 얻을 수 있습니다.

enter image description here

RESLOVED : 문제 정말 프레드 한 것이었다 - MSFT는 지적했다. 새로 고침 토큰을위한 영구 저장소를 구현해야했습니다. 그것을 성취하는 것은 정말로 쉽습니다. 은 Identity 서버

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 
    } 
} 

답변

1

W3wp.exe를 경고 : 0 : 2017 이것은 내가 그것을 어떻게입니다 -04-06 12 : 01 : 21.456 + 00 : 00 [경고] AuthorizationCodeStore가 구성되지 않음 - InMemory로 폴백

,363,210

W3wp.exe를 경고 : 0 : 2017년 4월 6일 12 : 01 : 21.512 +00 : 00 [경고] TokenHandleStore 구성되지 않음 - InMemory

W3wp.exe를 경고로 다시 하강 0 : 2017-04- 06 12 : 01 : 21.512 +00 : 00 ConsentStore가 구성되지 않음 - InMemory로 폴백

w3wp.exe 경고 : 0 : 2017-04-06 12:01:21.512 00 : - 당신이/저장 당신은 멀티와 푸른 웹 사이트에 호스팅하는 경우 문제의 원인이 될 수있는 메모리의 데이터를 유지하는 것이 보인다 InMemory

로 다시 떨어지는 00 [경고] 구성되지 RefreshTokenStore 로드 밸런서 뒤에있는 인스턴스 메모리 내 저장 대신 다른 데이터 저장소에 데이터를 저장하려고 할 수 있습니다.

_cache = MemoryCache.Default;

게다가 웹 응용 프로그램의 메모리를 통해 tokensCacheKey을 저장하고 검색하면 Azure 다중 인스턴스 웹 팜 환경에서 제대로 작동하지 않습니다. Azure 저장소, 데이터베이스 또는 Redis 캐시와 같은 외부 저장소에 데이터를 저장하십시오.

+0

웹 API (클라이언트)가 아닌 MVC 앱입니다. 그것은 단지 테스트 목적을위한 것입니다, 그래서 나는 거기에 기본 캐시를 사용하고 있습니다. 아무 문제 없습니다. 따라서 STS에서 메모리에 토큰을 저장하는 것이 문제 일 수 있다고 말하고 있습니까? – user2128702

+0

STS의 메모리에 저장되어 있습니까? –

+0

글쎄, 나는 영구 저장소를 사용하지 않는다고 확신한다. – user2128702