2016-09-08 2 views
10

토큰 유효성 검사에 IdentityServer4를 사용하는 API가 있습니다. 인 메모리 TestServer를 사용하여이 API를 단위 테스트하고 싶습니다. in-memory TestServer에서 IdentityServer를 호스팅하고 싶습니다.메모리 내 IdentityServer를 사용한 통합 테스트

IdentityServer에서 토큰을 생성 할 수있었습니다.

이 내가 얼마나 멀리 왔는지,하지만 난

API를 사용하는 [권한 부여]가 다른 정책을 -attribute "http://localhost:54100/.well-known/openid-configuration에서 구성을 가져올 수 없습니다"오류가 발생합니다. 이것이 내가 테스트하고 싶은 것입니다.

이 작업을 수행 할 수 있습니까? 내가 뭘 잘못하고 있습니까? IdentityServer4의 소스 코드를 살펴 보려고했지만 유사한 통합 테스트 시나리오를 발견하지 못했습니다.

protected IntegrationTestBase() 
{ 
    var startupAssembly = typeof(Startup).GetTypeInfo().Assembly; 

    _contentRoot = SolutionPathUtility.GetProjectPath(@"<my project path>", startupAssembly); 
    Configure(_contentRoot); 
    var orderApiServerBuilder = new WebHostBuilder() 
     .UseContentRoot(_contentRoot) 
     .ConfigureServices(InitializeServices) 
     .UseStartup<Startup>(); 
    orderApiServerBuilder.Configure(ConfigureApp); 
    OrderApiTestServer = new TestServer(orderApiServerBuilder); 

    HttpClient = OrderApiTestServer.CreateClient(); 
} 

private void InitializeServices(IServiceCollection services) 
{ 
    var cert = new X509Certificate2(Path.Combine(_contentRoot, "idsvr3test.pfx"), "idsrv3test"); 
    services.AddIdentityServer(options => 
     { 
      options.IssuerUri = "http://localhost:54100"; 
     }) 
     .AddInMemoryClients(Clients.Get()) 
     .AddInMemoryScopes(Scopes.Get()) 
     .AddInMemoryUsers(Users.Get()) 
     .SetSigningCredential(cert); 

    services.AddAuthorization(options => 
    { 
     options.AddPolicy(OrderApiConstants.StoreIdPolicyName, policy => policy.Requirements.Add(new StoreIdRequirement("storeId"))); 
    }); 
    services.AddSingleton<IPersistedGrantStore, InMemoryPersistedGrantStore>(); 
    services.AddSingleton(_orderManagerMock.Object); 
    services.AddMvc(); 
} 

private void ConfigureApp(IApplicationBuilder app) 
{ 
    app.UseIdentityServer(); 
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); 
    var options = new IdentityServerAuthenticationOptions 
    { 
     Authority = _appsettings.IdentityServerAddress, 
     RequireHttpsMetadata = false, 

     ScopeName = _appsettings.IdentityServerScopeName, 
     AutomaticAuthenticate = false 
    }; 
    app.UseIdentityServerAuthentication(options); 
    app.UseMvc(); 
} 

그리고 내 단위 테스트에서

:

private HttpMessageHandler _handler; 
const string TokenEndpoint = "http://localhost/connect/token"; 
public Test() 
{ 
    _handler = OrderApiTestServer.CreateHandler(); 
} 

[Fact] 
public async Task LeTest() 
{ 
    var accessToken = await GetToken(); 
    HttpClient.SetBearerToken(accessToken); 

    var httpResponseMessage = await HttpClient.GetAsync("stores/11/orders/asdf"); // Fails on this line 

} 

private async Task<string> GetToken() 
{ 
    var client = new TokenClient(TokenEndpoint, "client", "secret", innerHttpMessageHandler: _handler); 

    var response = await client.RequestClientCredentialsAsync("TheMOON.OrderApi"); 

    return response.AccessToken; 
} 

답변

3

나는 아마 당신은 당신이 원하는 얼마나 많은 기능에 따라 권한 부여 미들웨어에 대한 테스트를 두 번 가짜를 만들 필요가 있다고 생각합니다. 따라서 기본적으로 인증 미들웨어가 발견 문서에 대한 백 채널 호출을 제외한 모든 것을 수행하는 미들웨어가 필요합니다.

IdentityServer4.AccessTokenValidation은 두 개의 미들웨어에 대한 래퍼입니다. JwtBearerAuthentication 미들웨어 및 OAuth2IntrospectionAuthentication 미들웨어. 이 두 가지 모두 http를 통해 발견 문서를 수집하여 토큰 유효성 검사에 사용합니다. 메모리 내장형 테스트를 원한다면 어떤 문제가 있습니다.

문제를 해결하려면 검색 문서를 가져 오는 외부 호출을 수행하지 않는 app.UseIdentityServerAuthentication의 가짜 버전을 만들어야 할 수 있습니다. [Authorize] 정책을 테스트 할 수 있도록 HttpContext principal 만 채 웁니다.

IdentityServer4.AccessTokenValidation의 고기가 here 인 모양을 확인하십시오. 그리고 JwtBearer Middleware가 어떻게 보이는지에 대해 살펴 보자. here

+0

덕분에 많은 @Lutando. 첫 번째 대답은 올바른 방향으로 나를 가리켰다. –

+0

아, 괜찮습니다. @emedbo 가짜 테스트를 두 번 할 수 있다고 생각했습니다. 하지만 작동합니다. – Lutando

+0

가짜 프로젝트는 약 20 개의 파일로 구성되어 있으므로 책임이 있습니다. 위쪽은 꽤 산뜻하지만! 나는 나보다 많은 지식을 가진 누군가가 덜 복잡하게 만들 수 있다고 확신한다. –

4

@ james-fera 게시 된 것보다 더 완전한 대답이 필요하다는 것을 알고있다. 나는 그의 답변에서 배웠으며 시험 프로젝트와 API 프로젝트로 구성된 github 프로젝트를 만들었다. 코드는 이해하기 쉽고 이해하기 어렵지 않아야합니다.

https://github.com/emedbo/identityserver-test-template

IdentityServerSetup.cs 클래스 https://github.com/emedbo/identityserver-test-template/blob/master/tests/API.Tests/Config/IdentityServerSetup.cs 예컨대 얻어 추상화 될 수있다 NuGetted, 기본 클래스를 떠나 멀리 NuGetted

본질은 사용자, 클라이언트, 범위, 암호 등등으로 정상적인 IdentityServer와 똑같이 IdentityServer 작업을 수행 할 수 있습니다. [Authorize (Role = " 관리자)이 증명. 대신 여기에 코드를 게시하는

을, 나는 기초 다음 내 프로젝트 실행 테스트를 당겨받을 제임스-FERA의 포스트 @ 읽을 것을 권장합니다.

IdentityServer이와 같은 훌륭한 도구이며, TestServer 프레임 워크를 사용할 수있는 능력이 향상되었습니다.

+0

아직 도움이된다면 언급하신 프로젝트를보고 싶습니다. 나는 현재 당신이 묘사하는 것과 비슷한 것을하려고 노력하고 있습니다. –

+0

위에서 게시 한 @ james-fera를 사용해 보셨습니까? 그렇지 않다면 먼저 솔루션을 시도해 볼 것입니다. 솔루션에 더 많은 코드가 필요하기 때문입니다. –

+0

나는 그것을 시도하고, 그것은 작동하지 않았다. 그런데 아이디 서버 설정에서 구성 오류가 발견되었습니다. 일단 고정되면, @ james-fera의 제안은 완벽하게 작동했습니다. –

12

그는 당신의 초기 질문에 게시 된 코드를 올바르게 추적합니다.

IdentityServerAuthenticationOptions 개체는 기본적에게이 백 채널 통신에 사용 HttpMessageHandlers를 오버라이드 (override)하는 속성이 있습니다. 당신이 CreateHandler() 당신의 TESTSERVER 객체에 방법이 결합되면

당신이 얻을 :

//build identity server here 

    var idBuilder = new WebBuilderHost(); 
    idBuilder.UseStartup<Startup>(); 
    //... 

    TestServer identityTestServer = new TestServer(idBuilder); 

    var identityServerClient = identityTestServer.CreateClient(); 

    var token = //use identityServerClient to get Token from IdentityServer 

    //build Api TestServer 
    var options = new IdentityServerAuthenticationOptions() 
    { 
     Authority = "http://localhost:5001", 

     // IMPORTANT PART HERE 
     JwtBackChannelHandler = identityTestServer.CreateHandler(), 
     IntrospectionDiscoveryHandler = identityTestServer.CreateHandler(), 
     IntrospectionBackChannelHandler = identityTestServer.CreateHandler() 
    }; 

    var apiBuilder = new WebHostBuilder(); 

    apiBuilder.ConfigureServices(c => c.AddSingleton(options)); 
    //build api server here 

    var apiClient = new TestServer(apiBuilder).CreateClient(); 
    apiClient.SetBearerToken(token); 

    //proceed with auth testing 

이 당신의 IN-와 직접 통신 할 API 프로젝트에 AccessTokenValidation 미들웨어를 할 수 있습니다 메모리 신원 서버. 농구대를 뛰어 넘을 필요가 없습니다. 보조 노트로

는 API 프로젝트를 위해, 나는 그것이 유용 TryAddSingleton를 사용하는 대신 인라인을 만드는 의 서비스 컬렉션에 IdentityServerAuthenticationOptions에게 Startup.cs을 추가 찾을 :

public void ConfigureServices(IServiceCollection services) 
    { 
     services.TryAddSingleton(new IdentityServerAuthenticationOptions 
     { 
      Authority = Configuration.IdentityServerAuthority(), 
      ScopeName = "api1", 
      ScopeSecret = "secret", 
      //..., 
     }); 
    } 

    public void Configure(IApplicationBuilder app) 
    { 
     var options = app.ApplicationServices.GetService<IdentityServerAuthenticationOptions>() 

     app.UseIdentityServerAuthentication(options); 

     //... 

    } 

이렇게하면 Api 프로젝트의 코드를 변경하지 않고도 IdentityServerAuthenticationOptions 객체를 테스트에 등록 할 수 있습니다.

+0

그건 꽤 깔끔하게 보입니다. 나는 그것을 시도 할 것입니다. 이 솔루션으로 인증 및 모든 클레임을 작성할 수 있습니까? 현재 솔루션은 더 많은 작업을 필요로했지만 원활하게 실행되고 있으며 역할, 주장, 모든 것에 대해 테스트 할 수 있습니다. –

+0

이것은 완전히 기능하는 인 메모리 IdentityServer 및 Api 서버를 만듭니다 (동일한 서버에 둘 다 가지고 있던 원래 예제와는 다른 두 개의 TestServer 객체에 보관합니다). 이를 통해 원하는 모든 것을 테스트 할 수 있습니다. apiClient를 사용하여 요청을 발행하고 응답을 테스트하십시오. 인증 기능 (클레임, 역할 등)은 IdentityServer에서 반환 된 토큰에 저장되며 적합하다고 판단되는 기능을 사용하는 것은 Api 서버에 달려 있습니다. –

+0

이것은 깔끔합니다. 가짜 백 채널 처리기를 삽입 할 수 있도록 후크를 찾으려고했으나 찾지 못했습니다. 추가되었거나 항상 존재 했나요? – Lutando

0

테스트 API 시작 :

public class Startup 
{ 
    public static HttpMessageHandler BackChannelHandler { get; set; } 

    public void Configuration(IAppBuilder app) 
    { 
     //accept access tokens from identityserver and require a scope of 'Test' 
     app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions 
     { 
      Authority = "https://localhost", 
      BackchannelHttpHandler = BackChannelHandler, 
      ... 
     }); 

     ... 
    } 
} 

내 단위 테스트 프로젝트에 TestApi BackChannelHandler에 AuthServer.Handler 할당 :

protected TestServer AuthServer { get; set; } 
    protected TestServer MockApiServer { get; set; } 
    protected TestServer TestApiServer { get; set; } 

    [OneTimeSetUp] 
    public void Setup() 
    { 
     ... 
     AuthServer = TestServer.Create<AuthenticationServer.Startup>(); 
     TestApi.Startup.BackChannelHandler = AuthServer.Handler; 
     TestApiServer = TestServer.Create<TestApi.Startup>(); 
    } 
관련 문제