diff --git a/Security.sln b/Security.sln
index 5ae54786fc..976cfd4ea6 100644
--- a/Security.sln
+++ b/Security.sln
@@ -28,6 +28,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SocialSample", "samples\Soc
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Security.Google", "src\Microsoft.AspNet.Security.Google\Microsoft.AspNet.Security.Google.kproj", "{89BF8535-A849-458E-868A-A68FCF620486}"
EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Security.Twitter", "src\Microsoft.AspNet.Security.Twitter\Microsoft.AspNet.Security.Twitter.kproj", "{C96B77EA-4078-4C31-BDB2-878F11C5E061}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -108,6 +110,16 @@ Global
{89BF8535-A849-458E-868A-A68FCF620486}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{89BF8535-A849-458E-868A-A68FCF620486}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{89BF8535-A849-458E-868A-A68FCF620486}.Release|x86.ActiveCfg = Release|Any CPU
+ {C96B77EA-4078-4C31-BDB2-878F11C5E061}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C96B77EA-4078-4C31-BDB2-878F11C5E061}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C96B77EA-4078-4C31-BDB2-878F11C5E061}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {C96B77EA-4078-4C31-BDB2-878F11C5E061}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {C96B77EA-4078-4C31-BDB2-878F11C5E061}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C96B77EA-4078-4C31-BDB2-878F11C5E061}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C96B77EA-4078-4C31-BDB2-878F11C5E061}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C96B77EA-4078-4C31-BDB2-878F11C5E061}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {C96B77EA-4078-4C31-BDB2-878F11C5E061}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {C96B77EA-4078-4C31-BDB2-878F11C5E061}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -120,5 +132,6 @@ Global
{3984651C-FD44-4394-8793-3D14EE348C04} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{8C73D216-332D-41D8-BFD0-45BC4BC36552} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
{89BF8535-A849-458E-868A-A68FCF620486} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
+ {C96B77EA-4078-4C31-BDB2-878F11C5E061} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
EndGlobalSection
EndGlobal
diff --git a/samples/SocialSample/Project.json b/samples/SocialSample/Project.json
index 525d9dd6a6..1af9beef0a 100644
--- a/samples/SocialSample/Project.json
+++ b/samples/SocialSample/Project.json
@@ -7,6 +7,7 @@
"Microsoft.AspNet.Security.Cookies": "1.0.0-*",
"Microsoft.AspNet.Security.Facebook": "1.0.0-*",
"Microsoft.AspNet.Security.Google": "1.0.0-*",
+ "Microsoft.AspNet.Security.Twitter": "1.0.0-*",
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
"Microsoft.Framework.DependencyInjection": "1.0.0-*"
},
diff --git a/samples/SocialSample/Startup.cs b/samples/SocialSample/Startup.cs
index 68dd967e40..bc2e967b3c 100644
--- a/samples/SocialSample/Startup.cs
+++ b/samples/SocialSample/Startup.cs
@@ -4,6 +4,7 @@ using Microsoft.AspNet.Http.Security;
using Microsoft.AspNet.Security.Cookies;
using Microsoft.AspNet.Security.Facebook;
using Microsoft.AspNet.Security.Google;
+using Microsoft.AspNet.Security.Twitter;
namespace CookieSample
{
@@ -32,6 +33,12 @@ namespace CookieSample
ClientSecret = "n2Q-GEw9RQjzcRbU3qhfTj8f",
});
+ app.UseTwitterAuthentication(new TwitterAuthenticationOptions()
+ {
+ ConsumerKey = "6XaCTaLbMqfj6ww3zvZ5g",
+ ConsumerSecret = "Il2eFzGIrYhz6BWjYhVXBPQSfZuS4xoHpSSyD9PI",
+ });
+
// Choose an authentication type
app.Map("/login", signoutApp =>
{
diff --git a/src/Microsoft.AspNet.Security.Twitter/Messages/AccessToken.cs b/src/Microsoft.AspNet.Security.Twitter/Messages/AccessToken.cs
new file mode 100644
index 0000000000..f935da72a7
--- /dev/null
+++ b/src/Microsoft.AspNet.Security.Twitter/Messages/AccessToken.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNet.Security.Twitter.Messages
+{
+ ///
+ /// The Twitter access token retrieved from the access token endpoint.
+ ///
+ public class AccessToken : RequestToken
+ {
+ ///
+ /// Gets or sets the Twitter User ID.
+ ///
+ public string UserId { get; set; }
+
+ ///
+ /// Gets or sets the Twitter screen name.
+ ///
+ public string ScreenName { get; set; }
+ }
+}
diff --git a/src/Microsoft.AspNet.Security.Twitter/Messages/RequestToken.cs b/src/Microsoft.AspNet.Security.Twitter/Messages/RequestToken.cs
new file mode 100644
index 0000000000..f801e555f7
--- /dev/null
+++ b/src/Microsoft.AspNet.Security.Twitter/Messages/RequestToken.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNet.Http.Security;
+
+namespace Microsoft.AspNet.Security.Twitter.Messages
+{
+ ///
+ /// The Twitter request token obtained from the request token endpoint.
+ ///
+ public class RequestToken
+ {
+ ///
+ /// Gets or sets the Twitter request token.
+ ///
+ public string Token { get; set; }
+
+ ///
+ /// Gets or sets the Twitter token secret.
+ ///
+ public string TokenSecret { get; set; }
+
+ public bool CallbackConfirmed { get; set; }
+
+ ///
+ /// Gets or sets a property bag for common authentication properties.
+ ///
+ public AuthenticationProperties Properties { get; set; }
+ }
+}
diff --git a/src/Microsoft.AspNet.Security.Twitter/Messages/RequestTokenSerializer.cs b/src/Microsoft.AspNet.Security.Twitter/Messages/RequestTokenSerializer.cs
new file mode 100644
index 0000000000..725dcc341f
--- /dev/null
+++ b/src/Microsoft.AspNet.Security.Twitter/Messages/RequestTokenSerializer.cs
@@ -0,0 +1,93 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using Microsoft.AspNet.Http.Security;
+using Microsoft.AspNet.Security.DataHandler.Serializer;
+
+namespace Microsoft.AspNet.Security.Twitter.Messages
+{
+ ///
+ /// Serializes and deserializes Twitter request and access tokens so that they can be used by other application components.
+ ///
+ public class RequestTokenSerializer : IDataSerializer
+ {
+ private const int FormatVersion = 1;
+
+ ///
+ /// Serialize a request token.
+ ///
+ /// The token to serialize
+ /// A byte array containing the serialized token
+ [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Dispose is idempotent")]
+ public virtual byte[] Serialize(RequestToken model)
+ {
+ using (var memory = new MemoryStream())
+ {
+ using (var writer = new BinaryWriter(memory))
+ {
+ Write(writer, model);
+ writer.Flush();
+ return memory.ToArray();
+ }
+ }
+ }
+
+ ///
+ /// Deserializes a request token.
+ ///
+ /// A byte array containing the serialized token
+ /// The Twitter request token
+ [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Dispose is idempotent")]
+ public virtual RequestToken Deserialize(byte[] data)
+ {
+ using (var memory = new MemoryStream(data))
+ {
+ using (var reader = new BinaryReader(memory))
+ {
+ return Read(reader);
+ }
+ }
+ }
+
+ ///
+ /// Writes a Twitter request token as a series of bytes. Used by the method.
+ ///
+ /// The writer to use in writing the token
+ /// The token to write
+ public static void Write([NotNull] BinaryWriter writer, [NotNull] RequestToken token)
+ {
+ writer.Write(FormatVersion);
+ writer.Write(token.Token);
+ writer.Write(token.TokenSecret);
+ writer.Write(token.CallbackConfirmed);
+ PropertiesSerializer.Write(writer, token.Properties);
+ }
+
+ ///
+ /// Reads a Twitter request token from a series of bytes. Used by the method.
+ ///
+ /// The reader to use in reading the token bytes
+ /// The token
+ public static RequestToken Read([NotNull] BinaryReader reader)
+ {
+ if (reader.ReadInt32() != FormatVersion)
+ {
+ return null;
+ }
+
+ string token = reader.ReadString();
+ string tokenSecret = reader.ReadString();
+ bool callbackConfirmed = reader.ReadBoolean();
+ AuthenticationProperties properties = PropertiesSerializer.Read(reader);
+ if (properties == null)
+ {
+ return null;
+ }
+
+ return new RequestToken { Token = token, TokenSecret = tokenSecret, CallbackConfirmed = callbackConfirmed, Properties = properties };
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Security.Twitter/Messages/Serializers.cs b/src/Microsoft.AspNet.Security.Twitter/Messages/Serializers.cs
new file mode 100644
index 0000000000..3d2e1a458d
--- /dev/null
+++ b/src/Microsoft.AspNet.Security.Twitter/Messages/Serializers.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNet.Security.DataHandler.Serializer;
+
+namespace Microsoft.AspNet.Security.Twitter.Messages
+{
+ ///
+ /// Provides access to a request token serializer.
+ ///
+ public static class Serializers
+ {
+ static Serializers()
+ {
+ RequestToken = new RequestTokenSerializer();
+ }
+
+ ///
+ /// Gets or sets a statically-avaliable serializer object. The value for this property will be by default.
+ ///
+ public static IDataSerializer RequestToken { get; private set; }
+ }
+}
diff --git a/src/Microsoft.AspNet.Security.Twitter/Microsoft.AspNet.Security.Twitter.kproj b/src/Microsoft.AspNet.Security.Twitter/Microsoft.AspNet.Security.Twitter.kproj
new file mode 100644
index 0000000000..48dfc30697
--- /dev/null
+++ b/src/Microsoft.AspNet.Security.Twitter/Microsoft.AspNet.Security.Twitter.kproj
@@ -0,0 +1,28 @@
+
+
+
+ 12.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ c96b77ea-4078-4c31-bdb2-878f11c5e061
+ Library
+
+
+
+ ConsoleDebugger
+
+
+ WebDebugger
+
+
+
+
+
+
+
+ 2.0
+
+
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Security.Twitter/NotNullAttribute.cs b/src/Microsoft.AspNet.Security.Twitter/NotNullAttribute.cs
new file mode 100644
index 0000000000..0d6e98224d
--- /dev/null
+++ b/src/Microsoft.AspNet.Security.Twitter/NotNullAttribute.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.AspNet.Security.Twitter
+{
+ [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
+ internal sealed class NotNullAttribute : Attribute
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Security.Twitter/Notifications/ITwitterAuthenticationNotifications.cs b/src/Microsoft.AspNet.Security.Twitter/Notifications/ITwitterAuthenticationNotifications.cs
new file mode 100644
index 0000000000..55fd548926
--- /dev/null
+++ b/src/Microsoft.AspNet.Security.Twitter/Notifications/ITwitterAuthenticationNotifications.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNet.Security.Twitter
+{
+ ///
+ /// Specifies callback methods which the invokes to enable developer control over the authentication process. />
+ ///
+ public interface ITwitterAuthenticationNotifications
+ {
+ ///
+ /// Invoked whenever Twitter succesfully authenticates a user
+ ///
+ /// Contains information about the login session as well as the user .
+ /// A representing the completed operation.
+ Task Authenticated(TwitterAuthenticatedContext context);
+
+ ///
+ /// Invoked prior to the being saved in a local cookie and the browser being redirected to the originally requested URL.
+ ///
+ ///
+ /// A representing the completed operation.
+ Task ReturnEndpoint(TwitterReturnEndpointContext context);
+
+ ///
+ /// Called when a Challenge causes a redirect to authorize endpoint in the Twitter middleware
+ ///
+ /// Contains redirect URI and of the challenge
+ void ApplyRedirect(TwitterApplyRedirectContext context);
+ }
+}
diff --git a/src/Microsoft.AspNet.Security.Twitter/Notifications/TwitterApplyRedirectContext.cs b/src/Microsoft.AspNet.Security.Twitter/Notifications/TwitterApplyRedirectContext.cs
new file mode 100644
index 0000000000..3d5ab80ffc
--- /dev/null
+++ b/src/Microsoft.AspNet.Security.Twitter/Notifications/TwitterApplyRedirectContext.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Http.Security;
+using Microsoft.AspNet.Security.Notifications;
+
+namespace Microsoft.AspNet.Security.Twitter
+{
+ ///
+ /// Context passed when a Challenge causes a redirect to authorize endpoint in the Twitter middleware
+ ///
+ public class TwitterApplyRedirectContext : BaseContext
+ {
+ ///
+ /// Creates a new context object.
+ ///
+ /// The HTTP request context
+ /// The Facebook middleware options
+ /// The authenticaiton properties of the challenge
+ /// The initial redirect URI
+ public TwitterApplyRedirectContext(HttpContext context, TwitterAuthenticationOptions options,
+ AuthenticationProperties properties, string redirectUri)
+ : base(context, options)
+ {
+ RedirectUri = redirectUri;
+ Properties = properties;
+ }
+
+ ///
+ /// Gets the URI used for the redirect operation.
+ ///
+ public string RedirectUri { get; private set; }
+
+ ///
+ /// Gets the authenticaiton properties of the challenge
+ ///
+ public AuthenticationProperties Properties { get; private set; }
+ }
+}
diff --git a/src/Microsoft.AspNet.Security.Twitter/Notifications/TwitterAuthenticatedContext.cs b/src/Microsoft.AspNet.Security.Twitter/Notifications/TwitterAuthenticatedContext.cs
new file mode 100644
index 0000000000..0c050d9e8d
--- /dev/null
+++ b/src/Microsoft.AspNet.Security.Twitter/Notifications/TwitterAuthenticatedContext.cs
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Security.Claims;
+using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Http.Security;
+using Microsoft.AspNet.Security.Notifications;
+
+namespace Microsoft.AspNet.Security.Twitter
+{
+ ///
+ /// Contains information about the login session as well as the user .
+ ///
+ public class TwitterAuthenticatedContext : BaseContext
+ {
+ ///
+ /// Initializes a
+ ///
+ /// The HTTP environment
+ /// Twitter user ID
+ /// Twitter screen name
+ /// Twitter access token
+ /// Twitter access token secret
+ public TwitterAuthenticatedContext(
+ HttpContext context,
+ string userId,
+ string screenName,
+ string accessToken,
+ string accessTokenSecret)
+ : base(context)
+ {
+ UserId = userId;
+ ScreenName = screenName;
+ AccessToken = accessToken;
+ AccessTokenSecret = accessTokenSecret;
+ }
+
+ ///
+ /// Gets the Twitter user ID
+ ///
+ public string UserId { get; private set; }
+
+ ///
+ /// Gets the Twitter screen name
+ ///
+ public string ScreenName { get; private set; }
+
+ ///
+ /// Gets the Twitter access token
+ ///
+ public string AccessToken { get; private set; }
+
+ ///
+ /// Gets the Twitter access token secret
+ ///
+ public string AccessTokenSecret { get; private set; }
+
+ ///
+ /// Gets the representing the user
+ ///
+ public ClaimsIdentity Identity { get; set; }
+
+ ///
+ /// Gets or sets a property bag for common authentication properties
+ ///
+ public AuthenticationProperties Properties { get; set; }
+ }
+}
diff --git a/src/Microsoft.AspNet.Security.Twitter/Notifications/TwitterAuthenticationNotifications.cs b/src/Microsoft.AspNet.Security.Twitter/Notifications/TwitterAuthenticationNotifications.cs
new file mode 100644
index 0000000000..2c6ff19d5e
--- /dev/null
+++ b/src/Microsoft.AspNet.Security.Twitter/Notifications/TwitterAuthenticationNotifications.cs
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNet.Security.Twitter
+{
+ ///
+ /// Default implementation.
+ ///
+ public class TwitterAuthenticationNotifications : ITwitterAuthenticationNotifications
+ {
+ ///
+ /// Initializes a
+ ///
+ public TwitterAuthenticationNotifications()
+ {
+ OnAuthenticated = context => Task.FromResult