From a4dd0cd9d7fcf103442af752410c61fb30a983cd Mon Sep 17 00:00:00 2001 From: Pawel Kadluczka Date: Thu, 16 Nov 2017 16:00:15 -0800 Subject: [PATCH] JWT JS Sample --- SignalR.sln | 11 +- .../EchoEndPoint.cs | 1 - .../Program.cs | 5 - samples/JwtSample/Broadcaster.cs | 17 +++ samples/JwtSample/JwtSample.csproj | 27 +++++ samples/JwtSample/Program.cs | 29 +++++ .../JwtSample/Properties/launchSettings.json | 27 +++++ samples/JwtSample/Startup.cs | 83 ++++++++++++++ samples/JwtSample/wwwroot/index.html | 104 ++++++++++++++++++ 9 files changed, 296 insertions(+), 8 deletions(-) create mode 100644 samples/JwtSample/Broadcaster.cs create mode 100644 samples/JwtSample/JwtSample.csproj create mode 100644 samples/JwtSample/Program.cs create mode 100644 samples/JwtSample/Properties/launchSettings.json create mode 100644 samples/JwtSample/Startup.cs create mode 100644 samples/JwtSample/wwwroot/index.html 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 + + +
+ +
+ + + +