diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..288a4cb Binary files /dev/null and b/.DS_Store differ diff --git a/Molaware.Nancy.Auth.JWT.sln b/Molaware.Nancy.Auth.JWT.sln new file mode 100644 index 0000000..81f3a6f --- /dev/null +++ b/Molaware.Nancy.Auth.JWT.sln @@ -0,0 +1,35 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TryNancyNginx", "TryNancyNginx\TryNancyNginx.csproj", "{F83D14AC-808C-4D0D-A6CE-45AB8DC88631}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TryOwinSelfHost", "TryOwinSelfHost\TryOwinSelfHost.csproj", "{F7E68CBE-ADE2-44E4-BDBB-ED226016C813}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Molaware.Nancy.Auth.JWT", "Molaware.Nancy.Auth.JWT\Molaware.Nancy.Auth.JWT.csproj", "{78E10B42-9DF5-4534-82E2-02D9983FAB03}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestSelfhostJwt", "TestSelfhostJwt\TestSelfhostJwt.csproj", "{C6FA3DBF-B406-4AD4-B3CE-394A5FC52DD0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {78E10B42-9DF5-4534-82E2-02D9983FAB03}.Debug|x86.ActiveCfg = Debug|Any CPU + {78E10B42-9DF5-4534-82E2-02D9983FAB03}.Debug|x86.Build.0 = Debug|Any CPU + {78E10B42-9DF5-4534-82E2-02D9983FAB03}.Release|x86.ActiveCfg = Release|Any CPU + {78E10B42-9DF5-4534-82E2-02D9983FAB03}.Release|x86.Build.0 = Release|Any CPU + {C6FA3DBF-B406-4AD4-B3CE-394A5FC52DD0}.Debug|x86.ActiveCfg = Debug|x86 + {C6FA3DBF-B406-4AD4-B3CE-394A5FC52DD0}.Debug|x86.Build.0 = Debug|x86 + {C6FA3DBF-B406-4AD4-B3CE-394A5FC52DD0}.Release|x86.ActiveCfg = Release|x86 + {C6FA3DBF-B406-4AD4-B3CE-394A5FC52DD0}.Release|x86.Build.0 = Release|x86 + {F7E68CBE-ADE2-44E4-BDBB-ED226016C813}.Debug|x86.ActiveCfg = Debug|x86 + {F7E68CBE-ADE2-44E4-BDBB-ED226016C813}.Debug|x86.Build.0 = Debug|x86 + {F7E68CBE-ADE2-44E4-BDBB-ED226016C813}.Release|x86.ActiveCfg = Release|x86 + {F7E68CBE-ADE2-44E4-BDBB-ED226016C813}.Release|x86.Build.0 = Release|x86 + {F83D14AC-808C-4D0D-A6CE-45AB8DC88631}.Debug|x86.ActiveCfg = Debug|x86 + {F83D14AC-808C-4D0D-A6CE-45AB8DC88631}.Debug|x86.Build.0 = Debug|x86 + {F83D14AC-808C-4D0D-A6CE-45AB8DC88631}.Release|x86.ActiveCfg = Release|x86 + {F83D14AC-808C-4D0D-A6CE-45AB8DC88631}.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal diff --git a/Molaware.Nancy.Auth.JWT/.DS_Store b/Molaware.Nancy.Auth.JWT/.DS_Store new file mode 100644 index 0000000..f359617 Binary files /dev/null and b/Molaware.Nancy.Auth.JWT/.DS_Store differ diff --git a/Molaware.Nancy.Auth.JWT/AuthOptions.cs b/Molaware.Nancy.Auth.JWT/AuthOptions.cs new file mode 100644 index 0000000..3655a58 --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/AuthOptions.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace Molaware.Nancy.Auth.JWT +{ + public class AuthOptions + { + public IEnumerable IgnorePaths { get; set; } + public string WWWAuthenticationChallenge { get; set; } + public bool PassThruUnAuthorizedRequests { get; set; } + } +} + diff --git a/Molaware.Nancy.Auth.JWT/Bootstrapper.cs b/Molaware.Nancy.Auth.JWT/Bootstrapper.cs new file mode 100644 index 0000000..43e4ae3 --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/Bootstrapper.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq; +using Nancy; +using Nancy.Bootstrapper; +using Nancy.TinyIoc; + +namespace Molaware.Nancy.Auth.JWT +{ + public class Bootstrapper : DefaultNancyBootstrapper + { + + protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) + { + base.ApplicationStartup(container, pipelines); + + + if (!container.CanResolve()) + return; // if no implementation detected, no actions will be made + + this.RegisterDependencies(container); + + + + var auth = container.Resolve(); + + auth.Enable(pipelines); + } + + private void RegisterDependencies(TinyIoCContainer container) + { + + #if DEBUG + var securekeyProvider = container.Resolve(); + Console.WriteLine("application: " + securekeyProvider.GetType().FullName); + #endif + + + + container.Register(); // always use this one + + } + } +} + diff --git a/Molaware.Nancy.Auth.JWT/CheckAuthOnStartup.cs b/Molaware.Nancy.Auth.JWT/CheckAuthOnStartup.cs new file mode 100644 index 0000000..4d851d8 --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/CheckAuthOnStartup.cs @@ -0,0 +1,28 @@ +using System; +using Nancy; +using Nancy.Bootstrapper; +using Nancy.TinyIoc; + +namespace Molaware.Nancy.Auth.JWT +{ + public class CheckAuthOnStartup : IApplicationStartup + { + private readonly StatelessAuth auth; + + public CheckAuthOnStartup(AuthOptions options) + { + var validator = TinyIoCContainer.Current.Resolve(); + this.auth = new StatelessAuth(validator, options); + } + + #region IApplicationStartup implementation + + public void Initialize(IPipelines pipelines) + { + this.auth.Enable(pipelines); + } + + #endregion + } +} + diff --git a/Molaware.Nancy.Auth.JWT/DefaultSecurekeyProvider.cs b/Molaware.Nancy.Auth.JWT/DefaultSecurekeyProvider.cs new file mode 100644 index 0000000..196f7e3 --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/DefaultSecurekeyProvider.cs @@ -0,0 +1,20 @@ +using System; + +namespace Molaware.Nancy.Auth.JWT +{ + class DefaultSecurekeyProvider : ISecurekeyProvider + { + #region ISecurekeyProvider implementation + + public string GetSecurekey() + { + return System.Configuration.ConfigurationManager.AppSettings["securekey"] ?? "Molaware0x10"; + } + + + #endregion + + + } +} + diff --git a/Molaware.Nancy.Auth.JWT/DefaultTokenUserMapper.cs b/Molaware.Nancy.Auth.JWT/DefaultTokenUserMapper.cs new file mode 100644 index 0000000..e02c48d --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/DefaultTokenUserMapper.cs @@ -0,0 +1,20 @@ +using System; +using Nancy.Security; + +namespace Molaware.Nancy.Auth.JWT +{ + class DefaultTokenUserMapper : ITokenUserMapper + { + #region ITokenUserMapper implementation + public IUserIdentity Convert(JwtToken token) + { + return new DefaultUserIdentity { + UserName = token.UserName, + Claims = token.Claims + }; + } + #endregion + + } +} + diff --git a/Molaware.Nancy.Auth.JWT/DefaultUserIdentity.cs b/Molaware.Nancy.Auth.JWT/DefaultUserIdentity.cs new file mode 100644 index 0000000..06dc44e --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/DefaultUserIdentity.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using Nancy.Security; + +namespace Molaware.Nancy.Auth.JWT +{ + public class DefaultUserIdentity : IUserIdentity + { + public string UserName { get; set; } + + public IEnumerable Claims { get; set; } + } +} + diff --git a/Molaware.Nancy.Auth.JWT/IAuthOptionsProvider.cs b/Molaware.Nancy.Auth.JWT/IAuthOptionsProvider.cs new file mode 100644 index 0000000..4dd7df1 --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/IAuthOptionsProvider.cs @@ -0,0 +1,10 @@ +using System; + +namespace Molaware.Nancy.Auth.JWT +{ + public interface IAuthOptionsProvider + { + AuthOptions Configure(); + } +} + diff --git a/Molaware.Nancy.Auth.JWT/ISecurekeyProvider.cs b/Molaware.Nancy.Auth.JWT/ISecurekeyProvider.cs new file mode 100644 index 0000000..8a2294d --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/ISecurekeyProvider.cs @@ -0,0 +1,11 @@ +using System; + +namespace Molaware.Nancy.Auth.JWT +{ + public interface ISecurekeyProvider + { + string GetSecurekey(); + + } +} + diff --git a/Molaware.Nancy.Auth.JWT/ITokenUserMapper.cs b/Molaware.Nancy.Auth.JWT/ITokenUserMapper.cs new file mode 100644 index 0000000..c9205df --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/ITokenUserMapper.cs @@ -0,0 +1,11 @@ +using System; +using Nancy.Security; + +namespace Molaware.Nancy.Auth.JWT +{ + public interface ITokenUserMapper + { + IUserIdentity Convert(JwtToken token); + } +} + diff --git a/Molaware.Nancy.Auth.JWT/ITokenValidator.cs b/Molaware.Nancy.Auth.JWT/ITokenValidator.cs new file mode 100644 index 0000000..fae78d6 --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/ITokenValidator.cs @@ -0,0 +1,11 @@ +using System; +using Nancy.Security; + +namespace Molaware.Nancy.Auth.JWT +{ + interface ITokenValidator + { + IUserIdentity ValidateUser(string token); + } +} + diff --git a/Molaware.Nancy.Auth.JWT/JwtAuthExtensions.cs b/Molaware.Nancy.Auth.JWT/JwtAuthExtensions.cs new file mode 100644 index 0000000..e093c5b --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/JwtAuthExtensions.cs @@ -0,0 +1,21 @@ +using System; +using Nancy; +using Nancy.TinyIoc; + +namespace Molaware.Nancy.Auth.JWT +{ + public static class JwtAuthExtensions + { + public static string JwtToken(this NancyModule module, JwtToken token, ISecurekeyProvider keyProvider) + { + #if DEBUG + Console.WriteLine("request: " + keyProvider.GetType().FullName); + #endif + + var encoder = new JwtTokenEncoder(keyProvider); + string encoded = encoder.Encode(token); + return encoded; + } + } +} + diff --git a/Molaware.Nancy.Auth.JWT/JwtToken.cs b/Molaware.Nancy.Auth.JWT/JwtToken.cs new file mode 100644 index 0000000..093cbbb --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/JwtToken.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Molaware.Nancy.Auth.JWT +{ + public class JwtToken + { + public string UserName { get; set; } + + public DateTime Expire { get; set; } + + public IEnumerable Claims { get; set; } + } +} + diff --git a/Molaware.Nancy.Auth.JWT/JwtTokenEncoder.cs b/Molaware.Nancy.Auth.JWT/JwtTokenEncoder.cs new file mode 100644 index 0000000..d50874c --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/JwtTokenEncoder.cs @@ -0,0 +1,21 @@ +using System; +using JWT; + +namespace Molaware.Nancy.Auth.JWT +{ + class JwtTokenEncoder + { + private readonly ISecurekeyProvider key; + + public JwtTokenEncoder(ISecurekeyProvider key) + { + this.key = key; + } + + public string Encode(JwtToken token) + { + return JsonWebToken.Encode(token, this.key.GetSecurekey(), JwtHashAlgorithm.HS256); + } + } +} + diff --git a/Molaware.Nancy.Auth.JWT/Molaware.Nancy.Auth.JWT.csproj b/Molaware.Nancy.Auth.JWT/Molaware.Nancy.Auth.JWT.csproj new file mode 100644 index 0000000..91c4df6 --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/Molaware.Nancy.Auth.JWT.csproj @@ -0,0 +1,65 @@ + + + + Debug + AnyCPU + {78E10B42-9DF5-4534-82E2-02D9983FAB03} + Library + Molaware.Nancy.Auth.JWT + Molaware.Nancy.Auth.JWT + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + + ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll + + + ..\packages\Minimatch.1.1.0.0\lib\portable-net40+sl50+win+wp80\Minimatch.dll + + + + ..\packages\JWT.1.3.4\lib\3.5\JWT.dll + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Molaware.Nancy.Auth.JWT/MyClass.cs b/Molaware.Nancy.Auth.JWT/MyClass.cs new file mode 100644 index 0000000..f51845b --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/MyClass.cs @@ -0,0 +1,12 @@ +using System; + +namespace Molaware.Nancy.Auth.JWT +{ + public class MyClass + { + public MyClass() + { + } + } +} + diff --git a/Molaware.Nancy.Auth.JWT/MyJwtTokenValidor.cs b/Molaware.Nancy.Auth.JWT/MyJwtTokenValidor.cs new file mode 100644 index 0000000..b60e134 --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/MyJwtTokenValidor.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using Nancy.Security; +using JWT; + +namespace Molaware.Nancy.Auth.JWT +{ + sealed class MyJwtTokenValidor : ITokenValidator + { + + private readonly ISecurekeyProvider secret; + private readonly ITokenUserMapper mapper; + + public MyJwtTokenValidor(ISecurekeyProvider securekeyProvider, ITokenUserMapper userMapper) + { + this.secret = securekeyProvider; + this.mapper = userMapper; + } + + + #region ITokenValidator implementation + + public IUserIdentity ValidateUser(string token) + { + try + { + string securekey = this.secret.GetSecurekey(); + + #if DEBUG + Console.WriteLine(securekey); + #endif + + var decoded = JsonWebToken.DecodeToObject(token, securekey) as Dictionary; + + JwtToken tk = new JwtToken + { + UserName = decoded["UserName"].ToString(), + Expire = DateTime.Parse(decoded["Expire"].ToString()) + }; + + if (decoded.ContainsKey("Claims")) + { + var claims = new List(); + var decodedClaims = (ArrayList)decoded["Claims"]; + for (int i = 0; i < decodedClaims.Count; i++) + { + string claim = decodedClaims[i].ToString(); + claims.Add(claim); + } + + tk.Claims = claims; + } + + if (tk.Expire < DateTime.UtcNow) // expires + return null; + + return this.mapper.Convert(tk); + } + catch (Exception) + { + return null; + } + } + + #endregion + } +} + diff --git a/Molaware.Nancy.Auth.JWT/Properties/AssemblyInfo.cs b/Molaware.Nancy.Auth.JWT/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a7e473f --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("Molaware.Nancy.Auth.JWT")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("libin")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/Molaware.Nancy.Auth.JWT/StatelessAuth.cs b/Molaware.Nancy.Auth.JWT/StatelessAuth.cs new file mode 100644 index 0000000..4fd2dbb --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/StatelessAuth.cs @@ -0,0 +1,88 @@ +using System; +using System.Threading.Tasks; +using System.Linq; +using System.Collections.Generic; + +using Nancy; +using Nancy.Bootstrapper; + +namespace Molaware.Nancy.Auth.JWT +{ + class StatelessAuth + { + private readonly ITokenValidator validator; + private readonly IAuthOptionsProvider optionsProvider; + private readonly AuthOptions options; + + private const string TokenName = "Authorization"; + + public StatelessAuth(ITokenValidator tokenValidator, IAuthOptionsProvider optionsProvider) + { + this.validator = tokenValidator; + this.optionsProvider = optionsProvider; + + this.options = this.optionsProvider.Configure(); + } + + public void Enable(IPipelines pipelines) + { + pipelines.BeforeRequest.AddItemToStartOfPipeline(CheckToken); + } + + private Response CheckToken(NancyContext context) + { + var path = Uri.UnescapeDataString(context.Request.Path); + + // check paths to ignore auth checking + if (this.options != null && this.options.IgnorePaths != null) + { + foreach (var ignore in this.options.IgnorePaths) + { + var matcher = new Minimatch.Minimatcher(ignore, new Minimatch.Options { IgnoreCase = true }); + + if (matcher.IsMatch(path)) + { + return null; + } + } + } + + // check auth header token + string token = context.Request.Headers.Authorization; + if (string.IsNullOrWhiteSpace(token)) + return AuthChallengeResponse(context); + + var user = this.validator.ValidateUser(token); + + if (user == null) + return AuthChallengeResponse(context); + + // token is valid, set it to CurrentUser + context.CurrentUser = user; + + return null; + } + + private Response AuthChallengeResponse(NancyContext context) + { + if (this.options != null && this.options.PassThruUnAuthorizedRequests) // just pass thru + return null; // do nothing + + var resp = new Response(); + resp.StatusCode = HttpStatusCode.Unauthorized; + + // set www-authentication header + if (this.options != null && !string.IsNullOrWhiteSpace(this.options.WWWAuthenticationChallenge)) + { + string challengePath = this.options.WWWAuthenticationChallenge; + + resp.WithHeader("WWW-Authenticate", challengePath); + + } + + return resp; + + } + } +} + diff --git a/Molaware.Nancy.Auth.JWT/packages.config b/Molaware.Nancy.Auth.JWT/packages.config new file mode 100644 index 0000000..7c8b208 --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Molaware.Nancy.Auth.JWT/readme.md b/Molaware.Nancy.Auth.JWT/readme.md new file mode 100644 index 0000000..b4e47bd --- /dev/null +++ b/Molaware.Nancy.Auth.JWT/readme.md @@ -0,0 +1,20 @@ + +Authentication by JWT (Json Web Token) + +works by checking the Authorization header: + Authorization: + +returns http status code 401 if the request does not contains a valid Authorization header or the token value is not valid; +returns http status code 403 if the authenticated user is not qualified for resources he requests; + + +usage: + 1. reference this dll. + 2. add a class implementing the interface IAuthOptionsProvider + 3. in your app.config, add . NOTE: this step is optional + 4. and you're done. use user related functions such as CurrentUser, RequiresClaims normally. + +advanced: + add a class implementing ISecurekeyProvider to customize the secure key used by JWT encoder; + inherit class DefaultUserIdentity if you need, implement the ITokenUserMapper interface which extact useridentity from jwt token value; + inherit from Molaware.Nancy.Auth.JWT.Bootstrapper if you need your own Nancy bootstrapper; \ No newline at end of file diff --git a/TestSelfhostJwt/AuthOptionsProvider.cs b/TestSelfhostJwt/AuthOptionsProvider.cs new file mode 100644 index 0000000..bdca348 --- /dev/null +++ b/TestSelfhostJwt/AuthOptionsProvider.cs @@ -0,0 +1,22 @@ +using System; +using Molaware.Nancy.Auth.JWT; + +namespace TestSelfhostJwt +{ + public class AuthOptionsProvider : IAuthOptionsProvider + { + // add a file like this to Enable the JWT Auth thing. + // if not present, auth will not take effect + + public AuthOptions Configure() + { + return new AuthOptions { + IgnorePaths = new[] { "/login", "/info", "/" }, + WWWAuthenticationChallenge = "/login" + }; + } + + + } +} + diff --git a/TestSelfhostJwt/DemoSecurekeyProvider.cs b/TestSelfhostJwt/DemoSecurekeyProvider.cs new file mode 100644 index 0000000..e13088e --- /dev/null +++ b/TestSelfhostJwt/DemoSecurekeyProvider.cs @@ -0,0 +1,22 @@ +using System; +using Molaware.Nancy.Auth.JWT; + +namespace TestSelfhostJwt +{ + public class DemoSecurekeyProvider : ISecurekeyProvider + { + #region ISecurekeyProvider implementation + + public string GetSecurekey() + { + return "my secure key"; + } + + + + #endregion + + + } +} + diff --git a/TestSelfhostJwt/HelloModule.cs b/TestSelfhostJwt/HelloModule.cs new file mode 100644 index 0000000..a459a98 --- /dev/null +++ b/TestSelfhostJwt/HelloModule.cs @@ -0,0 +1,94 @@ +using System; +using System.Linq; + +using Nancy; +using Nancy.ModelBinding; +using Nancy.Security; + +using Molaware.Nancy.Auth.JWT; + +namespace TestSelfhostJwt +{ + public class HelloModule : NancyModule + { + + public HelloModule(ISecurekeyProvider securekey) // passing this dependency + { + StaticConfiguration.DisableErrorTraces = false; + + Get["/"] = x => "Hello World from SelfHosting with JWT auth"; // should be ignored hereby auth option + + + Get["/safeapi"] = x => + { + this.RequiresClaims(new[] { "gender:man" }); // more checks than default + + var data = new[] { + new Data { Id = 1, Name = "one", Date = DateTime.Today }, + new Data { Id = 2, Name = "two", Date = DateTime.Now }, + }; + + return data; + }; + + Get["/api"] = x => + { + + var data = new[] { + new Data { Id = 1, Name = "one", Date = DateTime.Today }, + new Data { Id = 2, Name = "two", Date = DateTime.Now }, + }; + + return data; + }; + + + Post["/login"] = x => // should be ignored hereby auth option + { + var info = this.Bind(); + + if (info.UserName == "cate") // ok + { + var jwt = new JwtToken { + UserName = info.UserName, + Expire = DateTime.UtcNow.AddDays(2), + Claims = new[] { "age:20", "gender:woman" } + }; + + return this.JwtToken(jwt, securekey); + } + else if (info.UserName == "bob") + { + var jwt = new JwtToken { + UserName = info.UserName, + Expire = DateTime.UtcNow.AddDays(2), + Claims = new[] { "age:20", "gender:man" } + }; + + return this.JwtToken(jwt, securekey); + } + + return HttpStatusCode.Unauthorized; + }; + + + } + } + + + class LoginInfo + { + public string UserName { get; set; } + public string Password { get; set; } + } + + public class Data // need to be public when auto content-negotiation + { + public int Id { get; set; } + public string Name { get; set; } + public DateTime Date { get; set; } + } + + +} + diff --git a/TestSelfhostJwt/Program.cs b/TestSelfhostJwt/Program.cs new file mode 100644 index 0000000..7f955a1 --- /dev/null +++ b/TestSelfhostJwt/Program.cs @@ -0,0 +1,29 @@ +using System; +using Nancy.Hosting.Self; + +namespace TestSelfhostJwt +{ + class MainClass + { + public static void Main(string[] args) + { + string uri = "http://localhost:8899"; + + Console.WriteLine($"starting Nancy on {uri}"); + + // new a nancy host + var conf = new HostConfiguration() { + RewriteLocalhost = false + }; + + var host = new NancyHost(conf, new Uri(uri)); + //var host = new NancyHost(new Uri(uri)); + host.Start(); + + Console.ReadLine(); + + Console.WriteLine("stopping Nancy"); + host.Stop(); + } + } +} diff --git a/TestSelfhostJwt/Properties/AssemblyInfo.cs b/TestSelfhostJwt/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c4d74ad --- /dev/null +++ b/TestSelfhostJwt/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("TestSelfhostJwt")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("libin")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/TestSelfhostJwt/TestSelfhostJwt.csproj b/TestSelfhostJwt/TestSelfhostJwt.csproj new file mode 100644 index 0000000..0865ba2 --- /dev/null +++ b/TestSelfhostJwt/TestSelfhostJwt.csproj @@ -0,0 +1,58 @@ + + + + Debug + x86 + {C6FA3DBF-B406-4AD4-B3CE-394A5FC52DD0} + Exe + TestSelfhostJwt + TestSelfhostJwt + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + + + full + true + bin\Release + prompt + 4 + true + x86 + + + + + ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll + + + ..\packages\Nancy.Hosting.Self.1.4.1\lib\net40\Nancy.Hosting.Self.dll + + + + + + + + + + + + + + + + {78E10B42-9DF5-4534-82E2-02D9983FAB03} + Molaware.Nancy.Auth.JWT + + + \ No newline at end of file diff --git a/TestSelfhostJwt/packages.config b/TestSelfhostJwt/packages.config new file mode 100644 index 0000000..4ac39d3 --- /dev/null +++ b/TestSelfhostJwt/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/TryNancyNginx/.DS_Store b/TryNancyNginx/.DS_Store new file mode 100644 index 0000000..8a30732 Binary files /dev/null and b/TryNancyNginx/.DS_Store differ diff --git a/TryNancyNginx/HelloModule.cs b/TryNancyNginx/HelloModule.cs new file mode 100644 index 0000000..b5be0d7 --- /dev/null +++ b/TryNancyNginx/HelloModule.cs @@ -0,0 +1,89 @@ +using System; +using Nancy; +using Nancy.ModelBinding; +using Nancy.Swagger; + +namespace TryNancyNginx +{ + public class HelloModule : NancyModule + { + + public HelloModule() + { + StaticConfiguration.DisableErrorTraces = false; + + Get["index", "/"] = x => "Hello World"; + + Get["entry", "/api"] = x => + { + var data = new[] { + new Data { Id = 1, Name = "one", Date = DateTime.Today }, + new Data { Id = 2, Name = "two", Date = DateTime.Now }, + }; + + return data; + }; + + + Get["single", "/api/{id}"] = x => + { + string _id = x.id; + + int id; + if (!int.TryParse(_id, out id)) + return 400; // bad request + + return new Data{ Id = id, Name = "one", Date = DateTime.Now}; + }; + + Post["post", "/api"] = x => + { + Data posted = this.Bind("date"); + // more specific handling or model error; + // pipelines.OnError handle this in a general way +// try +// { +// posted = this.Bind("date"); +// } +// catch +// { +// return HttpStatusCode.BadRequest; +// } + + System.Diagnostics.Debug.Assert(posted != null); + + posted.Date = DateTime.Today.AddYears(1); + return posted; + }; + + } + } + + + // naming is essential here: {name}MetadataModule + public class HelloMetadataModule : Nancy.Metadata.Modules.MetadataModule + { + public HelloMetadataModule() + { + Describe["entry"] = description => description.AsSwagger(with => + { + with.ResourcePath("/api"); + with.Summary("The list of data"); + with.Notes("This returns a list of data from our awesome app"); + with.Model(); + }); + + } + } + + + public class Data // need to be public when auto content-negotiation + { + public int Id { get; set; } + public string Name { get; set; } + public DateTime Date { get; set; } + } + + +} + diff --git a/TryNancyNginx/LoggingBeforeRequests.cs b/TryNancyNginx/LoggingBeforeRequests.cs new file mode 100644 index 0000000..f5deb77 --- /dev/null +++ b/TryNancyNginx/LoggingBeforeRequests.cs @@ -0,0 +1,35 @@ +using System; +using Nancy; +using Nancy.Bootstrapper; + +namespace TryNancyNginx +{ + public class LoggingBeforeRequests : IRequestStartup + { + + + public LoggingBeforeRequests() + { + } + + #region IRequestStartup implementation + + public void Initialize(IPipelines pipelines, NancyContext context) + { + pipelines.BeforeRequest.AddItemToEndOfPipeline(ctx => { + + Console.WriteLine(ctx.Request.Method + " " + ctx.Request.Url.ToString()); // log to console + + return ctx.Response; + }); + + pipelines.OnError.AddItemToEndOfPipeline((ctx, ex) => + { + return HttpStatusCode.BadRequest; + }); + } + + #endregion + } +} + diff --git a/TryNancyNginx/MyBootstrapper.cs b/TryNancyNginx/MyBootstrapper.cs new file mode 100644 index 0000000..20aaa3e --- /dev/null +++ b/TryNancyNginx/MyBootstrapper.cs @@ -0,0 +1,16 @@ +using System; +using Nancy.Conventions; + +namespace TryNancyNginx +{ + public class MyBootstrapper : Nancy.DefaultNancyBootstrapper + { + protected override void ConfigureConventions(NancyConventions nancyConventions) + { + //base.ConfigureConventions(nancyConventions); + + nancyConventions.StaticContentsConventions.AddDirectory("doc", "swagger-ui"); + } + } +} + diff --git a/TryNancyNginx/Program.cs b/TryNancyNginx/Program.cs new file mode 100644 index 0000000..d0bdb5c --- /dev/null +++ b/TryNancyNginx/Program.cs @@ -0,0 +1,45 @@ +using System; +using Nancy.Hosting.Self; +using Mono.Unix; +using Mono.Unix.Native; + +namespace TryNancyNginx +{ + class MainClass + { + public static void Main(string[] args) + { + //string uri = "http://127.0.0.1:8888"; // this works without the conf + string uri = "http://localhost:8888"; + + Console.WriteLine($"starting Nancy on {uri}"); + + // new a nancy host + var conf = new HostConfiguration() { + RewriteLocalhost = false + }; + + var host = new NancyHost(conf, new Uri(uri)); + //var host = new NancyHost(new Uri(uri)); + host.Start(); + + // if running on Mono + if (Type.GetType("Mono.Runtime") != null) + { + UnixSignal.WaitAny(new [] { + new UnixSignal(Signum.SIGINT), + new UnixSignal(Signum.SIGTERM), + new UnixSignal(Signum.SIGQUIT), + new UnixSignal(Signum.SIGHUP), + }); + } + else + { + Console.ReadLine(); + } + + Console.WriteLine("stopping Nancy"); + host.Stop(); + } + } +} diff --git a/TryNancyNginx/Properties/AssemblyInfo.cs b/TryNancyNginx/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..48d3e04 --- /dev/null +++ b/TryNancyNginx/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("TryNancyNginx")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("libin")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/TryNancyNginx/TryNancyNginx.csproj b/TryNancyNginx/TryNancyNginx.csproj new file mode 100644 index 0000000..966d51f --- /dev/null +++ b/TryNancyNginx/TryNancyNginx.csproj @@ -0,0 +1,65 @@ + + + + Debug + x86 + {F83D14AC-808C-4D0D-A6CE-45AB8DC88631} + Exe + TryNancyNginx + TryNancyNginx + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + + + full + true + bin\Release + prompt + 4 + true + x86 + + + + + ..\packages\Nancy.Hosting.Self.1.4.1\lib\net40\Nancy.Hosting.Self.dll + + + ..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll + + + + ..\packages\Swagger.ObjectModel.0.1.0-alpha3\lib\net40\Swagger.ObjectModel.dll + + + ..\packages\Nancy.Swagger.0.1.0-alpha3\lib\net40\Nancy.Swagger.dll + + + ..\packages\Nancy.Metadata.Modules.1.4.1\lib\net40\Nancy.Metadata.Modules.dll + + + ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TryNancyNginx/packages.config b/TryNancyNginx/packages.config new file mode 100644 index 0000000..9ecccd7 --- /dev/null +++ b/TryNancyNginx/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/TryOwinSelfHost/.DS_Store b/TryOwinSelfHost/.DS_Store new file mode 100644 index 0000000..8901944 Binary files /dev/null and b/TryOwinSelfHost/.DS_Store differ diff --git a/TryOwinSelfHost/AuthOptionsProvider.cs b/TryOwinSelfHost/AuthOptionsProvider.cs new file mode 100644 index 0000000..63997b7 --- /dev/null +++ b/TryOwinSelfHost/AuthOptionsProvider.cs @@ -0,0 +1,22 @@ +using System; +using Molaware.Nancy.Auth.JWT; + +namespace TryOwinSelfHost +{ + public class AuthOptionsProvider : IAuthOptionsProvider + { + // add a file like this to Enable the JWT Auth thing. + // if not present, auth will not take effect + + public AuthOptions Configure() + { + return new AuthOptions { + IgnorePaths = new[] { "/login", "/" }, + WWWAuthenticationChallenge = "/login" + }; + } + + + } +} + diff --git a/TryOwinSelfHost/DemoSecurekeyProvider.cs b/TryOwinSelfHost/DemoSecurekeyProvider.cs new file mode 100644 index 0000000..8ef3f29 --- /dev/null +++ b/TryOwinSelfHost/DemoSecurekeyProvider.cs @@ -0,0 +1,22 @@ +using System; +using Molaware.Nancy.Auth.JWT; + +namespace TryOwinSelfHost +{ + public class DemoSecurekeyProvider : ISecurekeyProvider + { + #region ISecurekeyProvider implementation + + public string GetSecurekey() + { + return "my secure key"; + } + + + + #endregion + + + } +} + diff --git a/TryOwinSelfHost/HelloModule.cs b/TryOwinSelfHost/HelloModule.cs new file mode 100644 index 0000000..e032a63 --- /dev/null +++ b/TryOwinSelfHost/HelloModule.cs @@ -0,0 +1,85 @@ +using System; +using Nancy; +using Nancy.ModelBinding; +using Nancy.Security; + +using Molaware.Nancy.Auth.JWT; + +namespace TryOwinSelfHost +{ + public class HelloModule : NancyModule + { + public HelloModule(ISecurekeyProvider keyProvider) + { + StaticConfiguration.DisableErrorTraces = false; + + Get["/"] = x => "Hello World from Owin SelfHosting"; + + Get["/api"] = x => + { + + var data = new[] { + new Data { Id = 1, Name = "one", Date = DateTime.Today }, + new Data { Id = 2, Name = "two", Date = DateTime.Now }, + }; + + return data; + }; + + Get["/safeapi"] = x => + { + this.RequiresClaims(new[] { "gender:woman" }); // more checks than default + + var data = new[] { + new Data { Id = 1, Name = "one", Date = DateTime.Today }, + new Data { Id = 2, Name = "two", Date = DateTime.Now }, + }; + + return data; + }; + + + Post["/login"] = x => + { + var info = this.Bind(); + + if (info.UserName == "cate") // ok + { + var jwt = new JwtToken { + UserName = info.UserName, + Expire = DateTime.UtcNow.AddDays(2), + Claims = new[] { "age:20", "gender:woman" } + }; + + return this.JwtToken(jwt, keyProvider); + } + else if (info.UserName == "bob") + { + var jwt = new JwtToken { + UserName = info.UserName, + Expire = DateTime.UtcNow.AddDays(2), + Claims = new[] { "age:20", "gender:man" } + }; + + return this.JwtToken(jwt, keyProvider); + } + + return HttpStatusCode.Unauthorized; + }; + } + } + + class LoginInfo + { + public string UserName { get; set; } + public string Password { get; set; } + } + + public class Data // need to be public when auto content-negotiation + { + public int Id { get; set; } + public string Name { get; set; } + public DateTime Date { get; set; } + } +} + diff --git a/TryOwinSelfHost/LoggingBeforeRequests.cs b/TryOwinSelfHost/LoggingBeforeRequests.cs new file mode 100644 index 0000000..4b87ad1 --- /dev/null +++ b/TryOwinSelfHost/LoggingBeforeRequests.cs @@ -0,0 +1,35 @@ +using System; +using Nancy; +using Nancy.Bootstrapper; + +namespace TryOwinSelfHost +{ + public class LoggingBeforeRequests : IRequestStartup + { + + + public LoggingBeforeRequests() + { + } + + #region IRequestStartup implementation + + public void Initialize(IPipelines pipelines, NancyContext context) + { + pipelines.BeforeRequest.AddItemToEndOfPipeline(ctx => { + + Console.WriteLine(ctx.Request.Method + " " + ctx.Request.Url.ToString()); // log to console + + return ctx.Response; + }); + + pipelines.OnError.AddItemToEndOfPipeline((ctx, ex) => + { + return ex; + }); + } + + #endregion + } +} + diff --git a/TryOwinSelfHost/Program.cs b/TryOwinSelfHost/Program.cs new file mode 100644 index 0000000..ce1f16f --- /dev/null +++ b/TryOwinSelfHost/Program.cs @@ -0,0 +1,23 @@ +using System; +using Microsoft.Owin.Hosting; + +using static System.Console; + +namespace TryOwinSelfHost +{ + class MainClass + { + public static void Main(string[] args) + { + string url = "http://localhost:8889"; + + using (WebApp.Start(url)) + { + Console.WriteLine($"Running on {url}"); + Console.WriteLine("Press enter to exit"); + + ReadLine(); + } + } + } +} diff --git a/TryOwinSelfHost/Properties/AssemblyInfo.cs b/TryOwinSelfHost/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4b7e146 --- /dev/null +++ b/TryOwinSelfHost/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("TryOwinSelfHost")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("libin")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/TryOwinSelfHost/Startup.cs b/TryOwinSelfHost/Startup.cs new file mode 100644 index 0000000..45dc2b5 --- /dev/null +++ b/TryOwinSelfHost/Startup.cs @@ -0,0 +1,20 @@ +using System; +using Owin; +using Molaware.Nancy.Auth.JWT; + +namespace TryOwinSelfHost +{ + public class Startup + { + public void Configuration(IAppBuilder app) + { + + + app.UseNancy(); + + } + + + } +} + diff --git a/TryOwinSelfHost/TryOwinSelfHost.csproj b/TryOwinSelfHost/TryOwinSelfHost.csproj new file mode 100644 index 0000000..b6a0c24 --- /dev/null +++ b/TryOwinSelfHost/TryOwinSelfHost.csproj @@ -0,0 +1,74 @@ + + + + Debug + x86 + {F7E68CBE-ADE2-44E4-BDBB-ED226016C813} + Exe + TryOwinSelfHost + TryOwinSelfHost + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + + + full + true + bin\Release + prompt + 4 + true + x86 + + + + + ..\packages\Owin.1.0\lib\net40\Owin.dll + + + ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll + + + ..\packages\Nancy.Owin.1.4.1\lib\net40\Nancy.Owin.dll + + + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll + + + ..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll + + + ..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll + + + + + + + + + + + + + + + + + + + + {78E10B42-9DF5-4534-82E2-02D9983FAB03} + Molaware.Nancy.Auth.JWT + + + \ No newline at end of file diff --git a/TryOwinSelfHost/app.config b/TryOwinSelfHost/app.config new file mode 100644 index 0000000..d2865fc --- /dev/null +++ b/TryOwinSelfHost/app.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/TryOwinSelfHost/packages.config b/TryOwinSelfHost/packages.config new file mode 100644 index 0000000..9ddbafe --- /dev/null +++ b/TryOwinSelfHost/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/TryOwinSelfHost/readme.md b/TryOwinSelfHost/readme.md new file mode 100644 index 0000000..81bd108 --- /dev/null +++ b/TryOwinSelfHost/readme.md @@ -0,0 +1,3 @@ + +works the same as Nancy selfhost. +can be running on Linux + Nginx + Supervisor, as as self hosting \ No newline at end of file