diff --git a/src/Nancy.Authentication.Token.Tests/Nancy.Authentication.Token.Tests.csproj b/src/Nancy.Authentication.Token.Tests/Nancy.Authentication.Token.Tests.csproj deleted file mode 100644 index 56fcef0b89..0000000000 --- a/src/Nancy.Authentication.Token.Tests/Nancy.Authentication.Token.Tests.csproj +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - Debug - AnyCPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20} - Library - Properties - Nancy.Authentication.Token.Tests - Nancy.Authentication.Token.Tests - v4.5 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - true - bin\MonoDebug\ - DEBUG;TRACE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - false - - - bin\MonoRelease\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - false - - - - False - ..\packages\FakeItEasy.1.19.0\lib\net40\FakeItEasy.dll - - - - - - - - - - False - ..\packages\xunit.1.9.1\lib\net20\xunit.dll - - - False - ..\packages\xunit.extensions.1.9.1\lib\net20\xunit.extensions.dll - - - - - Fakes\FakeRequest.cs - - - ShouldExtensions.cs - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - {97fa024a-f6ed-4086-bcc1-1a51be63474c} - Nancy.Authentication.Token - - - {D79203C0-B672-4751-9C95-C3AB7D3FEFBE} - Nancy.Testing - - - {34576216-0dca-4b0f-a0dc-9075e75a676f} - Nancy - - - - - - - - \ No newline at end of file diff --git a/src/Nancy.Authentication.Token.Tests/Storage/FileSystemTokenKeyStoreFixture.cs b/src/Nancy.Authentication.Token.Tests/Storage/FileSystemTokenKeyStoreFixture.cs deleted file mode 100644 index d047db4c09..0000000000 --- a/src/Nancy.Authentication.Token.Tests/Storage/FileSystemTokenKeyStoreFixture.cs +++ /dev/null @@ -1,72 +0,0 @@ -namespace Nancy.Authentication.Token.Tests.Storage -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Text; - - using Nancy.Authentication.Token.Storage; - using Nancy.Testing.Fakes; - - using Xunit; - - public class FileSystemTokenKeyStoreFixture - { - [Fact] - public void Should_store_keys_in_file() - { - // Given - var keyStore = GetKeyStore(); - try - { - var keys = new Dictionary - { - { DateTime.UtcNow, Encoding.UTF8.GetBytes("fake encryption key") } - }; - - // When - keyStore.Store(keys); - - // Then - Assert.True(File.Exists(keyStore.FilePath)); - } - finally - { - keyStore.Purge(); - } - } - - [Fact] - public void Should_retrieve_keys_from_file() - { - // Given - var keyStore = GetKeyStore(); - try - { - var keys = new Dictionary - { - { DateTime.UtcNow, Encoding.UTF8.GetBytes("fake encryption key") } - }; - - keyStore.Store(keys); - - // When - var retrievedKeys = keyStore.Retrieve(); - - // Then - Assert.True(Encoding.UTF8.GetString(retrievedKeys.Values.First()) == "fake encryption key"); - } - finally - { - keyStore.Purge(); - } - } - - private static FileSystemTokenKeyStore GetKeyStore() - { - var rootPathProvider = new FakeRootPathProvider(); - return new FileSystemTokenKeyStore(rootPathProvider); - } - } -} diff --git a/src/Nancy.Authentication.Token.Tests/TokenAuthenticationConfigurationFixture.cs b/src/Nancy.Authentication.Token.Tests/TokenAuthenticationConfigurationFixture.cs deleted file mode 100644 index 65999ee00a..0000000000 --- a/src/Nancy.Authentication.Token.Tests/TokenAuthenticationConfigurationFixture.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Nancy.Authentication.Token.Tests -{ - using System; - - using Nancy.Tests; - - using Xunit; - - public class TokenAuthenticationConfigurationFixture - { - [Fact] - public void Should_throw_with_null_tokenizer() - { - var result = Record.Exception(() => new TokenAuthenticationConfiguration(null)); - - result.ShouldBeOfType(typeof (ArgumentException)); - } - } -} diff --git a/src/Nancy.Authentication.Token.Tests/TokenAuthenticationFixture.cs b/src/Nancy.Authentication.Token.Tests/TokenAuthenticationFixture.cs deleted file mode 100644 index 42fbe4094d..0000000000 --- a/src/Nancy.Authentication.Token.Tests/TokenAuthenticationFixture.cs +++ /dev/null @@ -1,188 +0,0 @@ -namespace Nancy.Authentication.Token.Tests -{ - using System; - using System.Collections.Generic; - using System.Threading; - - using FakeItEasy; - - using Nancy.Bootstrapper; - using Nancy.Security; - using Nancy.Tests; - using Nancy.Tests.Fakes; - - using Xunit; - - public class TokenAuthenticationFixture - { - private readonly TokenAuthenticationConfiguration config; - private readonly IPipelines hooks; - - public TokenAuthenticationFixture() - { - this.config = new TokenAuthenticationConfiguration(A.Fake()); - this.hooks = new Pipelines(); - TokenAuthentication.Enable(this.hooks, this.config); - } - - [Fact] - public void Should_add_a_pre_hook_in_application_when_enabled() - { - // Given - var pipelines = A.Fake(); - - // When - TokenAuthentication.Enable(pipelines, this.config); - - // Then - A.CallTo(() => pipelines.BeforeRequest.AddItemToStartOfPipeline(A>.Ignored)) - .MustHaveHappened(Repeated.Exactly.Once); - } - - [Fact] - public void Should_add_both_token_and_requires_auth_pre_hook_in_module_when_enabled() - { - // Given - var module = new FakeModule(); - - // When - TokenAuthentication.Enable(module, this.config); - - // Then - module.Before.PipelineDelegates.ShouldHaveCount(2); - } - - [Fact] - public void Should_throw_with_null_config_passed_to_enable_with_application() - { - // Given, When - var result = Record.Exception(() => TokenAuthentication.Enable(A.Fake(), null)); - - // Then - result.ShouldBeOfType(typeof(ArgumentNullException)); - } - - [Fact] - public void Should_throw_with_null_config_passed_to_enable_with_module() - { - // Given, When - var result = Record.Exception(() => TokenAuthentication.Enable(new FakeModule(), null)); - - // Then - result.ShouldBeOfType(typeof(ArgumentNullException)); - } - - [Fact] - public void Should_throw_with_null_pipeline_passed_to_enable_with_config() - { - // Given, When - var result = Record.Exception(() => TokenAuthentication.Enable((IPipelines)null, null)); - - // Then - result.ShouldBeOfType(typeof(ArgumentNullException)); - } - - [Fact] - public void Should_throw_with_null_module_passed_to_enable_with_config() - { - // Given, When - var result = Record.Exception(() => TokenAuthentication.Enable((INancyModule)null, null)); - - // Then - result.ShouldBeOfType(typeof(ArgumentNullException)); - } - - [Fact] - public void Pre_request_hook_should_not_set_auth_details_with_no_auth_headers() - { - // Given - var context = new NancyContext() - { - Request = new FakeRequest("GET", "/") - }; - - // When - var result = this.hooks.BeforeRequest.Invoke(context, new CancellationToken()); - - // Then - result.Result.ShouldBeNull(); - context.CurrentUser.ShouldBeNull(); - } - - [Fact] - public void Pre_request_hook_should_not_set_auth_details_when_invalid_scheme_in_auth_header() - { - // Given - var context = CreateContextWithHeader( - "Authorization", new[] { "FooScheme" + " " + "A-FAKE-TOKEN" }); - - // When - var result = this.hooks.BeforeRequest.Invoke(context, new CancellationToken()); - - // Then - result.Result.ShouldBeNull(); - context.CurrentUser.ShouldBeNull(); - } - - [Fact] - public void Pre_request_hook_should_call_tokenizer_with_token_in_auth_header() - { - // Given - var context = CreateContextWithHeader( - "Authorization", new[] { "Token" + " " + "mytoken" }); - - // When - this.hooks.BeforeRequest.Invoke(context, new CancellationToken()); - - // Then - A.CallTo(() => config.Tokenizer.Detokenize("mytoken", context, A.Ignored)).MustHaveHappened(); - } - - [Fact] - public void Should_set_user_in_context_with_valid_username_in_auth_header() - { - // Given - var fakePipelines = new Pipelines(); - - var context = CreateContextWithHeader( - "Authorization", new[] { "Token" + " " + "mytoken" }); - - var tokenizer = A.Fake(); - var fakeUser = A.Fake(); - A.CallTo(() => tokenizer.Detokenize("mytoken", context, A.Ignored)).Returns(fakeUser); - - var cfg = new TokenAuthenticationConfiguration(tokenizer); - - TokenAuthentication.Enable(fakePipelines, cfg); - - // When - fakePipelines.BeforeRequest.Invoke(context, new CancellationToken()); - - // Then - context.CurrentUser.ShouldBeSameAs(fakeUser); - } - - private static NancyContext CreateContextWithHeader(string name, IEnumerable values) - { - var header = new Dictionary> - { - { name, values } - }; - - return new NancyContext() - { - Request = new FakeRequest("GET", "/", header) - }; - } - - class FakeModule : NancyModule - { - public FakeModule() - { - this.After = new AfterPipeline(); - this.Before = new BeforePipeline(); - this.OnError = new ErrorPipeline(); - } - } - } -} \ No newline at end of file diff --git a/src/Nancy.Authentication.Token.Tests/TokenizerFixture.cs b/src/Nancy.Authentication.Token.Tests/TokenizerFixture.cs deleted file mode 100644 index b708c0b8c0..0000000000 --- a/src/Nancy.Authentication.Token.Tests/TokenizerFixture.cs +++ /dev/null @@ -1,340 +0,0 @@ -namespace Nancy.Authentication.Token.Tests -{ - using System; - using System.Collections.Generic; - using System.Text; - using System.Threading; - - using FakeItEasy; - - using Nancy.Authentication.Token.Storage; - using Nancy.Security; - using Nancy.Tests; - using Nancy.Tests.Fakes; - - using Xunit; - - public class TokenizerFixture - { - private readonly NancyContext context; - private readonly FakeRequest request; - - public TokenizerFixture() - { - context = new NancyContext(); - request = new FakeRequest("GET", "/", - new Dictionary> - { - {"User-Agent", new[] {"a fake user agent"}} - }); - context.Request = request; - } - - [Fact] - public void Should_throw_argument_exception_if_token_expiration_exceeds_key_expiration() - { - var result = Record.Exception(() => - { - CreateTokenizer(cfg => cfg.TokenExpiration(() => TimeSpan.FromDays(8))); - }); - - result.ShouldBeOfType(); - } - - [Fact] - public void Should_throw_argument_exception_if_key_expiration_is_less_than_token_expiration() - { - var result = Record.Exception(() => - { - CreateTokenizer(cfg => cfg.KeyExpiration(() => TimeSpan.FromTicks(1))); - }); - - result.ShouldBeOfType(); - } - - [Fact] - public void Should_be_able_to_create_token_from_user_identity() - { - var tokenizer = CreateTokenizer(); - - var identity = new FakeUserIdentity - { - UserName = "joe", - Claims = new[] { "claim1", "claim2" } - }; - - var token = tokenizer.Tokenize(identity, context); - - token.ShouldNotBeNull(); - } - - [Fact] - public void Should_be_able_to_extract_user_identity_from_token() - { - var tokenizer = CreateTokenizer(); - - var identity = new FakeUserIdentity - { - UserName = "joe", - Claims = new[] { "claim1", "claim2" } - }; - - var token = tokenizer.Tokenize(identity, context); - - var detokenizedIdentity = tokenizer.Detokenize(token, this.context, new DefaultUserIdentityResolver()); - - detokenizedIdentity.ShouldNotBeNull(); - - detokenizedIdentity.UserName.ShouldEqual("joe"); - - detokenizedIdentity.Claims.ShouldEqualSequence(new[] { "claim1", "claim2" }); - } - - [Fact] - public void Should_not_be_able_to_extract_user_identity_from_modified_token() - { - var tokenizer = CreateTokenizer(); - - var identity = new FakeUserIdentity - { - UserName = "joe", - Claims = new[] { "claim1", "claim2" } - }; - - var token = tokenizer.Tokenize(identity, context); - var parts = token.Split(new[] { ":" }, StringSplitOptions.RemoveEmptyEntries); - var bytes = Convert.FromBase64String(parts[0]); - - var tweak = new List(bytes); - tweak.Add(Encoding.UTF8.GetBytes("X")[0]); - - var badToken = Convert.ToBase64String(tweak.ToArray()) + ":" + parts[1]; - - var detokenizedIdentity = tokenizer.Detokenize(badToken, this.context, new DefaultUserIdentityResolver()); - - detokenizedIdentity.ShouldBeNull(); - } - - [Fact] - public void Should_be_able_to_extract_user_identity_from_token_with_extra_items() - { - var tokenizer = CreateTokenizer(); - - var identity = new FakeUserIdentity - { - UserName = "joe", - Claims = new[] { "claim1", "claim2" } - }; - - var token = tokenizer.Tokenize(identity, context); - - var detokenizedIdentity = tokenizer.Detokenize(token, this.context, new DefaultUserIdentityResolver()); - - detokenizedIdentity.ShouldNotBeNull(); - - detokenizedIdentity.UserName.ShouldEqual("joe"); - - detokenizedIdentity.Claims.ShouldEqualSequence(new[] { "claim1", "claim2" }); - } - - [Fact] - public void Should_fail_to_detokenize_when_additional_items_do_not_match() - { - var tokenizer = CreateTokenizer(); - - var identity = new FakeUserIdentity - { - UserName = "joe", - Claims = new[] { "claim1", "claim2" } - }; - - var token = tokenizer.Tokenize(identity, context); - - var badRequest = new FakeRequest("GET", "/", - new Dictionary> - { - {"User-Agent", new[] {"uh oh! no matchey!"}} - }); - var badContext = new NancyContext - { - Request = badRequest - }; - - var detokenizedIdentity = tokenizer.Detokenize(token, badContext, new DefaultUserIdentityResolver()); - - detokenizedIdentity.ShouldBeNull(); - } - - [Fact] - public void Should_expire_token_when_expiration_has_lapsed() - { - var tokenizer = CreateTokenizer(cfg => cfg.TokenExpiration(() => TimeSpan.FromMilliseconds(10))); - - var identity = new FakeUserIdentity - { - UserName = "joe", - Claims = new[] { "claim1", "claim2" } - }; - - var token = tokenizer.Tokenize(identity, context); - - Thread.Sleep(20); - - var detokenizedIdentity = tokenizer.Detokenize(token, this.context, new DefaultUserIdentityResolver()); - - detokenizedIdentity.ShouldBeNull(); - } - - [Fact] - public void Should_not_expire_token_when_key_expiration_has_lapsed_but_token_expiration_has_not() - { - var tokenizer = CreateTokenizer(cfg => - { - cfg.TokenExpiration(() => TimeSpan.FromMilliseconds(50)); - cfg.KeyExpiration(() => TimeSpan.FromMilliseconds(100)); - }); - - var identity = new FakeUserIdentity - { - UserName = "joe", - Claims = new[] { "claim1", "claim2" } - }; - - tokenizer.Tokenize(identity, context); // prime the pump to generate a key - - Thread.Sleep(75); // key is 75% to its expiration - - var token = tokenizer.Tokenize(identity, context); - - Thread.Sleep(25); // key is now expired but should not be purged until token expiration lapses - - var detokenizedIdentity = tokenizer.Detokenize(token, this.context, new DefaultUserIdentityResolver()); - - detokenizedIdentity.ShouldNotBeNull(); - } - - [Fact] - public void Should_generate_new_token_after_previous_key_has_expired() - { - var tokenizer = CreateTokenizer(cfg => - { - cfg.TokenExpiration(() => TimeSpan.FromMilliseconds(50)); - cfg.KeyExpiration(() => TimeSpan.FromMilliseconds(100)); - cfg.TokenStamp(() => new DateTime(2014, 1, 1)); - }); - - var identity = new FakeUserIdentity - { - UserName = "joe", - Claims = new[] { "claim1", "claim2" } - }; - - var token = tokenizer.Tokenize(identity, context); // prime the pump to generate a key - - Thread.Sleep(120); // expire the key - - var secondToken = tokenizer.Tokenize(identity, context); - - token.ShouldNotEqual(secondToken); - } - - [Fact] - public void Should_expire_token_when_key_expiration_has_lapsed() - { - var tokenizer = CreateTokenizer(cfg => - { - cfg.TokenExpiration(() => TimeSpan.FromMilliseconds(10)); - cfg.KeyExpiration(() => TimeSpan.FromMilliseconds(20)); - }); - - var identity = new FakeUserIdentity - { - UserName = "joe", - Claims = new[] { "claim1", "claim2" } - }; - - var token = tokenizer.Tokenize(identity, context); - - Thread.Sleep(30); - - var detokenizedIdentity = tokenizer.Detokenize(token, this.context, new DefaultUserIdentityResolver()); - - detokenizedIdentity.ShouldBeNull(); - } - - [Fact] - public void Should_retrieve_keys_from_store_when_tokenizer_is_created() - { - var keyCache = A.Fake(); - - CreateTokenizer(cfg => cfg.WithKeyCache(keyCache)); - - A.CallTo(() => keyCache.Retrieve()).MustHaveHappened(Repeated.Exactly.Once); - } - - [Fact] - public void Should_store_keys_when_the_first_token_is_tokenized() - { - var keyCache = A.Fake(); - - var tokenizer = CreateTokenizer(cfg => cfg.WithKeyCache(keyCache)); - - var identity = new FakeUserIdentity - { - UserName = "joe", - Claims = new[] { "claim1", "claim2" } - }; - - tokenizer.Tokenize(identity, this.context); - - A.CallTo(() => keyCache.Store(A>.Ignored)).MustHaveHappened(Repeated.Exactly.Once); - } - - [Fact] - public void Should_store_keys_when_a_key_is_purged() - { - var keyCache = A.Fake(); - - var tokenizer = CreateTokenizer(cfg => - { - cfg.TokenExpiration(() => TimeSpan.FromMilliseconds(1)); - cfg.KeyExpiration(() => TimeSpan.FromMilliseconds(2)); - cfg.WithKeyCache(keyCache); - }); - - var identity = new FakeUserIdentity - { - UserName = "joe", - Claims = new[] { "claim1", "claim2" } - }; - - tokenizer.Tokenize(identity, context); - - Thread.Sleep(5); - - tokenizer.Tokenize(identity, context); - - A.CallTo(() => keyCache.Store(A>.Ignored)).MustHaveHappened(Repeated.AtLeast.Once); - } - - private Tokenizer CreateTokenizer(Action configuration = null) - { - var tokenizer = new Tokenizer(cfg => - { - cfg.WithKeyCache(new InMemoryTokenKeyStore()); - - if (configuration != null) - { - configuration(cfg); - } - }); - return tokenizer; - } - - public class FakeUserIdentity : IUserIdentity - { - public string UserName { get; set; } - public IEnumerable Claims { get; set; } - } - } -} \ No newline at end of file diff --git a/src/Nancy.Authentication.Token.Tests/app.config b/src/Nancy.Authentication.Token.Tests/app.config deleted file mode 100644 index 6fe47bb290..0000000000 --- a/src/Nancy.Authentication.Token.Tests/app.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/Nancy.Authentication.Token.Tests/packages.config b/src/Nancy.Authentication.Token.Tests/packages.config deleted file mode 100644 index 18f13949c6..0000000000 --- a/src/Nancy.Authentication.Token.Tests/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/Nancy.Authentication.Token/DefaultUserIdentityResolver.cs b/src/Nancy.Authentication.Token/DefaultUserIdentityResolver.cs deleted file mode 100644 index 28930d7207..0000000000 --- a/src/Nancy.Authentication.Token/DefaultUserIdentityResolver.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Nancy.Authentication.Token -{ - using System.Collections.Generic; - - using Nancy.Security; - - /// - /// The default user identity resolver. - /// This creates a plain user identity based on username and claims. - /// - public class DefaultUserIdentityResolver : IUserIdentityResolver - { - /// - /// Gets the from username and claims. - /// - /// The username. - /// The claims. - /// Current . - /// A populated , or null - public IUserIdentity GetUser(string userName, IEnumerable claims, NancyContext context) - { - return new TokenUserIdentity(userName, claims); - } - - private class TokenUserIdentity : IUserIdentity - { - public TokenUserIdentity(string userName, IEnumerable claims) - { - this.UserName = userName; - this.Claims = claims; - } - - public string UserName { get; private set; } - - public IEnumerable Claims { get; private set; } - } - } -} \ No newline at end of file diff --git a/src/Nancy.Authentication.Token/ITokenizer.cs b/src/Nancy.Authentication.Token/ITokenizer.cs deleted file mode 100644 index 2d6655d940..0000000000 --- a/src/Nancy.Authentication.Token/ITokenizer.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Nancy.Authentication.Token -{ - using Nancy.Security; - - /// - /// Encodes and decodes authorization tokens. - /// - public interface ITokenizer - { - /// - /// Create a token from a - /// - /// The user identity from which to create a token. - /// Current . - /// The generated token. - string Tokenize(IUserIdentity userIdentity, NancyContext context); - - /// - /// Create a from a token - /// - /// The token from which to create a user identity. - /// Current . - /// The user identity resolver. - /// The detokenized user identity. - IUserIdentity Detokenize(string token, NancyContext context, IUserIdentityResolver userIdentityResolver); - } -} \ No newline at end of file diff --git a/src/Nancy.Authentication.Token/IUserIdentityResolver.cs b/src/Nancy.Authentication.Token/IUserIdentityResolver.cs deleted file mode 100644 index 1067922477..0000000000 --- a/src/Nancy.Authentication.Token/IUserIdentityResolver.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Nancy.Authentication.Token -{ - using System.Collections.Generic; - - using Nancy.Security; - - /// - /// Provides a mapping between username and an . - /// - public interface IUserIdentityResolver - { - /// - /// Gets the from username and claims. - /// - /// The username. - /// The claims. - /// Current . - /// A populated , or null - IUserIdentity GetUser(string userName, IEnumerable claims, NancyContext context); - } -} \ No newline at end of file diff --git a/src/Nancy.Authentication.Token/Nancy.Authentication.Token.csproj b/src/Nancy.Authentication.Token/Nancy.Authentication.Token.csproj deleted file mode 100644 index bf6935e3b8..0000000000 --- a/src/Nancy.Authentication.Token/Nancy.Authentication.Token.csproj +++ /dev/null @@ -1,104 +0,0 @@ - - - - - Debug - AnyCPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C} - Library - Properties - Nancy.Authentication.Token - Nancy.Authentication.Token - v4.5 - 512 - ..\ - true - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - bin\Debug\Nancy.Authentication.Token.XML - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\Nancy.Authentication.Token.XML - false - - - true - bin\MonoDebug\ - DEBUG;TRACE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - bin\MonoDebug\Nancy.Authentication.Token.XML - false - - - bin\MonoRelease\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - bin\MonoRelease\Nancy.Authentication.Token.XML - false - - - - - - - - - - - - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - {34576216-0dca-4b0f-a0dc-9075e75a676f} - Nancy - - - - - - - - - \ No newline at end of file diff --git a/src/Nancy.Authentication.Token/Properties/Internals.cs b/src/Nancy.Authentication.Token/Properties/Internals.cs deleted file mode 100644 index 312935b597..0000000000 --- a/src/Nancy.Authentication.Token/Properties/Internals.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Nancy.Authentication.Token.Tests")] \ No newline at end of file diff --git a/src/Nancy.Authentication.Token/README.md b/src/Nancy.Authentication.Token/README.md deleted file mode 100644 index 09b5a1c16a..0000000000 --- a/src/Nancy.Authentication.Token/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# Meet Nancy Token Authentication - -The Nancy.Authentication.Token project was built for use by heterogeneous clients (iOS apps, Android apps, Angular SPA apps, etc.) that all communicate with the same back-end Nancy application. - -## Rationale - -Token authentication and authorization was built with the following requirements: - -* No Cookies (since not all client apps are web browsers) -* Avoid retrieving users and permissions from a backend data store once the user has been authenticated/authorized -* Allow client apps to store a token containing the current user's credentials for resubmission on subsequent requests (after first authenticating) -* Prevent rogue clients from simply generating their own spoofed credentials by incorporating a one-way hashing algorithm that ensures the token has not been tampered with -* Use server side keys for token hash generation with a configurable key expiration interval -* Use file system storage of server-side token generation private keys to allow keys to survive an application restart or an app pool recycle. Note: an "in memory" option is available primarily for testing, but could be used in a situation where expiring all user sessions on an application restart is acceptable behavior. - -Token Authentication can be wired up in a simliar fashion to other available forms of Nancy authentication. - -```csharp -public class Bootstrapper : DefaultNancyBootstrapper -{ - protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context) - { - TokenAuthentication.Enable(pipelines, new TokenAuthenticationConfiguration(container.Resolve())); - } -} -``` - -You will need to provide your own form of initial user authentication. This can use your own custom implementation that queries -from a database, from an AD store, from a webservice, or any other form you choose. It could also use another form of Nancy authentication (Basic with an IUserValidator implementation -for example). - -Tokens are generated from an `IUserIdentity` and a `NancyContext` by an implementation of `ITokenizer`. The -default implementation is named `Tokenizer` and provides some configuration options. By default, it generates a token -that includes the following components: - -* User name -* Pipe separated list of user claims -* UTC now in ticks -* The client's "User-Agent" http header value (required) - -It is recommended that you configure the Tokenizer to use an additional piece of information that can uniquely identify -the client device. - -The following code shows an example of how you can perform the initial user authorization and return the generated token to the client. - -```csharp -public class AuthModule : NancyModule -{ - public AuthModule(ITokenizer tokenizer) - : base("/auth") - { - Post["/"] = x => - { - var userName = (string)this.Request.Form.UserName; - var password = (string)this.Request.Form.Password; - - var userIdentity = UserDatabase.ValidateUser(userName, password); - - if (userIdentity == null) - { - return HttpStatusCode.Unauthorized; - } - - var token = tokenizer.Tokenize(userIdentity, Context); - - return new - { - Token = token, - }; - }; - - Get["/validation"] = _ => - { - this.RequiresAuthentication(); - return "Yay! You are authenticated!"; - }; - - Get["/admin"] = _ => - { - this.RequiresAuthentication(); - this.RequiresClaims(new[] { "admin" }); - return "Yay! You are authorized!"; - }; - } -} -``` - -## Contributors - -Nancy.Authentication.Token was originally created by the crack development team at [Lotpath](http://lotpath.com) ([Lotpath on github](http://github.com/Lotpath)). diff --git a/src/Nancy.Authentication.Token/Storage/FileSystemTokenKeyStore.cs b/src/Nancy.Authentication.Token/Storage/FileSystemTokenKeyStore.cs deleted file mode 100644 index ce40ae3ed1..0000000000 --- a/src/Nancy.Authentication.Token/Storage/FileSystemTokenKeyStore.cs +++ /dev/null @@ -1,113 +0,0 @@ -namespace Nancy.Authentication.Token.Storage -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Runtime.Serialization.Formatters.Binary; - - /// - /// Stores encryption keys in the file system - /// - public class FileSystemTokenKeyStore : ITokenKeyStore - { - private IRootPathProvider rootPathProvider; - - private BinaryFormatter binaryFormatter; - - private static object syncLock = new object(); - - /// - /// Creates a new - /// - public FileSystemTokenKeyStore() - : this(new DefaultRootPathProvider()) - { - } - - /// - /// Creates a new - /// - /// - public FileSystemTokenKeyStore(IRootPathProvider rootPathProvider) - { - this.rootPathProvider = rootPathProvider; - this.binaryFormatter = new BinaryFormatter(); - } - - /// - /// Retrieves encryption keys. - /// - /// Keys - public IDictionary Retrieve() - { - lock (syncLock) - { - if (!File.Exists(FilePath)) - { - return new Dictionary(); - } - - using (var fileStream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.None)) - { - return (Dictionary)binaryFormatter.Deserialize(fileStream); - } - } - } - - /// - /// Stores encyrption keys. - /// - /// Keys - public void Store(IDictionary keys) - { - lock (syncLock) - { - if (!Directory.Exists(StorageLocation)) - { - Directory.CreateDirectory(StorageLocation); - } - - var keyChain = new Dictionary(keys); - - using (var fileStream = new FileStream(FilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)) - { - binaryFormatter.Serialize(fileStream, keyChain); - } - } - } - - /// - /// Purges encryption keys - /// - public void Purge() - { - if (File.Exists(FilePath)) - { - File.Delete(FilePath); - } - if (Directory.Exists(StorageLocation)) - { - Directory.Delete(StorageLocation); - } - } - - /// - /// The location where token keys are stored - /// - public string FilePath - { - get - { - return Path.Combine(StorageLocation, "keyChain.bin"); - } - } - - private string StorageLocation - { - get - { - return Path.Combine(rootPathProvider.GetRootPath(), "keyStore"); - } - } - } -} \ No newline at end of file diff --git a/src/Nancy.Authentication.Token/Storage/ITokenKeyStore.cs b/src/Nancy.Authentication.Token/Storage/ITokenKeyStore.cs deleted file mode 100644 index 1cb92e4f2c..0000000000 --- a/src/Nancy.Authentication.Token/Storage/ITokenKeyStore.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Nancy.Authentication.Token.Storage -{ - using System; - using System.Collections.Generic; - - /// - /// Stores and retrieves encryption keys - /// - public interface ITokenKeyStore - { - /// - /// Retrieves encryption keys - /// - /// Keys - IDictionary Retrieve(); - - /// - /// Stores encryption keys - /// - /// Keys - void Store(IDictionary keys); - - /// - /// Purges encryption keys - /// - void Purge(); - } -} \ No newline at end of file diff --git a/src/Nancy.Authentication.Token/Storage/InMemoryTokenKeyStore.cs b/src/Nancy.Authentication.Token/Storage/InMemoryTokenKeyStore.cs deleted file mode 100644 index 170eaa0226..0000000000 --- a/src/Nancy.Authentication.Token/Storage/InMemoryTokenKeyStore.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Nancy.Authentication.Token.Storage -{ - using System; - using System.Collections.Generic; - - /// - /// In in memory implementation of . Useful for testing or scenarios - /// where encryption keys do not need to persist across application restarts (due to updates, app pool - /// expiration, etc.) - /// - public class InMemoryTokenKeyStore : ITokenKeyStore - { - private IDictionary keys; - - /// - /// Retrieves encryption keys - /// - /// Keys - public IDictionary Retrieve() - { - return new Dictionary(this.keys ?? new Dictionary()); - } - - /// - /// Stores encryption keys - /// - /// Keys - public void Store(IDictionary keys) - { - this.keys = new Dictionary(keys); - } - - /// - /// Purges encryption keys - /// - public void Purge() - { - this.keys = new Dictionary(); - } - } -} \ No newline at end of file diff --git a/src/Nancy.Authentication.Token/TokenAuthentication.cs b/src/Nancy.Authentication.Token/TokenAuthentication.cs deleted file mode 100644 index 040c27e008..0000000000 --- a/src/Nancy.Authentication.Token/TokenAuthentication.cs +++ /dev/null @@ -1,116 +0,0 @@ -namespace Nancy.Authentication.Token -{ - using System; - - using Nancy.Bootstrapper; - using Nancy.Security; - - /// - /// Nancy Token authentication implementation - /// - public static class TokenAuthentication - { - private const string Scheme = "Token"; - - /// - /// Enables Token authentication for the application - /// - /// Pipelines to add handlers to (usually "this") - /// Forms authentication configuration - public static void Enable(IPipelines pipelines, TokenAuthenticationConfiguration configuration) - { - if (pipelines == null) - { - throw new ArgumentNullException("pipelines"); - } - - if (configuration == null) - { - throw new ArgumentNullException("configuration"); - } - - pipelines.BeforeRequest.AddItemToStartOfPipeline(GetCredentialRetrievalHook(configuration)); - } - - /// - /// Enables Token authentication for a module - /// - /// Module to add handlers to (usually "this") - /// Forms authentication configuration - public static void Enable(INancyModule module, TokenAuthenticationConfiguration configuration) - { - if (module == null) - { - throw new ArgumentNullException("module"); - } - - if (configuration == null) - { - throw new ArgumentNullException("configuration"); - } - - module.RequiresAuthentication(); - module.Before.AddItemToStartOfPipeline(GetCredentialRetrievalHook(configuration)); - } - - /// - /// Gets the pre request hook for loading the authenticated user's details - /// from the auth header. - /// - /// Token authentication configuration to use - /// Pre request hook delegate - private static Func GetCredentialRetrievalHook(TokenAuthenticationConfiguration configuration) - { - if (configuration == null) - { - throw new ArgumentNullException("configuration"); - } - - return context => - { - RetrieveCredentials(context, configuration); - return null; - }; - } - - private static void RetrieveCredentials(NancyContext context, TokenAuthenticationConfiguration configuration) - { - var token = ExtractTokenFromHeader(context.Request); - if (token == null) - { - return; - } - - var user = configuration.Tokenizer.Detokenize(token, context, configuration.UserIdentityResolver); - if (user != null) - { - context.CurrentUser = user; - } - } - - private static string ExtractTokenFromHeader(Request request) - { - var authorization = request.Headers.Authorization; - - if (string.IsNullOrEmpty(authorization)) - { - return null; - } - - if (!authorization.StartsWith(Scheme)) - { - return null; - } - - try - { - var encodedToken = authorization.Substring(Scheme.Length).Trim(); - return String.IsNullOrWhiteSpace(encodedToken) ? null : encodedToken; - } - catch (FormatException) - { - return null; - } - } - } -} \ No newline at end of file diff --git a/src/Nancy.Authentication.Token/TokenAuthenticationConfiguration.cs b/src/Nancy.Authentication.Token/TokenAuthenticationConfiguration.cs deleted file mode 100644 index 8ddc4f107b..0000000000 --- a/src/Nancy.Authentication.Token/TokenAuthenticationConfiguration.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Nancy.Authentication.Token -{ - using System; - - /// - /// Configuration options for token authentication - /// - public class TokenAuthenticationConfiguration - { - /// - /// Initializes a new instance of the class. - /// - /// A valid instance of class - /// The user identity resolver. - public TokenAuthenticationConfiguration(ITokenizer tokenizer, IUserIdentityResolver userIdentityResolver = null) - { - if (tokenizer == null) - { - throw new ArgumentNullException("tokenizer"); - } - - this.Tokenizer = tokenizer; - this.UserIdentityResolver = userIdentityResolver ?? new DefaultUserIdentityResolver(); - } - - /// - /// Gets the token validator - /// - public ITokenizer Tokenizer { get; private set; } - - /// - /// Gets or sets the user identity resolver - /// - public IUserIdentityResolver UserIdentityResolver { get; set; } - } -} \ No newline at end of file diff --git a/src/Nancy.Authentication.Token/Tokenizer.cs b/src/Nancy.Authentication.Token/Tokenizer.cs deleted file mode 100644 index 062ab003c2..0000000000 --- a/src/Nancy.Authentication.Token/Tokenizer.cs +++ /dev/null @@ -1,413 +0,0 @@ -namespace Nancy.Authentication.Token -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using System.Security.Cryptography; - using System.Text; - - using Nancy.Authentication.Token.Storage; - using Nancy.ErrorHandling; - using Nancy.Security; - - /// - /// Default implementation of - /// - public class Tokenizer : ITokenizer - { - private readonly TokenValidator validator; - private ITokenKeyStore keyStore = new FileSystemTokenKeyStore(); - private Encoding encoding = Encoding.UTF8; - private string claimsDelimiter = "|"; - private string hashDelimiter = ":"; - private string itemDelimiter = Environment.NewLine; - private Func tokenStamp = () => DateTime.UtcNow; - private Func now = () => DateTime.UtcNow; - private Func tokenExpiration = () => TimeSpan.FromDays(1); - private Func keyExpiration = () => TimeSpan.FromDays(7); - - private Func[] additionalItems = - { - ctx => ctx.Request.Headers.UserAgent - }; - - /// - /// Initializes a new instance of the class. - /// - public Tokenizer() - : this(null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The configuration that should be used by the tokenizer. - public Tokenizer(Action configuration) - { - if (configuration != null) - { - var configurator = new TokenizerConfigurator(this); - configuration.Invoke(configurator); - } - var keyRing = new TokenKeyRing(this); - this.validator = new TokenValidator(keyRing); - } - - /// - /// Creates a token from a . - /// - /// The user identity from which to create a token. - /// Current . - /// The generated token. - public string Tokenize(IUserIdentity userIdentity, NancyContext context) - { - var items = new List - { - userIdentity.UserName, - string.Join(this.claimsDelimiter, userIdentity.Claims), - this.tokenStamp().Ticks.ToString(CultureInfo.InvariantCulture) - }; - - if (this.additionalItems != null) - { - foreach (var item in this.additionalItems.Select(additionalItem => additionalItem(context))) - { - if (string.IsNullOrWhiteSpace(item)) - { - throw new RouteExecutionEarlyExitException(new Response { StatusCode = HttpStatusCode.Unauthorized }); - } - items.Add(item); - } - } - - var message = string.Join(this.itemDelimiter, items); - var token = CreateToken(message); - return token; - } - - /// - /// Creates a from a token. - /// - /// The token from which to create a user identity. - /// Current . - /// The user identity resolver. - /// The detokenized user identity. - public IUserIdentity Detokenize(string token, NancyContext context, IUserIdentityResolver userIdentityResolver) - { - var tokenComponents = token.Split(new[] { this.hashDelimiter }, StringSplitOptions.None); - if (tokenComponents.Length != 2) - { - return null; - } - - var messagebytes = Convert.FromBase64String(tokenComponents[0]); - var hash = Convert.FromBase64String(tokenComponents[1]); - - if (!this.validator.IsValid(messagebytes, hash)) - { - return null; - } - - var items = this.encoding.GetString(messagebytes).Split(new[] { this.itemDelimiter }, StringSplitOptions.None); - - if (this.additionalItems != null) - { - var additionalItemCount = additionalItems.Count(); - for (var i = 0; i < additionalItemCount; i++) - { - var tokenizedValue = items[i + 3]; - var currentValue = additionalItems.ElementAt(i)(context); - if (tokenizedValue != currentValue) - { - // todo: may need to log here as this probably indicates hacking - return null; - } - } - } - - var generatedOn = new DateTime(long.Parse(items[2])); - - if (tokenStamp() - generatedOn > tokenExpiration()) - { - return null; - } - - var userName = items[0]; - var claims = items[1].Split(new[] { this.claimsDelimiter }, StringSplitOptions.None); - - return userIdentityResolver.GetUser(userName, claims, context); - } - - private string CreateToken(string message) - { - var messagebytes = this.encoding.GetBytes(message); - var hash = this.validator.CreateHash(messagebytes); - return Convert.ToBase64String(messagebytes) + this.hashDelimiter + Convert.ToBase64String(hash); - } - - /// - /// Provides an API for configuring a instance. - /// - public class TokenizerConfigurator - { - private readonly Tokenizer tokenizer; - - /// - /// Initializes a new instance of the class. - /// - /// - public TokenizerConfigurator(Tokenizer tokenizer) - { - this.tokenizer = tokenizer; - } - - /// - /// Sets the token key store used by the tokenizer - /// - /// - /// A reference to the current - public TokenizerConfigurator WithKeyCache(ITokenKeyStore store) - { - this.tokenizer.keyStore = store; - return this; - } - - /// - /// Sets the encoding used by the tokenizer - /// - /// - /// A reference to the current - public TokenizerConfigurator Encoding(Encoding encoding) - { - this.tokenizer.encoding = encoding; - return this; - } - - /// - /// Sets the delimiter between document and its hash - /// - /// - /// A reference to the current - public TokenizerConfigurator HashDelimiter(string hashDelimiter) - { - this.tokenizer.hashDelimiter = hashDelimiter; - return this; - } - - /// - /// Sets the delimiter between each item to be tokenized - /// - /// - /// A reference to the current - public TokenizerConfigurator ItemDelimiter(string itemDelimiter) - { - this.tokenizer.itemDelimiter = itemDelimiter; - return this; - } - - /// - /// Sets the delimiter between each claim - /// - /// - /// A reference to the current - public TokenizerConfigurator ClaimsDelimiter(string claimsDelimiter) - { - this.tokenizer.claimsDelimiter = claimsDelimiter; - return this; - } - - /// - /// Sets the token expiration interval. An expired token will cause a user to become unauthorized (logged out). - /// Suggested value is 1 day (which is also the default). - /// - /// - /// A reference to the current - public TokenizerConfigurator TokenExpiration(Func expiration) - { - this.tokenizer.tokenExpiration = expiration; - - if (this.tokenizer.tokenExpiration() >= this.tokenizer.keyExpiration()) - { - throw new ArgumentException("Token expiration must be less than key expiration", "expiration"); - } - - return this; - } - - /// - /// Sets the key expiration interval. Must be longer than the value. - /// When keys expire, they are scheduled to purge once any tokens they have been used to generate have expired. - /// Suggested range is 2 to 14 days. The default is 7 days. - /// - /// - /// A reference to the current - public TokenizerConfigurator KeyExpiration(Func expiration) - { - this.tokenizer.keyExpiration = expiration; - - if (this.tokenizer.tokenExpiration() >= this.tokenizer.keyExpiration()) - { - throw new ArgumentException("Key expiration must be greater than token expiration", "expiration"); - } - - return this; - } - - /// - /// Sets the token-generated-at timestamp - /// - /// - /// A reference to the current - public TokenizerConfigurator TokenStamp(Func tokenStamp) - { - this.tokenizer.tokenStamp = tokenStamp; - return this; - } - - /// - /// Sets the current date/time. - /// - /// - /// A reference to the current - public TokenizerConfigurator Now(Func now) - { - this.tokenizer.now = now; - return this; - } - - /// - /// Sets any additional items to be included when tokenizing. The default includes Request.Headers.UserAgent. - /// - /// - /// A reference to the current - public TokenizerConfigurator AdditionalItems(params Func[] additionalItems) - { - this.tokenizer.additionalItems = additionalItems; - return this; - } - } - - private class TokenValidator - { - private readonly TokenKeyRing keyRing; - - internal TokenValidator(TokenKeyRing keyRing) - { - this.keyRing = keyRing; - } - - public bool IsValid(byte[] message, byte[] hash) - { - return this.keyRing - .AllKeys() - .Select(key => GenerateHash(key, message)) - .Any(hash.SequenceEqual); - } - - public byte[] CreateHash(byte[] message) - { - var key = this.keyRing - .NonExpiredKeys() - .First(); - - return GenerateHash(key, message); - } - - private byte[] GenerateHash(byte[] key, byte[] message) - { - using (var hmac = new HMACSHA256(key)) - { - return hmac.ComputeHash(message); - } - } - } - - private class TokenKeyRing - { - private readonly Tokenizer tokenizer; - - private readonly IDictionary keys; - - internal TokenKeyRing(Tokenizer tokenizer) - { - this.tokenizer = tokenizer; - keys = this.tokenizer.keyStore.Retrieve(); - } - - public IEnumerable AllKeys() - { - return this.Keys(true); - } - - public IEnumerable NonExpiredKeys() - { - return this.Keys(false); - } - - private IEnumerable Keys(bool includeExpired) - { - var entriesToPurge = new List(); - var validKeys = new List(); - - foreach (var entry in this.keys.OrderByDescending(x => x.Key)) - { - if (IsReadyToPurge(entry)) - { - entriesToPurge.Add(entry.Key); - } - else if (!IsExpired(entry) || includeExpired) - { - validKeys.Add(entry.Value); - } - } - - var shouldStore = false; - - foreach (var entry in entriesToPurge) - { - this.keys.Remove(entry); - shouldStore = true; - } - - if (validKeys.Count == 0) - { - var key = CreateKey(); - this.keys[this.tokenizer.now()] = key; - validKeys.Add(key); - shouldStore = true; - } - - if (shouldStore) - { - this.tokenizer.keyStore.Store(keys); - } - - return validKeys; - } - - private bool IsReadyToPurge(KeyValuePair entry) - { - return this.tokenizer.now() - entry.Key > (this.tokenizer.keyExpiration() + this.tokenizer.tokenExpiration()); - } - - private bool IsExpired(KeyValuePair entry) - { - return this.tokenizer.now() - entry.Key > this.tokenizer.keyExpiration(); - } - - private byte[] CreateKey() - { - var secretKey = new byte[64]; - - using (var rng = new RNGCryptoServiceProvider()) - { - rng.GetBytes(secretKey); - } - - return secretKey; - } - } - } -} diff --git a/src/Nancy.Authentication.Token/nancy.authentication.token.nuspec b/src/Nancy.Authentication.Token/nancy.authentication.token.nuspec deleted file mode 100644 index 8a1fb8c3d4..0000000000 --- a/src/Nancy.Authentication.Token/nancy.authentication.token.nuspec +++ /dev/null @@ -1,26 +0,0 @@ - - - - Nancy.Authentication.Token - 0.0.0 - Andreas Håkansson, Steven Robbins and contributors - false - A token based authentication provider for Nancy. - Nancy is a lightweight web framework for the .Net platform, inspired by Sinatra. Nancy aim at delivering a low ceremony approach to building light, fast web applications. - en-US - Andreas Håkansson, Steven Robbins and contributors - http://nancyfx.org/nancy-nuget.png - https://github.com/NancyFx/Nancy/blob/master/license.txt - http://nancyfx.org - - - - Nancy Token Authentication - - - - - - - - \ No newline at end of file diff --git a/src/Nancy.Demo.Authentication.Token.TestingDemo/LoginFixture.cs b/src/Nancy.Demo.Authentication.Token.TestingDemo/LoginFixture.cs deleted file mode 100644 index d10580115b..0000000000 --- a/src/Nancy.Demo.Authentication.Token.TestingDemo/LoginFixture.cs +++ /dev/null @@ -1,113 +0,0 @@ -namespace Nancy.Demo.Authentication.Token.TestingDemo -{ - using Nancy.Testing; - using Nancy.Tests; - - using Xunit; - - public class LoginFixture - { - private readonly Browser browser; - - public LoginFixture() - { - var bootstrapper = new TestBootstrapper(); - this.browser = new Browser(bootstrapper); - } - - [Fact] - public void Should_return_generated_token_for_valid_user_credentials() - { - // Given, When - var response = this.browser.Post("/auth/", (with) => - { - with.HttpRequest(); - with.Accept("application/json"); - with.Header("User-Agent", "Nancy Browser"); - with.FormValue("UserName", "demo"); - with.FormValue("Password", "demo"); - }); - - // Then - response.Body.DeserializeJson().ShouldNotBeNull(); - } - - [Fact] - public void Should_return_unauthorized_for_invalid_user_credentials() - { - // Given, When - var response = this.browser.Post("/auth/", (with) => - { - with.HttpRequest(); - with.Accept("application/json"); - with.Header("User-Agent", "Nancy Browser"); - with.FormValue("UserName", "bad"); - with.FormValue("Password", "boy"); - }); - - // Then - response.StatusCode.ShouldEqual(HttpStatusCode.Unauthorized); - } - - [Fact] - public void Should_return_unauthorized_when_not_authenticated() - { - // Given, When - var response = this.browser.Get("/auth/validation/", (with) => - { - with.HttpRequest(); - }); - - // Then - response.StatusCode.ShouldEqual(HttpStatusCode.Unauthorized); - } - - [Fact] - public void Should_return_forbidden_when_not_authorized() - { - // Given, When - var response = this.browser.Post("/auth/", (with) => - { - with.HttpRequest(); - with.Accept("application/json"); - with.Header("User-Agent", "Nancy Browser"); - with.FormValue("UserName", "nonadmin"); - with.FormValue("Password", "nonadmin"); - }); - - var token = response.Body.DeserializeJson().Token; - - var secondResponse = response.Then.Get("/auth/admin/", with => - { - with.HttpRequest(); - with.Header("User-Agent", "Nancy Browser"); - with.Header("Authorization", "Token " + token); - }); - - // Then - secondResponse.StatusCode.ShouldEqual(HttpStatusCode.Forbidden); - } - - [Fact] - public void Should_return_unauthorized_without_user_agent() - { - // Given, When - var response = this.browser.Post("/auth/", (with) => - { - with.HttpRequest(); - with.Accept("application/json"); - with.FormValue("UserName", "demo"); - with.FormValue("Password", "demo"); - with.Header("User-Agent", null); - }); - - // Then - response.StatusCode.ShouldEqual(HttpStatusCode.Unauthorized); - } - - public class AuthResponse - { - public string Token { get; set; } - } - } -} \ No newline at end of file diff --git a/src/Nancy.Demo.Authentication.Token.TestingDemo/Nancy.Demo.Authentication.Token.TestingDemo.csproj b/src/Nancy.Demo.Authentication.Token.TestingDemo/Nancy.Demo.Authentication.Token.TestingDemo.csproj deleted file mode 100644 index dd44e0e1ea..0000000000 --- a/src/Nancy.Demo.Authentication.Token.TestingDemo/Nancy.Demo.Authentication.Token.TestingDemo.csproj +++ /dev/null @@ -1,118 +0,0 @@ - - - - - Debug - AnyCPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9} - Library - Properties - Nancy.Demo.Authentication.Token.TestingDemo - Nancy.Demo.Authentication.Token.TestingDemo - v4.5 - 512 - - - - true - full - false - bin\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - bin\ - TRACE - prompt - 4 - false - - - true - bin\ - DEBUG;TRACE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - false - - - bin\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - false - - - - - - - - - - - False - ..\packages\xunit.1.9.1\lib\net20\xunit.dll - - - False - ..\packages\xunit.extensions.1.9.1\lib\net20\xunit.extensions.dll - - - - - ShouldExtensions.cs - - - Properties\SharedAssemblyInfo.cs - - - - - - - {97FA024A-F6ED-4086-BCC1-1A51BE63474C} - Nancy.Authentication.Token - - - {35460aa4-b94a-4b64-9418-7243ec3d2f01} - Nancy.Demo.Authentication.Token - - - {d79203c0-b672-4751-9c95-c3ab7d3fefbe} - Nancy.Testing - - - {2c6f51df-015c-4db6-b44c-0e5e4f25e2a9} - Nancy.ViewEngines.Razor - - - {34576216-0dca-4b0f-a0dc-9075e75a676f} - Nancy - - - - - - - - - - - \ No newline at end of file diff --git a/src/Nancy.Demo.Authentication.Token.TestingDemo/TestBootstrapper.cs b/src/Nancy.Demo.Authentication.Token.TestingDemo/TestBootstrapper.cs deleted file mode 100644 index ace6cb1cc6..0000000000 --- a/src/Nancy.Demo.Authentication.Token.TestingDemo/TestBootstrapper.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Nancy.Demo.Authentication.Token.TestingDemo -{ - using System; - using System.IO; - - using Nancy.Authentication.Token; - using Nancy.Authentication.Token.Storage; - using Nancy.Testing; - using Nancy.Testing.Fakes; - using Nancy.TinyIoc; - - public class TestBootstrapper : TokenAuthBootstrapper - { - protected override void ConfigureApplicationContainer(TinyIoCContainer container) - { - container.Register(new Tokenizer(cfg => cfg.WithKeyCache(new InMemoryTokenKeyStore()))); - } - - protected override IRootPathProvider RootPathProvider - { - get - { - var assemblyFilePath = - new Uri(typeof(TokenAuthBootstrapper).Assembly.CodeBase).LocalPath; - - var assemblyPath = - Path.GetDirectoryName(assemblyFilePath); - - var rootPath = - PathHelper.GetParent(assemblyPath, 2); - - rootPath = - Path.Combine(rootPath, @"Nancy.Demo.Authentication.Token"); - - FakeRootPathProvider.RootPath = rootPath; - - return new FakeRootPathProvider(); - } - } - } -} \ No newline at end of file diff --git a/src/Nancy.Demo.Authentication.Token.TestingDemo/packages.config b/src/Nancy.Demo.Authentication.Token.TestingDemo/packages.config deleted file mode 100644 index 0c7d926236..0000000000 --- a/src/Nancy.Demo.Authentication.Token.TestingDemo/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/Nancy.Demo.Authentication.Token/AuthModule.cs b/src/Nancy.Demo.Authentication.Token/AuthModule.cs deleted file mode 100644 index 86c35dd5d3..0000000000 --- a/src/Nancy.Demo.Authentication.Token/AuthModule.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Nancy.Demo.Authentication.Token -{ - using Nancy.Authentication.Token; - using Nancy.Security; - - public class AuthModule : NancyModule - { - public AuthModule(ITokenizer tokenizer) - : base("/auth") - { - Post["/"] = x => - { - var userName = (string)this.Request.Form.UserName; - var password = (string)this.Request.Form.Password; - - var userIdentity = UserDatabase.ValidateUser(userName, password); - - if (userIdentity == null) - { - return HttpStatusCode.Unauthorized; - } - - var token = tokenizer.Tokenize(userIdentity, Context); - - return new - { - Token = token, - }; - }; - - Get["/validation"] = _ => - { - this.RequiresAuthentication(); - return "Yay! You are authenticated!"; - }; - - Get["/admin"] = _ => - { - this.RequiresClaims(new[] { "admin" }); - return "Yay! You are authorized!"; - }; - } - } -} \ No newline at end of file diff --git a/src/Nancy.Demo.Authentication.Token/DemoUserIdentity.cs b/src/Nancy.Demo.Authentication.Token/DemoUserIdentity.cs deleted file mode 100644 index f8be8595b4..0000000000 --- a/src/Nancy.Demo.Authentication.Token/DemoUserIdentity.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Nancy.Demo.Authentication.Token -{ - using System.Collections.Generic; - - using Nancy.Security; - - public class DemoUserIdentity : IUserIdentity - { - public string UserName { get; set; } - public IEnumerable Claims { get; set; } - } -} \ No newline at end of file diff --git a/src/Nancy.Demo.Authentication.Token/Nancy.Demo.Authentication.Token.csproj b/src/Nancy.Demo.Authentication.Token/Nancy.Demo.Authentication.Token.csproj deleted file mode 100644 index 6657ca240c..0000000000 --- a/src/Nancy.Demo.Authentication.Token/Nancy.Demo.Authentication.Token.csproj +++ /dev/null @@ -1,103 +0,0 @@ - - - - - Debug - AnyCPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01} - Exe - Properties - Nancy.Demo.Authentication.Token - Nancy.Demo.Authentication.Token - v4.5 - 512 - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - - - - true - bin\MonoDebug\ - DEBUG;TRACE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - false - - - bin\MonoRelease\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - false - - - - - - - - - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - {97fa024a-f6ed-4086-bcc1-1a51be63474c} - Nancy.Authentication.Token - - - {aa7f66eb-ec2c-47de-855f-30b3e6ef2134} - Nancy.Hosting.Self - - - {34576216-0dca-4b0f-a0dc-9075e75a676f} - Nancy - - - - - \ No newline at end of file diff --git a/src/Nancy.Demo.Authentication.Token/Program.cs b/src/Nancy.Demo.Authentication.Token/Program.cs deleted file mode 100644 index 21893aa7cc..0000000000 --- a/src/Nancy.Demo.Authentication.Token/Program.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Nancy.Demo.Authentication.Token -{ - using System; - - using Nancy.Hosting.Self; - - class Program - { - static void Main(string[] args) - { - var uri = - new Uri("http://localhost:3579"); - - using (var host = new NancyHost(uri)) - { - host.Start(); - - Console.WriteLine("Your application is running on " + uri); - Console.WriteLine("Press any [Enter] to close the host."); - Console.ReadLine(); - } - } - } -} diff --git a/src/Nancy.Demo.Authentication.Token/TokenAuthBootstrapper.cs b/src/Nancy.Demo.Authentication.Token/TokenAuthBootstrapper.cs deleted file mode 100644 index 1bf0be96d3..0000000000 --- a/src/Nancy.Demo.Authentication.Token/TokenAuthBootstrapper.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Nancy.Demo.Authentication.Token -{ - using Nancy.Authentication.Token; - using Nancy.Bootstrapper; - using Nancy.TinyIoc; - - public class TokenAuthBootstrapper : DefaultNancyBootstrapper - { - protected override void ConfigureApplicationContainer(TinyIoCContainer container) - { - container.Register(new Tokenizer()); - // Example options for specifying additional values for token generation - - //container.Register(new Tokenizer(cfg => - // cfg.AdditionalItems( - // ctx => - // ctx.Request.Headers["X-Custom-Header"].FirstOrDefault(), - // ctx => ctx.Request.Query.extraValue))); - } - - protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context) - { - TokenAuthentication.Enable(pipelines, new TokenAuthenticationConfiguration(container.Resolve())); - } - } -} \ No newline at end of file diff --git a/src/Nancy.Demo.Authentication.Token/UserDatabase.cs b/src/Nancy.Demo.Authentication.Token/UserDatabase.cs deleted file mode 100644 index 59e7008819..0000000000 --- a/src/Nancy.Demo.Authentication.Token/UserDatabase.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Nancy.Demo.Authentication.Token -{ - using System; - using System.Collections.Generic; - using System.Linq; - - using Nancy.Security; - - public class UserDatabase - { - private static readonly List> Users = new List>(); - private static readonly Dictionary> Claims = new Dictionary>(); - - static UserDatabase() - { - Users.Add(new Tuple("demo", "demo")); - Claims.Add("demo", new List { "demo", "admin" }); - - Users.Add(new Tuple("nonadmin", "nonadmin")); - Claims.Add("nonadmin", new List { "demo", }); - } - - public static IUserIdentity ValidateUser(string userName, string password) - { - var user = Users.FirstOrDefault(x => x.Item1 == userName && x.Item2 == password); - if (user == null) - { - return null; - } - - var claims = Claims[user.Item1]; - return new DemoUserIdentity {UserName = user.Item1, Claims = claims}; - } - } -} \ No newline at end of file diff --git a/src/Nancy.Demo.Authentication.Token/app.config b/src/Nancy.Demo.Authentication.Token/app.config deleted file mode 100644 index 6fe47bb290..0000000000 --- a/src/Nancy.Demo.Authentication.Token/app.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/Nancy.sln b/src/Nancy.sln index 7093039a3e..659a68f237 100644 --- a/src/Nancy.sln +++ b/src/Nancy.sln @@ -123,14 +123,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nancy.Owin.Tests", "Nancy.O EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nancy.Demo.ConstraintRouting", "Nancy.Demo.ConstraintRouting\Nancy.Demo.ConstraintRouting.csproj", "{972C2D45-49B6-4109-9A3A-0C8BC9225B95}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nancy.Authentication.Token.Tests", "Nancy.Authentication.Token.Tests\Nancy.Authentication.Token.Tests.csproj", "{3C131D45-AF1D-4659-8B26-A9F55EED0D20}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nancy.Authentication.Token", "Nancy.Authentication.Token\Nancy.Authentication.Token.csproj", "{97FA024A-F6ED-4086-BCC1-1A51BE63474C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nancy.Demo.Authentication.Token", "Nancy.Demo.Authentication.Token\Nancy.Demo.Authentication.Token.csproj", "{35460AA4-B94A-4B64-9418-7243EC3D2F01}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nancy.Demo.Authentication.Token.TestingDemo", "Nancy.Demo.Authentication.Token.TestingDemo\Nancy.Demo.Authentication.Token.TestingDemo.csproj", "{9121DA01-7BFD-49F0-9937-0D3E7875ADB9}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nancy.Metadata.Modules", "Nancy.Metadata.Modules\Nancy.Metadata.Modules.csproj", "{DC8AAA8B-7FD0-47C4-9413-3CC94088BCC4}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nancy.Metadata.Modules.Tests", "Nancy.Metadata.Modules.Tests\Nancy.Metadata.Modules.Tests.csproj", "{508A3A68-2012-4E62-925F-2F3083A013FC}" @@ -817,54 +809,6 @@ Global {972C2D45-49B6-4109-9A3A-0C8BC9225B95}.Release|Any CPU.ActiveCfg = Release|Any CPU {972C2D45-49B6-4109-9A3A-0C8BC9225B95}.Release|Any CPU.Build.0 = Release|Any CPU {972C2D45-49B6-4109-9A3A-0C8BC9225B95}.Release|x86.ActiveCfg = Release|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.Debug|x86.ActiveCfg = Debug|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.MonoDebug|Any CPU.ActiveCfg = MonoDebug|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.MonoDebug|Any CPU.Build.0 = MonoDebug|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.MonoDebug|x86.ActiveCfg = Debug|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.MonoRelease|Any CPU.ActiveCfg = MonoRelease|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.MonoRelease|Any CPU.Build.0 = MonoRelease|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.MonoRelease|x86.ActiveCfg = Release|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.Release|Any CPU.Build.0 = Release|Any CPU - {3C131D45-AF1D-4659-8B26-A9F55EED0D20}.Release|x86.ActiveCfg = Release|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.Debug|x86.ActiveCfg = Debug|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.MonoDebug|Any CPU.ActiveCfg = MonoDebug|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.MonoDebug|Any CPU.Build.0 = MonoDebug|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.MonoDebug|x86.ActiveCfg = Debug|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.MonoRelease|Any CPU.ActiveCfg = MonoRelease|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.MonoRelease|Any CPU.Build.0 = MonoRelease|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.MonoRelease|x86.ActiveCfg = Release|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.Release|Any CPU.Build.0 = Release|Any CPU - {97FA024A-F6ED-4086-BCC1-1A51BE63474C}.Release|x86.ActiveCfg = Release|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.Debug|Any CPU.Build.0 = Debug|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.Debug|x86.ActiveCfg = Debug|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.MonoDebug|Any CPU.ActiveCfg = MonoDebug|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.MonoDebug|Any CPU.Build.0 = MonoDebug|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.MonoDebug|x86.ActiveCfg = Debug|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.MonoRelease|Any CPU.ActiveCfg = MonoRelease|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.MonoRelease|Any CPU.Build.0 = MonoRelease|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.MonoRelease|x86.ActiveCfg = Release|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.Release|Any CPU.ActiveCfg = Release|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.Release|Any CPU.Build.0 = Release|Any CPU - {35460AA4-B94A-4B64-9418-7243EC3D2F01}.Release|x86.ActiveCfg = Release|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.Debug|x86.ActiveCfg = Debug|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.MonoDebug|Any CPU.ActiveCfg = MonoDebug|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.MonoDebug|Any CPU.Build.0 = MonoDebug|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.MonoDebug|x86.ActiveCfg = Debug|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.MonoRelease|Any CPU.ActiveCfg = MonoRelease|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.MonoRelease|Any CPU.Build.0 = MonoRelease|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.MonoRelease|x86.ActiveCfg = Release|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.Release|Any CPU.Build.0 = Release|Any CPU - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9}.Release|x86.ActiveCfg = Release|Any CPU {DC8AAA8B-7FD0-47C4-9413-3CC94088BCC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC8AAA8B-7FD0-47C4-9413-3CC94088BCC4}.Debug|Any CPU.Build.0 = Debug|Any CPU {DC8AAA8B-7FD0-47C4-9413-3CC94088BCC4}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -966,10 +910,6 @@ Global {864AF449-0902-44FC-BEEE-06BA9A6F4A8F} = {E944109B-0B7A-4ADE-8602-004CEFA5897D} {34DDDA42-4041-4F92-87A6-F0E8CE12C7E3} = {A427F9F8-0A6F-4EEA-837F-FCDAB6E7D4B3} {972C2D45-49B6-4109-9A3A-0C8BC9225B95} = {4A24657F-9695-437B-9702-2541ED280628} - {3C131D45-AF1D-4659-8B26-A9F55EED0D20} = {A427F9F8-0A6F-4EEA-837F-FCDAB6E7D4B3} - {97FA024A-F6ED-4086-BCC1-1A51BE63474C} = {E944109B-0B7A-4ADE-8602-004CEFA5897D} - {35460AA4-B94A-4B64-9418-7243EC3D2F01} = {4A24657F-9695-437B-9702-2541ED280628} - {9121DA01-7BFD-49F0-9937-0D3E7875ADB9} = {4A24657F-9695-437B-9702-2541ED280628} {DC8AAA8B-7FD0-47C4-9413-3CC94088BCC4} = {E944109B-0B7A-4ADE-8602-004CEFA5897D} {508A3A68-2012-4E62-925F-2F3083A013FC} = {A427F9F8-0A6F-4EEA-837F-FCDAB6E7D4B3} {7959D79C-9E2B-4871-9470-6A6B527ABE5E} = {4A24657F-9695-437B-9702-2541ED280628}