summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj3
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs122
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs118
-rw-r--r--samples/OAuthConsumer/App_Code/InMemoryTokenManager.cs54
-rw-r--r--samples/OAuthConsumer/Default.aspx1
-rw-r--r--samples/OAuthConsumer/SampleWcf.aspx.cs1
-rw-r--r--samples/OAuthConsumer/SignInWithTwitter.aspx38
-rw-r--r--samples/OAuthConsumer/SignInWithTwitter.aspx.cs37
-rw-r--r--samples/OAuthConsumer/images/Sign-in-with-Twitter-darker.pngbin0 -> 2370 bytes
9 files changed, 319 insertions, 55 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
index 28c4ff4..eab27db 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
+++ b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
@@ -65,9 +65,11 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
+ <Reference Include="System.configuration" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
+ <Reference Include="System.Web" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
@@ -82,6 +84,7 @@
<Compile Include="CustomExtensions\AcmeRequest.cs" />
<Compile Include="CustomExtensions\AcmeResponse.cs" />
<Compile Include="GoogleConsumer.cs" />
+ <Compile Include="InMemoryTokenManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TwitterConsumer.cs" />
<Compile Include="Util.cs" />
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs b/samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs
new file mode 100644
index 0000000..e83817a
--- /dev/null
+++ b/samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs
@@ -0,0 +1,122 @@
+//-----------------------------------------------------------------------
+// <copyright file="InMemoryTokenManager.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.ApplicationBlock {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+ using DotNetOpenAuth.OAuth.Messages;
+
+ /// <summary>
+ /// A token manager that only retains tokens in memory.
+ /// Meant for SHORT TERM USE TOKENS ONLY.
+ /// </summary>
+ /// <remarks>
+ /// A likely application of this class is for "Sign In With Twitter",
+ /// where the user only signs in without providing any authorization to access
+ /// Twitter APIs except to authenticate, since that access token is only useful once.
+ /// </remarks>
+ public class InMemoryTokenManager : IConsumerTokenManager {
+ private Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InMemoryTokenManager"/> class.
+ /// </summary>
+ /// <param name="consumerKey">The consumer key.</param>
+ /// <param name="consumerSecret">The consumer secret.</param>
+ public InMemoryTokenManager(string consumerKey, string consumerSecret) {
+ if (String.IsNullOrEmpty(consumerKey)) {
+ throw new ArgumentNullException("consumerKey");
+ }
+
+ this.ConsumerKey = consumerKey;
+ this.ConsumerSecret = consumerSecret;
+ }
+
+ /// <summary>
+ /// Gets the consumer key.
+ /// </summary>
+ /// <value>The consumer key.</value>
+ public string ConsumerKey { get; private set; }
+
+ /// <summary>
+ /// Gets the consumer secret.
+ /// </summary>
+ /// <value>The consumer secret.</value>
+ public string ConsumerSecret { get; private set; }
+
+ #region ITokenManager Members
+
+ /// <summary>
+ /// Gets the Token Secret given a request or access token.
+ /// </summary>
+ /// <param name="token">The request or access token.</param>
+ /// <returns>
+ /// The secret associated with the given token.
+ /// </returns>
+ /// <exception cref="ArgumentException">Thrown if the secret cannot be found for the given token.</exception>
+ public string GetTokenSecret(string token) {
+ return this.tokensAndSecrets[token];
+ }
+
+ /// <summary>
+ /// Stores a newly generated unauthorized request token, secret, and optional
+ /// application-specific parameters for later recall.
+ /// </summary>
+ /// <param name="request">The request message that resulted in the generation of a new unauthorized request token.</param>
+ /// <param name="response">The response message that includes the unauthorized request token.</param>
+ /// <exception cref="ArgumentException">Thrown if the consumer key is not registered, or a required parameter was not found in the parameters collection.</exception>
+ /// <remarks>
+ /// Request tokens stored by this method SHOULD NOT associate any user account with this token.
+ /// It usually opens up security holes in your application to do so. Instead, you associate a user
+ /// account with access tokens (not request tokens) in the <see cref="ExpireRequestTokenAndStoreNewAccessToken"/>
+ /// method.
+ /// </remarks>
+ public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) {
+ this.tokensAndSecrets[response.Token] = response.TokenSecret;
+ }
+
+ /// <summary>
+ /// Deletes a request token and its associated secret and stores a new access token and secret.
+ /// </summary>
+ /// <param name="consumerKey">The Consumer that is exchanging its request token for an access token.</param>
+ /// <param name="requestToken">The Consumer's request token that should be deleted/expired.</param>
+ /// <param name="accessToken">The new access token that is being issued to the Consumer.</param>
+ /// <param name="accessTokenSecret">The secret associated with the newly issued access token.</param>
+ /// <remarks>
+ /// <para>
+ /// Any scope of granted privileges associated with the request token from the
+ /// original call to <see cref="StoreNewRequestToken"/> should be carried over
+ /// to the new Access Token.
+ /// </para>
+ /// <para>
+ /// To associate a user account with the new access token,
+ /// <see cref="System.Web.HttpContext.User">HttpContext.Current.User</see> may be
+ /// useful in an ASP.NET web application within the implementation of this method.
+ /// Alternatively you may store the access token here without associating with a user account,
+ /// and wait until <see cref="WebConsumer.ProcessUserAuthorization()"/> or
+ /// <see cref="DesktopConsumer.ProcessUserAuthorization(string, string)"/> return the access
+ /// token to associate the access token with a user account at that point.
+ /// </para>
+ /// </remarks>
+ public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) {
+ this.tokensAndSecrets.Remove(requestToken);
+ this.tokensAndSecrets[accessToken] = accessTokenSecret;
+ }
+
+ /// <summary>
+ /// Classifies a token as a request token or an access token.
+ /// </summary>
+ /// <param name="token">The token to classify.</param>
+ /// <returns>Request or Access token, or invalid if the token is not recognized.</returns>
+ public TokenType GetTokenType(string token) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
index 29973c2..2e160f4 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
@@ -7,10 +7,13 @@
namespace DotNetOpenAuth.ApplicationBlock {
using System;
using System.Collections.Generic;
+ using System.Configuration;
using System.IO;
using System.Net;
+ using System.Web;
using System.Xml;
using System.Xml.Linq;
+ using System.Xml.XPath;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
@@ -20,7 +23,8 @@ namespace DotNetOpenAuth.ApplicationBlock {
/// </summary>
public static class TwitterConsumer {
/// <summary>
- /// The description of Twitter's OAuth protocol URIs.
+ /// The description of Twitter's OAuth protocol URIs for use with actually reading/writing
+ /// a user's private Twitter data.
/// </summary>
public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription {
RequestTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
@@ -30,6 +34,16 @@ namespace DotNetOpenAuth.ApplicationBlock {
};
/// <summary>
+ /// The description of Twitter's OAuth protocol URIs for use with their "Sign in with Twitter" feature.
+ /// </summary>
+ public static readonly ServiceProviderDescription SignInWithTwitterServiceDescription = new ServiceProviderDescription {
+ RequestTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
+ UserAuthorizationEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/authenticate", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
+ AccessTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
+ TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
+ };
+
+ /// <summary>
/// The URI to get a user's favorites.
/// </summary>
private static readonly MessageReceivingEndpoint GetFavoritesEndpoint = new MessageReceivingEndpoint("http://twitter.com/favorites.xml", HttpDeliveryMethods.GetRequest);
@@ -43,6 +57,45 @@ namespace DotNetOpenAuth.ApplicationBlock {
private static readonly MessageReceivingEndpoint UpdateProfileImageEndpoint = new MessageReceivingEndpoint("http://twitter.com/account/update_profile_image.xml", HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);
+ private static readonly MessageReceivingEndpoint VerifyCredentialsEndpoint = new MessageReceivingEndpoint("http://api.twitter.com/1/account/verify_credentials.xml", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);
+
+ private static InMemoryTokenManager ShortTermUserSessionTokenManager {
+ get {
+ var store = HttpContext.Current.Session;
+ var tokenManager = (InMemoryTokenManager)store["TwitterShortTermUserSessionTokenManager"];
+ if (tokenManager == null) {
+ string consumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"];
+ string consumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"];
+ if (IsTwitterConsumerConfigured) {
+ tokenManager = new InMemoryTokenManager(consumerKey, consumerSecret);
+ store["TwitterShortTermUserSessionTokenManager"] = tokenManager;
+ } else {
+ throw new InvalidOperationException("No Twitter OAuth consumer key and secret could be found in web.config AppSettings.");
+ }
+ }
+
+ return tokenManager;
+ }
+ }
+
+ private static WebConsumer signInConsumer;
+
+ private static object signInConsumerInitLock = new object();
+
+ private static WebConsumer TwitterSignIn {
+ get {
+ if (signInConsumer == null) {
+ lock (signInConsumerInitLock) {
+ if (signInConsumer == null) {
+ signInConsumer = new WebConsumer(SignInWithTwitterServiceDescription, ShortTermUserSessionTokenManager);
+ }
+ }
+ }
+
+ return signInConsumer;
+ }
+ }
+
/// <summary>
/// Initializes static members of the <see cref="TwitterConsumer"/> class.
/// </summary>
@@ -51,6 +104,13 @@ namespace DotNetOpenAuth.ApplicationBlock {
ServicePointManager.FindServicePoint(GetFavoritesEndpoint.Location).Expect100Continue = false;
}
+ public static bool IsTwitterConsumerConfigured {
+ get {
+ return !string.IsNullOrEmpty(ConfigurationManager.AppSettings["twitterConsumerKey"]) &&
+ !string.IsNullOrEmpty(ConfigurationManager.AppSettings["twitterConsumerSecret"]);
+ }
+ }
+
public static XDocument GetUpdates(ConsumerBase twitter, string accessToken) {
IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(GetFriendTimelineStatusEndpoint, accessToken);
return XDocument.Load(XmlReader.Create(response.GetResponseReader()));
@@ -87,5 +147,61 @@ namespace DotNetOpenAuth.ApplicationBlock {
string responseString = response.GetResponseReader().ReadToEnd();
return XDocument.Parse(responseString);
}
+
+ public static XDocument VerifyCredentials(ConsumerBase twitter, string accessToken) {
+ IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(VerifyCredentialsEndpoint, accessToken);
+ return XDocument.Load(XmlReader.Create(response.GetResponseReader()));
+ }
+
+ public static string GetUsername(ConsumerBase twitter, string accessToken) {
+ XDocument xml = VerifyCredentials(twitter, accessToken);
+ XPathNavigator nav = xml.CreateNavigator();
+ return nav.SelectSingleNode("/user/screen_name").Value;
+ }
+
+ /// <summary>
+ /// Prepares a redirect that will send the user to Twitter to sign in.
+ /// </summary>
+ /// <param name="forceNewLogin">if set to <c>true</c> the user will be required to re-enter their Twitter credentials even if already logged in to Twitter.</param>
+ /// <returns>The redirect message.</returns>
+ /// <remarks>
+ /// Call <see cref="OutgoingWebResponse.Send"/> or
+ /// <c>return StartSignInWithTwitter().<see cref="MessagingUtilities.AsActionResult">AsActionResult()</see></c>
+ /// to actually perform the redirect.
+ /// </remarks>
+ public static OutgoingWebResponse StartSignInWithTwitter(bool forceNewLogin) {
+ var redirectParameters = new Dictionary<string, string>();
+ if (forceNewLogin) {
+ redirectParameters["force_login"] = "true";
+ }
+ Uri callback = MessagingUtilities.GetRequestUrlFromContext().StripQueryArgumentsWithPrefix("oauth_");
+ var request = TwitterSignIn.PrepareRequestUserAuthorization(callback, null, redirectParameters);
+ return TwitterSignIn.Channel.PrepareResponse(request);
+ }
+
+ /// <summary>
+ /// Checks the incoming web request to see if it carries a Twitter authentication response,
+ /// and provides the user's Twitter screen name and unique id if available.
+ /// </summary>
+ /// <param name="screenName">The user's Twitter screen name.</param>
+ /// <param name="userId">The user's Twitter unique user ID.</param>
+ /// <returns>
+ /// A value indicating whether Twitter authentication was successful;
+ /// otherwise <c>false</c> to indicate that no Twitter response was present.
+ /// </returns>
+ public static bool TryFinishSignInWithTwitter(out string screenName, out int userId) {
+ screenName = null;
+ userId = 0;
+ var response = TwitterSignIn.ProcessUserAuthorization();
+ if (response == null) {
+ return false;
+ }
+
+ XDocument xml = VerifyCredentials(TwitterSignIn, response.AccessToken);
+ XPathNavigator nav = xml.CreateNavigator();
+ screenName = nav.SelectSingleNode("/user/screen_name").Value;
+ userId = int.Parse(nav.SelectSingleNode("/user/id").Value);
+ return true;
+ }
}
}
diff --git a/samples/OAuthConsumer/App_Code/InMemoryTokenManager.cs b/samples/OAuthConsumer/App_Code/InMemoryTokenManager.cs
deleted file mode 100644
index 120f00a..0000000
--- a/samples/OAuthConsumer/App_Code/InMemoryTokenManager.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="InMemoryTokenManager.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using DotNetOpenAuth.OAuth.ChannelElements;
-using DotNetOpenAuth.OAuth.Messages;
-
-public class InMemoryTokenManager : IConsumerTokenManager {
- private Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>();
-
- public InMemoryTokenManager(string consumerKey, string consumerSecret) {
- if (String.IsNullOrEmpty(consumerKey)) {
- throw new ArgumentNullException("consumerKey");
- }
-
- this.ConsumerKey = consumerKey;
- this.ConsumerSecret = consumerSecret;
- }
-
- public string ConsumerKey { get; private set; }
-
- public string ConsumerSecret { get; private set; }
-
- #region ITokenManager Members
-
- public string GetTokenSecret(string token) {
- return this.tokensAndSecrets[token];
- }
-
- public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) {
- this.tokensAndSecrets[response.Token] = response.TokenSecret;
- }
-
- public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) {
- this.tokensAndSecrets.Remove(requestToken);
- this.tokensAndSecrets[accessToken] = accessTokenSecret;
- }
-
- /// <summary>
- /// Classifies a token as a request token or an access token.
- /// </summary>
- /// <param name="token">The token to classify.</param>
- /// <returns>Request or Access token, or invalid if the token is not recognized.</returns>
- public TokenType GetTokenType(string token) {
- throw new NotImplementedException();
- }
-
- #endregion
-}
diff --git a/samples/OAuthConsumer/Default.aspx b/samples/OAuthConsumer/Default.aspx
index aa4ef79..c952877 100644
--- a/samples/OAuthConsumer/Default.aspx
+++ b/samples/OAuthConsumer/Default.aspx
@@ -8,6 +8,7 @@
<ul>
<li><a href="GoogleAddressBook.aspx">Download your Gmail address book</a></li>
<li><a href="Twitter.aspx">Get your Twitter updates</a></li>
+ <li><a href="SignInWithTwitter.aspx">Sign In With Twitter</a></li>
<li><a href="SampleWcf.aspx">Interop with Service Provider sample using WCF w/ OAuth</a></li>
</ul>
</asp:Content>
diff --git a/samples/OAuthConsumer/SampleWcf.aspx.cs b/samples/OAuthConsumer/SampleWcf.aspx.cs
index 7572dd8..d1af6a1 100644
--- a/samples/OAuthConsumer/SampleWcf.aspx.cs
+++ b/samples/OAuthConsumer/SampleWcf.aspx.cs
@@ -8,6 +8,7 @@ using System.ServiceModel.Channels;
using System.ServiceModel.Security;
using System.Web.UI.WebControls;
using DotNetOpenAuth;
+using DotNetOpenAuth.ApplicationBlock;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
diff --git a/samples/OAuthConsumer/SignInWithTwitter.aspx b/samples/OAuthConsumer/SignInWithTwitter.aspx
new file mode 100644
index 0000000..b1dd606
--- /dev/null
+++ b/samples/OAuthConsumer/SignInWithTwitter.aspx
@@ -0,0 +1,38 @@
+<%@ Page Language="C#" AutoEventWireup="true" CodeFile="SignInWithTwitter.aspx.cs"
+ Inherits="SignInWithTwitter" %>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head runat="server">
+ <title>Sign-in with Twitter</title>
+</head>
+<body>
+ <form id="form1" runat="server">
+ <div>
+ <asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0">
+ <asp:View ID="View1" runat="server">
+ <h2>
+ Twitter setup</h2>
+ <p>
+ A Twitter client app must be endorsed by a Twitter user.
+ </p>
+ <ol>
+ <li><a target="_blank" href="https://twitter.com/oauth_clients">Visit Twitter and create
+ a client app</a>. </li>
+ <li>Modify your web.config file to include your consumer key and consumer secret.</li>
+ </ol>
+ </asp:View>
+ <asp:View ID="View2" runat="server">
+ <asp:ImageButton ImageUrl="~/images/Sign-in-with-Twitter-darker.png" runat="server"
+ AlternateText="Sign In With Twitter" ID="signInButton" OnClick="signInButton_Click" />
+ <asp:CheckBox Text="force re-login" runat="server" ID="forceLoginCheckbox" />
+ <br />
+ <asp:Panel runat="server" ID="loggedInPanel" Visible="false">
+ Now logged in as
+ <asp:Label Text="[name]" runat="server" ID="loggedInName" />
+ </asp:Panel>
+ </asp:View>
+ </asp:MultiView>
+ </form>
+</body>
+</html>
diff --git a/samples/OAuthConsumer/SignInWithTwitter.aspx.cs b/samples/OAuthConsumer/SignInWithTwitter.aspx.cs
new file mode 100644
index 0000000..688471a
--- /dev/null
+++ b/samples/OAuthConsumer/SignInWithTwitter.aspx.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Linq;
+using System.Web;
+using System.Web.Security;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+using System.Xml.Linq;
+using System.Xml.XPath;
+using DotNetOpenAuth.ApplicationBlock;
+using DotNetOpenAuth.OAuth;
+
+public partial class SignInWithTwitter : System.Web.UI.Page {
+ protected void Page_Load(object sender, EventArgs e) {
+ if (TwitterConsumer.IsTwitterConsumerConfigured) {
+ MultiView1.ActiveViewIndex = 1;
+
+ if (!IsPostBack) {
+ string screenName;
+ int userId;
+ if (TwitterConsumer.TryFinishSignInWithTwitter(out screenName, out userId)) {
+ loggedInPanel.Visible = true;
+ loggedInName.Text = screenName;
+
+ // In a real app, the Twitter username would likely be used
+ // to log the user into the application.
+ ////FormsAuthentication.RedirectFromLoginPage(screenName, false);
+ }
+ }
+ }
+ }
+
+ protected void signInButton_Click(object sender, ImageClickEventArgs e) {
+ TwitterConsumer.StartSignInWithTwitter(forceLoginCheckbox.Checked).Send();
+ }
+} \ No newline at end of file
diff --git a/samples/OAuthConsumer/images/Sign-in-with-Twitter-darker.png b/samples/OAuthConsumer/images/Sign-in-with-Twitter-darker.png
new file mode 100644
index 0000000..746b6b9
--- /dev/null
+++ b/samples/OAuthConsumer/images/Sign-in-with-Twitter-darker.png
Binary files differ