diff --git a/SignalR.sln b/SignalR.sln
index 6fd749c6f1..2456b41517 100644
--- a/SignalR.sln
+++ b/SignalR.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.26919.1
+VisualStudioVersion = 15.0.26923.0
MinimumVisualStudioVersion = 15.0.26730.03
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DA69F624-5398-4884-87E4-B816698CDE65}"
ProjectSection(SolutionItems) = preProject
@@ -87,7 +87,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Client", "src\Microsoft.AspNetCore.SignalR.Client\Microsoft.AspNetCore.SignalR.Client.csproj", "{BE982591-F4BB-42D9-ABD4-A5D44C65971E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Redis.Tests", "test\Microsoft.AspNetCore.SignalR.Redis.Tests\Microsoft.AspNetCore.SignalR.Redis.Tests.csproj", "{0B083AE6-86CA-4E0B-AE02-59154D1FD005}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Redis.Tests", "test\Microsoft.AspNetCore.SignalR.Redis.Tests\Microsoft.AspNetCore.SignalR.Redis.Tests.csproj", "{0B083AE6-86CA-4E0B-AE02-59154D1FD005}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JwtSample", "samples\JwtSample\JwtSample.csproj", "{6A7491D3-3C97-49BD-A71C-433AED657F30}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -195,6 +197,10 @@ Global
{0B083AE6-86CA-4E0B-AE02-59154D1FD005}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B083AE6-86CA-4E0B-AE02-59154D1FD005}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B083AE6-86CA-4E0B-AE02-59154D1FD005}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6A7491D3-3C97-49BD-A71C-433AED657F30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6A7491D3-3C97-49BD-A71C-433AED657F30}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6A7491D3-3C97-49BD-A71C-433AED657F30}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6A7491D3-3C97-49BD-A71C-433AED657F30}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -226,6 +232,7 @@ Global
{E081EE41-D95F-4AD2-BC0B-4B562C0A2A47} = {DA69F624-5398-4884-87E4-B816698CDE65}
{BE982591-F4BB-42D9-ABD4-A5D44C65971E} = {DA69F624-5398-4884-87E4-B816698CDE65}
{0B083AE6-86CA-4E0B-AE02-59154D1FD005} = {6A35B453-52EC-48AF-89CA-D4A69800F131}
+ {6A7491D3-3C97-49BD-A71C-433AED657F30} = {C4BC9889-B49F-41B6-806B-F84941B2549B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7945A4E4-ACDB-4F6E-95CA-6AC6E7C2CD59}
diff --git a/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/EchoEndPoint.cs b/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/EchoEndPoint.cs
index 433532b432..e70b668e08 100644
--- a/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/EchoEndPoint.cs
+++ b/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/EchoEndPoint.cs
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.Sockets;
diff --git a/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/Program.cs b/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/Program.cs
index 00aa867bd1..b2a6f5898a 100644
--- a/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/Program.cs
+++ b/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/Program.cs
@@ -1,11 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-using System;
-using System.Collections.Generic;
using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
@@ -16,7 +12,6 @@ namespace Microsoft.AspNetCore.SignalR.Test.Server
public static void Main(string[] args)
{
var host = new WebHostBuilder()
- .UseSetting(WebHostDefaults.PreventHostingStartupKey, "true") // Work around https://github.com/aspnet/Hosting/issues/1075
.ConfigureLogging(factory =>
{
factory.AddConsole();
diff --git a/samples/JwtSample/Broadcaster.cs b/samples/JwtSample/Broadcaster.cs
new file mode 100644
index 0000000000..42408af174
--- /dev/null
+++ b/samples/JwtSample/Broadcaster.cs
@@ -0,0 +1,17 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.SignalR;
+
+namespace JwtSample
+{
+ [Authorize(JwtBearerDefaults.AuthenticationScheme)]
+ public class Broadcaster : Hub
+ {
+ public Task Broadcast(string sender, string message) =>
+ Clients.All.InvokeAsync("Message", sender, message);
+ }
+}
diff --git a/samples/JwtSample/JwtSample.csproj b/samples/JwtSample/JwtSample.csproj
new file mode 100644
index 0000000000..4a7f3c5078
--- /dev/null
+++ b/samples/JwtSample/JwtSample.csproj
@@ -0,0 +1,27 @@
+
+
+
+ netcoreapp2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/JwtSample/Program.cs b/samples/JwtSample/Program.cs
new file mode 100644
index 0000000000..a4bff29a37
--- /dev/null
+++ b/samples/JwtSample/Program.cs
@@ -0,0 +1,29 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.IO;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace JwtSample
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ new WebHostBuilder()
+ .ConfigureLogging(factory =>
+ {
+ factory.AddConsole();
+ factory.AddFilter("Console", level => level >= LogLevel.Information);
+ factory.AddDebug();
+ })
+ .UseKestrel()
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .UseIISIntegration()
+ .UseStartup()
+ .Build()
+ .Run();
+ }
+ }
+}
diff --git a/samples/JwtSample/Properties/launchSettings.json b/samples/JwtSample/Properties/launchSettings.json
new file mode 100644
index 0000000000..70acec6066
--- /dev/null
+++ b/samples/JwtSample/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:54542/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "JwtSample": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:54543/"
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/JwtSample/Startup.cs b/samples/JwtSample/Startup.cs
new file mode 100644
index 0000000000..46f9598cfb
--- /dev/null
+++ b/samples/JwtSample/Startup.cs
@@ -0,0 +1,83 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.IdentityModel.Tokens;
+
+namespace JwtSample
+{
+ public class Startup
+ {
+ private readonly SymmetricSecurityKey SecurityKey = new SymmetricSecurityKey(Guid.NewGuid().ToByteArray());
+ private readonly JwtSecurityTokenHandler JwtTokenHandler = new JwtSecurityTokenHandler();
+
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddSignalR();
+ services.AddAuthorization(options =>
+ {
+ options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, policy =>
+ {
+ policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
+ policy.RequireClaim(ClaimTypes.NameIdentifier);
+ });
+ });
+
+ services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
+ .AddJwtBearer(options =>
+ {
+ options.TokenValidationParameters =
+ new TokenValidationParameters
+ {
+ ValidateAudience = false,
+ ValidateIssuer = false,
+ ValidateActor = false,
+ ValidateLifetime = true,
+ IssuerSigningKey = SecurityKey
+ };
+
+ options.Events = new JwtBearerEvents
+ {
+ OnMessageReceived = context =>
+ {
+ var signalRTokenHeader = context.Request.Query["signalRTokenHeader"];
+
+ if (!string.IsNullOrEmpty(signalRTokenHeader) &&
+ (context.HttpContext.WebSockets.IsWebSocketRequest || context.Request.Headers["Accept"] == "text/event-stream"))
+ {
+ context.Token = context.Request.Query["signalRTokenHeader"];
+ }
+ return Task.CompletedTask;
+ }
+ };
+ });
+ }
+
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ app.UseFileServer();
+ app.UseSignalR(options => options.MapHub("broadcast"));
+
+ var routeBuilder = new RouteBuilder(app);
+ routeBuilder.MapGet("generatetoken", c => c.Response.WriteAsync(GenerateToken(c)));
+ app.UseRouter(routeBuilder.Build());
+ }
+
+ private string GenerateToken(HttpContext httpContext)
+ {
+ var claims = new[] { new Claim(ClaimTypes.NameIdentifier, httpContext.Request.Query["user"]) };
+ var credentials = new SigningCredentials(SecurityKey, SecurityAlgorithms.HmacSha256);
+ var token = new JwtSecurityToken("SignalRTestServer", "SignalRTests", claims, expires: DateTime.Now.AddSeconds(30), signingCredentials: credentials);
+ return JwtTokenHandler.WriteToken(token);
+ }
+ }
+}
diff --git a/samples/JwtSample/wwwroot/index.html b/samples/JwtSample/wwwroot/index.html
new file mode 100644
index 0000000000..c53acbac9a
--- /dev/null
+++ b/samples/JwtSample/wwwroot/index.html
@@ -0,0 +1,104 @@
+
+
+
+
+ SignalR JWT Sample
+
+
+
+
+
+
+
+
+