private static TestServer CreateResourceServer(Action<OAuthIntrospectionOptions> configuration) { var server = CreateAuthorizationServer(); var builder = new WebHostBuilder(); builder.UseEnvironment("Testing"); builder.ConfigureServices(services => { services.AddAuthentication(); services.AddDistributedMemoryCache(); }); builder.Configure(app => { app.UseOAuthIntrospection(options => { options.AutomaticAuthenticate = true; options.AutomaticChallenge = true; options.Authority = server.BaseAddress.AbsoluteUri; options.HttpClient = server.CreateClient(); // Run the configuration delegate // registered by the unit tests. configuration?.Invoke(options); }); app.Map("/ticket", map => map.Run(async context => { var ticket = new AuthenticateContext(OAuthIntrospectionDefaults.AuthenticationScheme); await context.Authentication.AuthenticateAsync(ticket); if (!ticket.Accepted || ticket.Principal == null || ticket.Properties == null) { await context.Authentication.ChallengeAsync(); return; } context.Response.ContentType = "application/json"; // Return the authentication ticket as a JSON object. await context.Response.WriteAsync(JsonConvert.SerializeObject(new { Claims = from claim in ticket.Principal.Claims select new { claim.Type, claim.Value }, Properties = from property in ticket.Properties select new { Name = property.Key, property.Value } })); })); app.Run(context => { if (!context.User.Identities.Any(identity => identity.IsAuthenticated)) { return context.Authentication.ChallengeAsync(); } return context.Response.WriteAsync(context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value); }); }); return new TestServer(builder); }
private static TestServer CreateAuthorizationServer() { var builder = new WebHostBuilder(); builder.UseEnvironment("Testing"); builder.ConfigureLogging(options => options.AddDebug()); builder.ConfigureServices(services => { services.AddAuthentication(); services.AddDistributedMemoryCache(); services.AddLogging(); }); builder.Configure(app => { app.Map("/.well-known/openid-configuration", map => map.Run(async context => { using (var buffer = new MemoryStream()) using (var writer = new JsonTextWriter(new StreamWriter(buffer))) { var payload = new JObject { [OAuthIntrospectionConstants.Metadata.IntrospectionEndpoint] = "http://localhost/connect/introspect" }; payload.WriteTo(writer); writer.Flush(); context.Response.ContentLength = buffer.Length; context.Response.ContentType = "application/json;charset=UTF-8"; buffer.Seek(offset: 0, loc: SeekOrigin.Begin); await buffer.CopyToAsync(context.Response.Body, 4096, context.RequestAborted); } })); app.Map("/connect/introspect", map => map.Run(async context => { using (var buffer = new MemoryStream()) using (var writer = new JsonTextWriter(new StreamWriter(buffer))) { var payload = new JObject(); var form = await context.Request.ReadFormAsync(); switch (form[OAuthIntrospectionConstants.Parameters.Token]) { case "invalid-token": { payload[OAuthIntrospectionConstants.Claims.Active] = false; break; } case "expired-token": { payload[OAuthIntrospectionConstants.Claims.Active] = true; payload[OAuthIntrospectionConstants.Claims.Subject] = "Fabrikam"; // 1451602800 = 01/01/2016 - 00:00:00 AM. payload[OAuthIntrospectionConstants.Claims.ExpiresAt] = 1455359642; break; } case "valid-token": { payload[OAuthIntrospectionConstants.Claims.Active] = true; payload[OAuthIntrospectionConstants.Claims.JwtId] = "jwt-token-identifier"; payload[OAuthIntrospectionConstants.Claims.Subject] = "Fabrikam"; break; } case "valid-token-with-scopes": { payload[OAuthIntrospectionConstants.Claims.Active] = true; payload[OAuthIntrospectionConstants.Claims.JwtId] = "jwt-token-identifier"; payload[OAuthIntrospectionConstants.Claims.Subject] = "Fabrikam"; payload[OAuthIntrospectionConstants.Claims.Scope] = "C54A8F5E-0387-43F4-BA43-FD4B50DC190D 5C57E3BD-9EFB-4224-9AB8-C8C5E009FFD7"; break; } case "valid-token-with-single-audience": { payload[OAuthIntrospectionConstants.Claims.Active] = true; payload[OAuthIntrospectionConstants.Claims.JwtId] = "jwt-token-identifier"; payload[OAuthIntrospectionConstants.Claims.Subject] = "Fabrikam"; payload[OAuthIntrospectionConstants.Claims.Audience] = "http://www.google.com/"; break; } case "valid-token-with-multiple-audiences": { payload[OAuthIntrospectionConstants.Claims.Active] = true; payload[OAuthIntrospectionConstants.Claims.JwtId] = "jwt-token-identifier"; payload[OAuthIntrospectionConstants.Claims.Subject] = "Fabrikam"; payload[OAuthIntrospectionConstants.Claims.Audience] = JArray.FromObject(new[] { "http://www.google.com/", "http://www.fabrikam.com/" }); break; } } payload.WriteTo(writer); writer.Flush(); context.Response.ContentLength = buffer.Length; context.Response.ContentType = "application/json;charset=UTF-8"; buffer.Seek(offset: 0, loc: SeekOrigin.Begin); await buffer.CopyToAsync(context.Response.Body, 4096, context.RequestAborted); } })); }); return new TestServer(builder); }
private static TestServer CreateResourceServer(Action<OAuthValidationOptions> configuration = null) { var builder = new WebHostBuilder(); var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(MockBehavior.Strict); format.Setup(mock => mock.Unprotect(It.Is<string>(token => token == "invalid-token"))) .Returns(value: null); format.Setup(mock => mock.Unprotect(It.Is<string>(token => token == "valid-token"))) .Returns(delegate { var identity = new ClaimsIdentity(OAuthValidationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "Fabrikam")); var properties = new AuthenticationProperties(); return new AuthenticationTicket(new ClaimsPrincipal(identity), properties, OAuthValidationDefaults.AuthenticationScheme); }); format.Setup(mock => mock.Unprotect(It.Is<string>(token => token == "valid-token-with-scopes"))) .Returns(delegate { var identity = new ClaimsIdentity(OAuthValidationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "Fabrikam")); var properties = new AuthenticationProperties(); properties.Items[OAuthValidationConstants.Properties.Scopes] = "C54A8F5E-0387-43F4-BA43-FD4B50DC190D 5C57E3BD-9EFB-4224-9AB8-C8C5E009FFD7"; return new AuthenticationTicket(new ClaimsPrincipal(identity), properties, OAuthValidationDefaults.AuthenticationScheme); }); format.Setup(mock => mock.Unprotect(It.Is<string>(token => token == "valid-token-with-single-audience"))) .Returns(delegate { var identity = new ClaimsIdentity(OAuthValidationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "Fabrikam")); var properties = new AuthenticationProperties(new Dictionary<string, string> { [OAuthValidationConstants.Properties.Audiences] = "http://www.google.com/" }); return new AuthenticationTicket(new ClaimsPrincipal(identity), properties, OAuthValidationDefaults.AuthenticationScheme); }); format.Setup(mock => mock.Unprotect(It.Is<string>(token => token == "valid-token-with-multiple-audiences"))) .Returns(delegate { var identity = new ClaimsIdentity(OAuthValidationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "Fabrikam")); var properties = new AuthenticationProperties(new Dictionary<string, string> { [OAuthValidationConstants.Properties.Audiences] = "http://www.google.com/ http://www.fabrikam.com/" }); return new AuthenticationTicket(new ClaimsPrincipal(identity), properties, OAuthValidationDefaults.AuthenticationScheme); }); format.Setup(mock => mock.Unprotect(It.Is<string>(token => token == "expired-token"))) .Returns(delegate { var identity = new ClaimsIdentity(OAuthValidationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "Fabrikam")); var properties = new AuthenticationProperties(); properties.ExpiresUtc = DateTimeOffset.UtcNow - TimeSpan.FromDays(1); return new AuthenticationTicket(new ClaimsPrincipal(identity), properties, OAuthValidationDefaults.AuthenticationScheme); }); builder.UseEnvironment("Testing"); builder.ConfigureLogging(options => options.AddDebug()); builder.ConfigureServices(services => { services.AddAuthentication(); }); builder.Configure(app => { app.UseOAuthValidation(options => { options.AutomaticAuthenticate = true; options.AutomaticChallenge = true; options.AccessTokenFormat = format.Object; // Run the configuration delegate // registered by the unit tests. configuration?.Invoke(options); }); app.Map("/ticket", map => map.Run(async context => { var ticket = new AuthenticateContext(OAuthValidationDefaults.AuthenticationScheme); await context.Authentication.AuthenticateAsync(ticket); if (!ticket.Accepted || ticket.Principal == null || ticket.Properties == null) { await context.Authentication.ChallengeAsync(); return; } context.Response.ContentType = "application/json"; // Return the authentication ticket as a JSON object. await context.Response.WriteAsync(JsonConvert.SerializeObject(new { Claims = from claim in ticket.Principal.Claims select new { claim.Type, claim.Value }, Properties = from property in ticket.Properties select new { Name = property.Key, property.Value } })); })); app.Run(context => { if (!context.User.Identities.Any(identity => identity.IsAuthenticated)) { return context.Authentication.ChallengeAsync(); } var identifier = context.User.FindFirst(ClaimTypes.NameIdentifier).Value; return context.Response.WriteAsync(identifier); }); }); return new TestServer(builder); }