// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers() .AddJsonOptions(options => { // required to serialize options.JsonSerializerOptions.Converters .Add(new ObjectDictionaryConverter()); options.JsonSerializerOptions.IgnoreNullValues = true; }); // graphql services.AddSingleton <IChat, Data.Chat>(); services.AddSingleton <IChatResolverService, ChatResolverService>(); services.AddSingleton <ChatSchemas>(); services.AddSingleton(provider => provider.GetRequiredService <ChatSchemas>().Chat); // configure execution options var tanka = services.AddTankaGraphQL() .ConfigureRules(rules => rules.Concat(new[] { CostAnalyzer.MaxCost(100, 1, true) }).ToArray()) .ConfigureSchema <ISchema>(schema => new ValueTask <ISchema>(schema)) .ConfigureWebSockets(); //if (Env.IsDevelopment()) tanka.AddExtension<TraceExtension>(); // signalr server services.AddSignalR(options => options.EnableDetailedErrors = true) .AddTankaGraphQL(); // graphql-ws websocket server // web socket server services.AddWebSockets(options => { options.AllowedOrigins.Add("http://localhost:5000"); options.AllowedOrigins.Add("http://localhost:3000"); }); // CORS is required for the graphql.samples.chat.ui React App services.AddCors(options => { options.AddDefaultPolicy(policy => { policy.WithOrigins("http://localhost:3000"); policy.AllowAnyHeader(); policy.AllowAnyMethod(); policy.AllowCredentials(); //policy.WithHeaders("X-Requested-With", "authorization"); }); }); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); // graphql services.AddSingleton <IChat, Chat>(); services.AddSingleton <IChatResolverService, ChatResolverService>(); services.AddSingleton <ChatSchemas>(); services.AddSingleton(provider => provider.GetRequiredService <ChatSchemas>().Chat); // configure execution options services.AddTankaSchemaOptions() .Configure <ISchema>((options, schema) => { options.ValidationRules = ExecutionRules.All .Concat(new[] { CostAnalyzer.MaxCost(maxCost: 100, defaultFieldComplexity: 1, addExtensionData: true) }).ToArray(); options.GetSchema = query => new ValueTask <ISchema>(schema); }); // signalr server services.AddSignalR(options => options.EnableDetailedErrors = true) // add GraphQL query streaming hub .AddTankaServerHubWithTracing(); // graphql-ws websocket server // web socket server services.AddWebSockets(options => { options.AllowedOrigins.Add("https://localhost:5000"); options.AllowedOrigins.Add("https://localhost:3000"); }); services.AddTankaWebSocketServerWithTracing(); // CORS is required for the graphql.samples.chat.ui React App services.AddCors(options => { options.AddDefaultPolicy(policy => { policy.WithOrigins("http://localhost:3000"); policy.AllowAnyHeader(); policy.AllowAnyMethod(); policy.AllowCredentials(); policy.WithHeaders("X-Requested-With", "authorization"); }); }); }
public void Cost_below_max_cost_with_with_costDirective_and_multiplier() { /* Given */ var document = Parser.ParseDocument( @"{ withMultiplier }"); /* When */ var result = Validate( document, CostAnalyzer.MaxCost( maxCost: 3, defaultFieldComplexity: 0) ); /* Then */ Assert.True(result.IsValid); }
public void Cost_below_max_cost_with_defaultComplexity() { /* Given */ var document = Parser.ParseDocument( @"{ default }"); /* When */ var result = Validate( document, CostAnalyzer.MaxCost( maxCost: 1, defaultFieldComplexity: 1) ); /* Then */ Assert.True(result.IsValid); }
public void Cost_below_max_cost_with_with_costDirective_and_multiplier() { /* Given */ var document = @"{ withMultiplier }"; /* When */ var result = Validate( document, CostAnalyzer.MaxCost( 3, 0) ); /* Then */ Assert.True(result.IsValid); }
public void Cost_below_max_cost_with_defaultComplexity() { /* Given */ var document = @"{ default }"; /* When */ var result = Validate( document, CostAnalyzer.MaxCost( 1, 1) ); /* Then */ Assert.True(result.IsValid); }
public void Cost_above_max_cost_with_costDirective_and_multiplier() { /* Given */ var document = Parser.ParseDocument( @"{ withMultiplier(count: 5) }"); /* When */ var result = Validate( document, CostAnalyzer.MaxCost( maxCost: 5, defaultFieldComplexity: 0) ); /* Then */ Assert.False(result.IsValid); Assert.Single( result.Errors, error => error.Code == "MAX_COST"); }
public void Cost_above_max_cost_with_defaultComplexity() { /* Given */ var document = Parser.ParseDocument( @"{ default }"); /* When */ var result = Validate( document, CostAnalyzer.MaxCost( maxCost: 0, defaultFieldComplexity: 1) ); /* Then */ Assert.False(result.IsValid); Assert.Single( result.Errors, error => error.Code == "MAX_COST"); }
public void Cost_above_max_cost_with_costDirective() { /* Given */ var document = @"{ withCost }"; /* When */ var result = Validate( document, CostAnalyzer.MaxCost( 0, 0) ); /* Then */ Assert.False(result.IsValid); Assert.Single( result.Errors, error => error.Code == "MAX_COST"); }
public void ConfigureServices(IServiceCollection services) { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear(); AddForwardedHeaders(services); services.AddMemoryCache(); // signalr authentication services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "sub", RoleClaimType = "role" }; options.Authority = Configuration["JWT:Authority"]; options.Audience = Configuration["JWT:Audience"]; options.SaveToken = true; // We have to hook the OnMessageReceived event in order to // allow the JWT authentication handler to read the access // token from the query string when a WebSocket or // Server-Sent Events request comes in. options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; // Read the token out of the query string context.Token = accessToken; return(Task.CompletedTask); }, OnTokenValidated = async context => { if (context.SecurityToken is JwtSecurityToken jwt) { /* * PERFORMANCE/SECURITY * * For performance reasons we're caching the results of the userInfo request. * * This is NOT production quality and you should carefully think when using * it in your application */ var cache = context.HttpContext.RequestServices.GetRequiredService <IMemoryCache>(); var key = jwt.Claims.Single(c => c.Type == "sub").Value; var userInfoClaimsIdentity = await cache .GetOrCreateAsync(key, async entry => { entry.SetAbsoluteExpiration(jwt.ValidTo - TimeSpan.FromSeconds(5)); var configuration = await context.Options.ConfigurationManager .GetConfigurationAsync(CancellationToken.None); var userInfoEndpoint = configuration.UserInfoEndpoint; var client = new HttpClient(); var userInfo = await client.GetUserInfoAsync(new UserInfoRequest { Address = userInfoEndpoint, Token = jwt.RawData }); if (userInfo.IsError) { return(new ClaimsIdentity()); } return(new ClaimsIdentity(userInfo.Claims)); }); context.Principal.AddIdentity(userInfoClaimsIdentity); } } }; }); services.AddAuthorization(options => options.AddPolicy("authorize", policy => policy.RequireAuthenticatedUser())); services.AddHttpContextAccessor(); // add domain services.AddSingleton <Messages>(); // add schema services.AddSchemaControllers() .AddQueryController <QueryController>() .AddMutationController <MutationController>() .AddSubscriptionController <SubscriptionController>() .AddMessageController <MessageController>(); services.AddMemoryCache(); services.AddSingleton <SchemaCache>(); services.AddTankaGraphQL() .ConfigureSchema <SchemaCache>(async cache => await cache.GetOrAdd()) .ConfigureRules(rules => rules.Concat(new[] { CostAnalyzer.MaxCost( 100 ) }).ToArray()); // add signalr services.AddSignalR(options => { options.EnableDetailedErrors = true; }) .AddTankaGraphQL(); }
public void ConfigureServices(IServiceCollection services) { services.AddControllers() .AddJsonOptions(options => { // required to serialize options.JsonSerializerOptions.Converters .Add(new ObjectDictionaryConverter()); }); AddForwardedHeaders(services); // signalr authentication services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "sub", RoleClaimType = "role" }; options.Authority = Configuration["JWT:Authority"]; options.Audience = Configuration["JWT:Audience"]; options.SaveToken = true; // We have to hook the OnMessageReceived event in order to // allow the JWT authentication handler to read the access // token from the query string when a WebSocket or // Server-Sent Events request comes in. options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"] .ToString(); if (string.IsNullOrEmpty(accessToken)) { accessToken = context.Request .Headers["Authorization"] .ToString() .Replace("Bearer ", string.Empty); } // Read the token out of the query string context.Token = accessToken; return(Task.CompletedTask); } }; }); services.AddAuthorization(options => options.AddPolicy("authorize", policy => policy.RequireAuthenticatedUser())); services.AddHttpContextAccessor(); // add domain services.AddSingleton <Channels>(); // add schema services.AddSchemaControllers() .AddQueryController <QueryController>() .AddChannelController <ChannelController>(); services.AddMemoryCache(); services.AddSingleton <SchemaCache>(); services.AddTankaGraphQL() .ConfigureSchema <SchemaCache>(async cache => await cache.GetOrAdd()) .ConfigureRules(rules => rules.Concat(new[] { CostAnalyzer.MaxCost( 100 ) }).ToArray()); // add signalr services.AddSignalR(options => { options.EnableDetailedErrors = true; }) .AddTankaGraphQL(); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); AddForwardedHeaders(services); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear(); services.AddCors(cors => { cors.AddDefaultPolicy(policy => { policy.SetIsOriginAllowed(origin => true); policy.AllowAnyHeader(); policy.AllowAnyMethod(); policy.AllowCredentials(); }); }); // signalr authentication services.AddAuthentication() .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "sub", RoleClaimType = "role" }; options.Authority = Configuration["JWT:Authority"]; options.Audience = Configuration["JWT:Audience"]; options.SaveToken = false; // We have to hook the OnMessageReceived event in order to // allow the JWT authentication handler to read the access // token from the query string when a WebSocket or // Server-Sent Events request comes in. options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"].ToString(); if (string.IsNullOrEmpty(accessToken)) { var messageContext = context.HttpContext .RequestServices.GetRequiredService <IMessageContextAccessor>(); var message = messageContext.Context?.Message; if (message?.Type == MessageType.GQL_CONNECTION_INIT) { if (messageContext.Context?.Message.Payload is Dictionary <string, object> payload && payload.ContainsKey("authorization")) { accessToken = payload["authorization"].ToString() !; } } } // Read the token out of the query string context.Token = accessToken; return(Task.CompletedTask); }, OnTokenValidated = context => { if (context.SecurityToken is JwtSecurityToken jwt) { var tokenIdentity = new ClaimsIdentity( new[] { new Claim("access_token", jwt.RawData) }); context.Principal?.AddIdentity(tokenIdentity); } return(Task.CompletedTask); } }; }); services.AddAuthorization(options => options.AddPolicy("authorize", policy => policy .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser())); services.AddHttpContextAccessor(); // add schema cache services.AddSingleton <SchemaCache>(); services.AddMemoryCache(); // add execution options services.AddTankaGraphQL() .ConfigureSchema <SchemaCache>(async cache => await cache.GetOrAdd()) .ConfigureRules <ILogger <Startup> >((rules, logger) => rules.Concat(new[] { CostAnalyzer.MaxCost( 100, 1, onCalculated: operation => { logger.LogInformation( $"Operation '{operation.Operation.ToGraphQL()}' costs " + $"'{operation.Cost}' (max: '{operation.MaxCost}')"); } ) }).ToArray()) .ConfigureWebSockets <IHttpContextAccessor>( async(context, accessor) => { var succeeded = await AuthorizeHelper.AuthorizeAsync( accessor.HttpContext, new List <IAuthorizeData> { new AuthorizeAttribute("authorize") }); if (succeeded) { await context.Output.WriteAsync(new OperationMessage { Type = MessageType.GQL_CONNECTION_ACK }); } else { // you must decide what kind of message to send back to the client // in case the connection is not accepted. await context.Output.WriteAsync(new OperationMessage { Type = MessageType.GQL_CONNECTION_ERROR, Id = context.Message.Id }); // complete the output forcing the server to disconnect context.Output.Complete(); } }); // add signalr services.AddSignalR(options => { options.EnableDetailedErrors = true; }) .AddTankaGraphQL(); }