diff options
19 files changed, 158 insertions, 320 deletions
diff --git a/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj b/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj index c9c8848..703f1c7 100644 --- a/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj +++ b/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj @@ -176,6 +176,10 @@ <Content Include="Views\Web.config" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\..\samples\DotNetOpenAuth.ApplicationBlock\DotNetOpenAuth.ApplicationBlock.csproj"> + <Project>{aa78d112-d889-414b-a7d4-467b34c7b663}</Project> + <Name>DotNetOpenAuth.ApplicationBlock</Name> + </ProjectReference> <ProjectReference Include="..\..\src\DotNetOpenAuth.InfoCard.UI\DotNetOpenAuth.InfoCard.UI.csproj"> <Project>{E040EB58-B4D2-457B-A023-AE6EF3BD34DE}</Project> <Name>DotNetOpenAuth.InfoCard.UI</Name> diff --git a/projecttemplates/MvcRelyingParty/OAuthTokenEndpoint.ashx.cs b/projecttemplates/MvcRelyingParty/OAuthTokenEndpoint.ashx.cs index 6f52ba4..f9a98f7 100644 --- a/projecttemplates/MvcRelyingParty/OAuthTokenEndpoint.ashx.cs +++ b/projecttemplates/MvcRelyingParty/OAuthTokenEndpoint.ashx.cs @@ -12,6 +12,7 @@ namespace MvcRelyingParty { using System.Threading.Tasks; using System.Web; using System.Web.SessionState; + using DotNetOpenAuth.ApplicationBlock; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2; using RelyingPartyLogic; @@ -19,7 +20,7 @@ namespace MvcRelyingParty { /// <summary> /// An OAuth 2.0 token endpoint. /// </summary> - public class OAuthTokenEndpoint : IHttpAsyncHandler, IRequiresSessionState { + public class OAuthTokenEndpoint : HttpAsyncHandlerBase, IRequiresSessionState { /// <summary> /// Initializes a new instance of the <see cref="OAuthTokenEndpoint"/> class. /// </summary> @@ -32,27 +33,11 @@ namespace MvcRelyingParty { /// <returns> /// true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false. /// </returns> - public bool IsReusable { + public override bool IsReusable { get { return true; } } - /// <summary> - /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface. - /// </summary> - /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> - public void ProcessRequest(HttpContext context) { - this.ProcessRequestAsync(context).GetAwaiter().GetResult(); - } - - public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { - return this.ProcessRequestAsync(context).ToApm(cb, extraData); - } - - public void EndProcessRequest(IAsyncResult result) { - ((Task)result).Wait(); // rethrows exceptions - } - - private async Task ProcessRequestAsync(HttpContext context) { + protected override async Task ProcessRequestAsync(HttpContext context) { var serviceProvider = OAuthServiceProvider.AuthorizationServer; var response = await serviceProvider.HandleTokenRequestAsync(new HttpRequestWrapper(context.Request), context.Response.ClientDisconnectedToken); await response.SendAsync(new HttpResponseWrapper(context.Response), context.Response.ClientDisconnectedToken); diff --git a/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx.cs b/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx.cs index 7f53a7b..bada3e4 100644 --- a/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx.cs +++ b/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx.cs @@ -11,16 +11,16 @@ namespace WebFormsRelyingParty { using System.Threading.Tasks; using System.Web; using System.Web.SessionState; + using DotNetOpenAuth.ApplicationBlock; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2; using RelyingPartyLogic; - using WebFormsRelyingParty.Code; /// <summary> /// An OAuth 2.0 token endpoint. /// </summary> - public class OAuthTokenEndpoint : IHttpAsyncHandler, IRequiresSessionState { + public class OAuthTokenEndpoint : HttpAsyncHandlerBase, IRequiresSessionState { /// <summary> /// Initializes a new instance of the <see cref="OAuthTokenEndpoint"/> class. /// </summary> @@ -33,27 +33,11 @@ namespace WebFormsRelyingParty { /// <returns> /// true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false. /// </returns> - public bool IsReusable { + public override bool IsReusable { get { return true; } } - /// <summary> - /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface. - /// </summary> - /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> - public void ProcessRequest(HttpContext context) { - this.ProcessRequestAsync(context).GetAwaiter().GetResult(); - } - - public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { - return this.ProcessRequestAsync(context).ToApm(cb, extraData); - } - - public void EndProcessRequest(IAsyncResult result) { - ((Task)result).Wait(); // rethrows exceptions - } - - private async Task ProcessRequestAsync(HttpContext context) { + protected override async Task ProcessRequestAsync(HttpContext context) { var serviceProvider = OAuthServiceProvider.AuthorizationServer; var response = await serviceProvider.HandleTokenRequestAsync(new HttpRequestWrapper(context.Request), context.Response.ClientDisconnectedToken); await response.SendAsync(new HttpResponseWrapper(context.Response), context.Response.ClientDisconnectedToken); diff --git a/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj b/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj index 7de91cc..11384e2 100644 --- a/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj +++ b/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj @@ -252,6 +252,10 @@ <Content Include="PrivacyPolicy.aspx" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\..\samples\DotNetOpenAuth.ApplicationBlock\DotNetOpenAuth.ApplicationBlock.csproj"> + <Project>{aa78d112-d889-414b-a7d4-467b34c7b663}</Project> + <Name>DotNetOpenAuth.ApplicationBlock</Name> + </ProjectReference> <ProjectReference Include="..\..\src\DotNetOpenAuth.InfoCard.UI\DotNetOpenAuth.InfoCard.UI.csproj"> <Project>{E040EB58-B4D2-457B-A023-AE6EF3BD34DE}</Project> <Name>DotNetOpenAuth.InfoCard.UI</Name> diff --git a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj index 3169116..9f74693 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj +++ b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj @@ -99,6 +99,7 @@ <Compile Include="Facebook\FacebookGraph.cs" /> <Compile Include="CustomExtensions\UIRequestAtRelyingPartyFactory.cs" /> <Compile Include="GoogleConsumer.cs" /> + <Compile Include="HttpAsyncHandlerBase.cs" /> <Compile Include="InMemoryClientAuthorizationTracker.cs" /> <Compile Include="InMemoryTokenManager.cs"> <SubType>Code</SubType> diff --git a/samples/DotNetOpenAuth.ApplicationBlock/HttpAsyncHandlerBase.cs b/samples/DotNetOpenAuth.ApplicationBlock/HttpAsyncHandlerBase.cs new file mode 100644 index 0000000..a72a9b1 --- /dev/null +++ b/samples/DotNetOpenAuth.ApplicationBlock/HttpAsyncHandlerBase.cs @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------- +// <copyright file="HttpAsyncHandlerBase.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.ApplicationBlock { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using System.Web; + + public abstract class HttpAsyncHandlerBase : IHttpAsyncHandler { + public abstract bool IsReusable { get; } + + public IAsyncResult BeginProcessRequest(HttpContext context, System.AsyncCallback cb, object extraData) { + return ToApm(this.ProcessRequestAsync(context), cb, extraData); + } + + public void EndProcessRequest(IAsyncResult result) { + ((Task)result).Wait(); // rethrows exceptions + } + + public void ProcessRequest(HttpContext context) { + this.ProcessRequestAsync(context).GetAwaiter().GetResult(); + } + + protected abstract Task ProcessRequestAsync(HttpContext context); + + private static Task ToApm(Task task, AsyncCallback callback, object state) { + if (task == null) { + throw new ArgumentNullException("task"); + } + + var tcs = new TaskCompletionSource<object>(state); + task.ContinueWith( + t => { + if (t.IsFaulted) { + tcs.TrySetException(t.Exception.InnerExceptions); + } else if (t.IsCanceled) { + tcs.TrySetCanceled(); + } else { + tcs.TrySetResult(null); + } + + if (callback != null) { + callback(tcs.Task); + } + }, + CancellationToken.None, + TaskContinuationOptions.None, + TaskScheduler.Default); + + return tcs.Task; + } + } +} diff --git a/samples/OAuthClient/OAuthClient.csproj b/samples/OAuthClient/OAuthClient.csproj index 4ff85d3..61b3a79 100644 --- a/samples/OAuthClient/OAuthClient.csproj +++ b/samples/OAuthClient/OAuthClient.csproj @@ -97,7 +97,6 @@ <Content Include="SampleWcf2.aspx" /> <Content Include="SignInWithTwitter.aspx" /> <Content Include="TracePage.aspx" /> - <Content Include="Twitter.aspx" /> <Content Include="Web.config" /> <None Include="Service References\SampleResourceServer\DataApi1.xsd"> <SubType>Designer</SubType> @@ -156,10 +155,6 @@ <Compile Include="TracePage.aspx.designer.cs"> <DependentUpon>TracePage.aspx</DependentUpon> </Compile> - <Compile Include="Twitter.aspx.cs"> - <DependentUpon>Twitter.aspx</DependentUpon> - <SubType>ASPXCodeBehind</SubType> - </Compile> <Compile Include="Code\Logging.cs" /> <Compile Include="Code\TracePageAppender.cs" /> <Compile Include="GoogleAddressBook.aspx.cs"> @@ -167,9 +162,6 @@ <SubType>ASPXCodeBehind</SubType> </Compile> <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="Twitter.aspx.designer.cs"> - <DependentUpon>Twitter.aspx</DependentUpon> - </Compile> <Compile Include="WindowsLive.aspx.cs"> <DependentUpon>WindowsLive.aspx</DependentUpon> <SubType>ASPXCodeBehind</SubType> diff --git a/samples/OAuthClient/Twitter.aspx b/samples/OAuthClient/Twitter.aspx deleted file mode 100644 index cb60851..0000000 --- a/samples/OAuthClient/Twitter.aspx +++ /dev/null @@ -1,35 +0,0 @@ -<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" Inherits="OAuthClient.Twitter" Codebehind="Twitter.aspx.cs" %> - -<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server"> -</asp:Content> -<asp:Content ID="Content2" ContentPlaceHolderID="Body" runat="Server"> - <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 runat="server"> - <h2>Updates</h2> - <p>Ok, Twitter has authorized us to download your feeds. Notice how we never asked - you for your Twitter username or password. </p> - <p> - Upload a new profile photo: - <asp:FileUpload ID="profilePhoto" runat="server" /> - <asp:Button ID="uploadProfilePhotoButton" runat="server" - onclick="uploadProfilePhotoButton_Click" Text="Upload photo" /> - <asp:Label ID="photoUploadedLabel" runat="server" EnableViewState="False" - Text="Done!" Visible="False"></asp:Label> - </p> - <p> - Click 'Get updates' to download updates to this sample. - </p> - <asp:Button ID="downloadUpdates" runat="server" Text="Get updates" OnClick="downloadUpdates_Click" /> - <asp:PlaceHolder runat="server" ID="resultsPlaceholder" /> - </asp:View> - </asp:MultiView> -</asp:Content> diff --git a/samples/OAuthClient/Twitter.aspx.cs b/samples/OAuthClient/Twitter.aspx.cs deleted file mode 100644 index 2ef5ab8..0000000 --- a/samples/OAuthClient/Twitter.aspx.cs +++ /dev/null @@ -1,101 +0,0 @@ -namespace OAuthClient { - using System; - using System.Collections.Generic; - using System.Configuration; - using System.Linq; - using System.Text; - using System.Web; - using System.Web.UI; - using System.Web.UI.WebControls; - using System.Xml.Linq; - using System.Xml.XPath; - using DotNetOpenAuth.ApplicationBlock; - using DotNetOpenAuth.OAuth; - - using DotNetOpenAuth.Messaging; - - public partial class Twitter : System.Web.UI.Page { - private string AccessToken { - get { return (string)Session["TwitterAccessToken"]; } - set { Session["TwitterAccessToken"] = value; } - } - - private InMemoryTokenManager TokenManager { - get { - var tokenManager = (InMemoryTokenManager)Application["TwitterTokenManager"]; - if (tokenManager == null) { - string consumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"]; - string consumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"]; - if (!string.IsNullOrEmpty(consumerKey)) { - tokenManager = new InMemoryTokenManager(consumerKey, consumerSecret); - Application["TwitterTokenManager"] = tokenManager; - } - } - - return tokenManager; - } - } - - protected async void Page_Load(object sender, EventArgs e) { - if (this.TokenManager != null) { - this.MultiView1.ActiveViewIndex = 1; - - if (!IsPostBack) { - var twitter = new WebConsumer(TwitterConsumer.ServiceDescription, this.TokenManager); - - // Is Twitter calling back with authorization? - var accessTokenResponse = await twitter.ProcessUserAuthorizationAsync(new HttpRequestWrapper(Request), Response.ClientDisconnectedToken); - if (accessTokenResponse != null) { - this.AccessToken = accessTokenResponse.AccessToken; - } else if (this.AccessToken == null) { - // If we don't yet have access, immediately request it. - var request = await twitter.PrepareRequestUserAuthorizationAsync(Response.ClientDisconnectedToken); - var response = await twitter.Channel.PrepareResponseAsync(request, Response.ClientDisconnectedToken); - await response.SendAsync(new HttpResponseWrapper(Response), Response.ClientDisconnectedToken); - } - } - } - } - - protected async void downloadUpdates_Click(object sender, EventArgs e) { - var twitter = new WebConsumer(TwitterConsumer.ServiceDescription, this.TokenManager); - XPathDocument updates = new XPathDocument((await TwitterConsumer.GetUpdatesAsync(twitter, this.AccessToken, Response.ClientDisconnectedToken)).CreateReader()); - XPathNavigator nav = updates.CreateNavigator(); - var parsedUpdates = from status in nav.Select("/statuses/status").OfType<XPathNavigator>() - where !status.SelectSingleNode("user/protected").ValueAsBoolean - select new { - User = status.SelectSingleNode("user/name").InnerXml, - Status = status.SelectSingleNode("text").InnerXml, - }; - - StringBuilder tableBuilder = new StringBuilder(); - tableBuilder.Append("<table><tr><td>Name</td><td>Update</td></tr>"); - - foreach (var update in parsedUpdates) { - tableBuilder.AppendFormat( - "<tr><td>{0}</td><td>{1}</td></tr>", - HttpUtility.HtmlEncode(update.User), - HttpUtility.HtmlEncode(update.Status)); - } - tableBuilder.Append("</table>"); - this.resultsPlaceholder.Controls.Add(new Literal { Text = tableBuilder.ToString() }); - } - - protected async void uploadProfilePhotoButton_Click(object sender, EventArgs e) { - if (this.profilePhoto.PostedFile.ContentType == null) { - this.photoUploadedLabel.Visible = true; - this.photoUploadedLabel.Text = "Select a file first."; - return; - } - - var twitter = new WebConsumer(TwitterConsumer.ServiceDescription, this.TokenManager); - XDocument imageResult = await TwitterConsumer.UpdateProfileImageAsync( - twitter, - this.AccessToken, - this.profilePhoto.PostedFile.InputStream, - this.profilePhoto.PostedFile.ContentType, - Response.ClientDisconnectedToken); - this.photoUploadedLabel.Visible = true; - } - } -}
\ No newline at end of file diff --git a/samples/OAuthClient/Twitter.aspx.designer.cs b/samples/OAuthClient/Twitter.aspx.designer.cs deleted file mode 100644 index e82f477..0000000 --- a/samples/OAuthClient/Twitter.aspx.designer.cs +++ /dev/null @@ -1,78 +0,0 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// </auto-generated> -//------------------------------------------------------------------------------ - -namespace OAuthClient { - - - public partial class Twitter { - - /// <summary> - /// MultiView1 control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.MultiView MultiView1; - - /// <summary> - /// View1 control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.View View1; - - /// <summary> - /// profilePhoto control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.FileUpload profilePhoto; - - /// <summary> - /// uploadProfilePhotoButton control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.Button uploadProfilePhotoButton; - - /// <summary> - /// photoUploadedLabel control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.Label photoUploadedLabel; - - /// <summary> - /// downloadUpdates control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.Button downloadUpdates; - - /// <summary> - /// resultsPlaceholder control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.PlaceHolder resultsPlaceholder; - } -} diff --git a/samples/OAuthServiceProvider/Code/DatabaseTokenManager.cs b/samples/OAuthServiceProvider/Code/DatabaseTokenManager.cs index 49da45d..7c80275 100644 --- a/samples/OAuthServiceProvider/Code/DatabaseTokenManager.cs +++ b/samples/OAuthServiceProvider/Code/DatabaseTokenManager.cs @@ -9,13 +9,18 @@ namespace OAuthServiceProvider.Code { using System.Collections.Generic; using System.Diagnostics; using System.Linq; + using System.ServiceModel; + using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; public class DatabaseTokenManager : IServiceProviderTokenManager { + internal OperationContext OperationContext { get; set; } + #region IServiceProviderTokenManager public IConsumerDescription GetConsumer(string consumerKey) { + this.ApplyOperationContext(); var consumerRow = Global.DataContext.OAuthConsumers.SingleOrDefault( consumerCandidate => consumerCandidate.ConsumerKey == consumerKey); if (consumerRow == null) { @@ -27,6 +32,7 @@ namespace OAuthServiceProvider.Code { public IServiceProviderRequestToken GetRequestToken(string token) { try { + this.ApplyOperationContext(); return Global.DataContext.OAuthTokens.First(t => t.Token == token && t.State != TokenAuthorizationState.AccessToken); } catch (InvalidOperationException ex) { throw new KeyNotFoundException("Unrecognized token", ex); @@ -34,6 +40,7 @@ namespace OAuthServiceProvider.Code { } public IServiceProviderAccessToken GetAccessToken(string token) { + this.ApplyOperationContext(); try { return Global.DataContext.OAuthTokens.First(t => t.Token == token && t.State == TokenAuthorizationState.AccessToken); } catch (InvalidOperationException ex) { @@ -54,6 +61,7 @@ namespace OAuthServiceProvider.Code { #region ITokenManager Members public string GetTokenSecret(string token) { + this.ApplyOperationContext(); var tokenRow = Global.DataContext.OAuthTokens.SingleOrDefault( tokenCandidate => tokenCandidate.Token == token); if (tokenRow == null) { @@ -64,6 +72,7 @@ namespace OAuthServiceProvider.Code { } public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) { + this.ApplyOperationContext(); RequestScopedTokenMessage scopedRequest = (RequestScopedTokenMessage)request; var consumer = Global.DataContext.OAuthConsumers.Single(consumerRow => consumerRow.ConsumerKey == request.ConsumerKey); string scope = scopedRequest.Scope; @@ -90,6 +99,7 @@ namespace OAuthServiceProvider.Code { /// been authorized, has expired or does not exist. /// </returns> public bool IsRequestTokenAuthorized(string requestToken) { + this.ApplyOperationContext(); var tokenFound = Global.DataContext.OAuthTokens.SingleOrDefault( token => token.Token == requestToken && token.State == TokenAuthorizationState.AuthorizedRequestToken); @@ -97,6 +107,8 @@ namespace OAuthServiceProvider.Code { } public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) { + this.ApplyOperationContext(); + var data = Global.DataContext; var consumerRow = data.OAuthConsumers.Single(consumer => consumer.ConsumerKey == consumerKey); var tokenRow = data.OAuthTokens.Single(token => token.Token == requestToken && token.OAuthConsumer == consumerRow); @@ -115,6 +127,7 @@ namespace OAuthServiceProvider.Code { /// <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) { + this.ApplyOperationContext(); var tokenRow = Global.DataContext.OAuthTokens.SingleOrDefault(tokenCandidate => tokenCandidate.Token == token); if (tokenRow == null) { return TokenType.InvalidToken; @@ -135,6 +148,7 @@ namespace OAuthServiceProvider.Code { throw new ArgumentNullException("user"); } + this.ApplyOperationContext(); var tokenRow = Global.DataContext.OAuthTokens.SingleOrDefault( tokenCandidate => tokenCandidate.Token == requestToken && tokenCandidate.State == TokenAuthorizationState.UnauthorizedRequestToken); @@ -151,6 +165,7 @@ namespace OAuthServiceProvider.Code { throw new ArgumentNullException("requestToken"); } + this.ApplyOperationContext(); var tokenRow = Global.DataContext.OAuthTokens.SingleOrDefault( tokenCandidate => tokenCandidate.Token == token); if (tokenRow == null) { @@ -159,5 +174,11 @@ namespace OAuthServiceProvider.Code { return tokenRow.OAuthConsumer; } + + private void ApplyOperationContext() { + if (this.OperationContext != null && OperationContext.Current == null) { + OperationContext.Current = this.OperationContext; + } + } } }
\ No newline at end of file diff --git a/samples/OAuthServiceProvider/Code/Global.cs b/samples/OAuthServiceProvider/Code/Global.cs index 60fed9f..37206ab 100644 --- a/samples/OAuthServiceProvider/Code/Global.cs +++ b/samples/OAuthServiceProvider/Code/Global.cs @@ -10,6 +10,10 @@ /// The web application global events and properties. /// </summary> public class Global : HttpApplication { + private readonly object syncObject = new object(); + + private volatile bool initialized; + /// <summary> /// An application memory cache of recent log messages. /// </summary> @@ -95,17 +99,6 @@ private void Application_Start(object sender, EventArgs e) { log4net.Config.XmlConfigurator.Configure(); Logger.Info("Sample starting..."); - string appPath = HttpContext.Current.Request.ApplicationPath; - if (!appPath.EndsWith("/")) { - appPath += "/"; - } - - // This will break in IIS Integrated Pipeline mode, since applications - // start before the first incoming request context is available. - // TODO: fix this. - Constants.WebRootUrl = new Uri(HttpContext.Current.Request.Url, appPath); - Global.TokenManager = new DatabaseTokenManager(); - Global.NonceStore = new DatabaseNonceStore(); } private void Application_End(object sender, EventArgs e) { @@ -128,8 +121,30 @@ } } + private void Application_BeginRequest(object sender, EventArgs e) { + this.EnsureInitialized(); + } + private void Application_EndRequest(object sender, EventArgs e) { CommitAndCloseDatabaseIfNecessary(); } + + private void EnsureInitialized() { + if (!this.initialized) { + lock (this.syncObject) { + if (!this.initialized) { + string appPath = HttpContext.Current.Request.ApplicationPath; + if (!appPath.EndsWith("/")) { + appPath += "/"; + } + + Constants.WebRootUrl = new Uri(HttpContext.Current.Request.Url, appPath); + Global.TokenManager = new DatabaseTokenManager(); + Global.NonceStore = new DatabaseNonceStore(); + this.initialized = true; + } + } + } + } } }
\ No newline at end of file diff --git a/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs b/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs index cf28c15..2d942b5 100644 --- a/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs +++ b/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs @@ -26,6 +26,7 @@ HttpRequestMessageProperty httpDetails = operationContext.RequestContext.RequestMessage.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty; Uri requestUri = operationContext.RequestContext.RequestMessage.Properties.Via; ServiceProvider sp = Constants.CreateServiceProvider(); + ((DatabaseTokenManager)sp.TokenManager).OperationContext = operationContext; // artificially preserve this across thread changes. return Task.Run( async delegate { try { diff --git a/samples/OAuthServiceProvider/Login.aspx b/samples/OAuthServiceProvider/Login.aspx index 4498ee0..0b84ab9 100644 --- a/samples/OAuthServiceProvider/Login.aspx +++ b/samples/OAuthServiceProvider/Login.aspx @@ -1,4 +1,4 @@ -<%@ Page Title="Login" Language="C#" MasterPageFile="~/MasterPage.master" %> +<%@ Page Title="Login" Language="C#" MasterPageFile="~/MasterPage.master" Async="true" %> <%@ Register Assembly="DotNetOpenAuth.OpenId.RelyingParty.UI" Namespace="DotNetOpenAuth.OpenId.RelyingParty" TagPrefix="rp" %> diff --git a/samples/OAuthServiceProvider/OAuth.ashx b/samples/OAuthServiceProvider/OAuth.ashx index 8a74926..7b3dc75 100644 --- a/samples/OAuthServiceProvider/OAuth.ashx +++ b/samples/OAuthServiceProvider/OAuth.ashx @@ -2,41 +2,45 @@ using System; using System.Linq; +using System.Threading.Tasks; using System.Web; using System.Web.SessionState; +using DotNetOpenAuth.ApplicationBlock; using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; using DotNetOpenAuth.Messaging; using OAuthServiceProvider.Code; -public class OAuth : IHttpHandler, IRequiresSessionState { +public class OAuth : HttpAsyncHandlerBase, IRequiresSessionState { ServiceProvider sp; public OAuth() { sp = new ServiceProvider(Constants.SelfDescription, Global.TokenManager, new CustomOAuthMessageFactory(Global.TokenManager)); } - public void ProcessRequest(HttpContext context) { - IProtocolMessage request = sp.ReadRequest(); + public override bool IsReusable { + get { return true; } + } + + protected override async Task ProcessRequestAsync(HttpContext context) { + IProtocolMessage request = await sp.ReadRequestAsync(); RequestScopedTokenMessage requestToken; UserAuthorizationRequest requestAuth; AuthorizedTokenRequest requestAccessToken; if ((requestToken = request as RequestScopedTokenMessage) != null) { var response = sp.PrepareUnauthorizedTokenMessage(requestToken); - sp.Channel.Send(response); + var responseMessage = await sp.Channel.PrepareResponseAsync(response); + await responseMessage.SendAsync(new HttpResponseWrapper(context.Response)); } else if ((requestAuth = request as UserAuthorizationRequest) != null) { Global.PendingOAuthAuthorization = requestAuth; HttpContext.Current.Response.Redirect("~/Members/Authorize.aspx"); } else if ((requestAccessToken = request as AuthorizedTokenRequest) != null) { var response = sp.PrepareAccessTokenMessage(requestAccessToken); - sp.Channel.Send(response); + var responseMessage = await sp.Channel.PrepareResponseAsync(response); + await responseMessage.SendAsync(new HttpResponseWrapper(context.Response)); } else { throw new InvalidOperationException(); } } - - public bool IsReusable { - get { return true; } - } } diff --git a/samples/OAuthServiceProvider/OAuthServiceProvider.csproj b/samples/OAuthServiceProvider/OAuthServiceProvider.csproj index 2be5873..65d5a1f 100644 --- a/samples/OAuthServiceProvider/OAuthServiceProvider.csproj +++ b/samples/OAuthServiceProvider/OAuthServiceProvider.csproj @@ -26,7 +26,7 @@ <RootNamespace>OAuthServiceProvider</RootNamespace> <AssemblyName>OAuthServiceProvider</AssemblyName> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> - <UseIISExpress>false</UseIISExpress> + <UseIISExpress>true</UseIISExpress> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -190,6 +190,10 @@ <Project>{3896A32A-E876-4C23-B9B8-78E17D134CD3}</Project> <Name>DotNetOpenAuth.OpenId</Name> </ProjectReference> + <ProjectReference Include="..\DotNetOpenAuth.ApplicationBlock\DotNetOpenAuth.ApplicationBlock.csproj"> + <Project>{aa78d112-d889-414b-a7d4-467b34c7b663}</Project> + <Name>DotNetOpenAuth.ApplicationBlock</Name> + </ProjectReference> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" /> @@ -198,12 +202,11 @@ <VisualStudio> <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}"> <WebProjectProperties> - <UseIIS>False</UseIIS> + <UseIIS>True</UseIIS> <AutoAssignPort>False</AutoAssignPort> <DevelopmentServerPort>65169</DevelopmentServerPort> <DevelopmentServerVPath>/</DevelopmentServerVPath> - <IISUrl> - </IISUrl> + <IISUrl>http://localhost:65169/</IISUrl> <NTLMAuthentication>False</NTLMAuthentication> <UseCustomServer>False</UseCustomServer> <CustomServerUrl> diff --git a/samples/OAuthServiceProvider/Web.config b/samples/OAuthServiceProvider/Web.config index 51e9ca9..84aea1e 100644 --- a/samples/OAuthServiceProvider/Web.config +++ b/samples/OAuthServiceProvider/Web.config @@ -58,7 +58,8 @@ --> <compilation debug="true" targetFramework="4.0"> <assemblies> - <add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/> + <add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" /> + <add assembly="System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> </assemblies> </compilation> <authentication mode="Forms"> diff --git a/samples/OpenIdProviderWebForms/Provider.ashx.cs b/samples/OpenIdProviderWebForms/Provider.ashx.cs index 8022aae..d5d747b 100644 --- a/samples/OpenIdProviderWebForms/Provider.ashx.cs +++ b/samples/OpenIdProviderWebForms/Provider.ashx.cs @@ -4,9 +4,9 @@ using System.Threading.Tasks; using System.Web; using System.Web.SessionState; + using DotNetOpenAuth.ApplicationBlock; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Provider; - using OpenIdProviderWebForms.Code; /// <summary> @@ -18,12 +18,12 @@ /// control to reduce the amount of source code in the web site. A typical Provider /// site will have EITHER this .ashx handler OR the .aspx page -- NOT both. /// </remarks> - public class Provider : IHttpAsyncHandler, IRequiresSessionState { - public bool IsReusable { + public class Provider : HttpAsyncHandlerBase, IRequiresSessionState { + public override bool IsReusable { get { return true; } } - private async Task ProcessRequestAsync(HttpContext context) { + protected override async Task ProcessRequestAsync(HttpContext context) { IRequest request = await ProviderEndpoint.Provider.GetRequestAsync(new HttpRequestWrapper(context.Request), context.Response.ClientDisconnectedToken); if (request != null) { // Some OpenID requests are automatable and can be responded to immediately. @@ -66,17 +66,5 @@ } } } - - public IAsyncResult BeginProcessRequest(HttpContext context, System.AsyncCallback cb, object extraData) { - return this.ProcessRequestAsync(context).ToApm(cb, extraData); - } - - public void EndProcessRequest(IAsyncResult result) { - ((Task)result).Wait(); // rethrows exceptions - } - - public void ProcessRequest(HttpContext context) { - this.ProcessRequestAsync(context).GetAwaiter().GetResult(); - } } } diff --git a/samples/OpenIdProviderWebForms/access_token.ashx.cs b/samples/OpenIdProviderWebForms/access_token.ashx.cs index afee82c..8dccc3f 100644 --- a/samples/OpenIdProviderWebForms/access_token.ashx.cs +++ b/samples/OpenIdProviderWebForms/access_token.ashx.cs @@ -2,22 +2,23 @@ using System; using System.Collections.Generic; using System.Linq; + using System.Threading; using System.Threading.Tasks; using System.Web; using System.Web.Services; - using DotNetOpenAuth.OAuth; + using DotNetOpenAuth.ApplicationBlock; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth; using OpenIdProviderWebForms.Code; - using System.Threading; [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] - public class access_token : IHttpAsyncHandler { - public bool IsReusable { + public class access_token : HttpAsyncHandlerBase { + public override bool IsReusable { get { return true; } } - public async Task ProcessRequestAsync(HttpContext context) { + protected override async Task ProcessRequestAsync(HttpContext context) { var request = await OAuthHybrid.ServiceProvider.ReadAccessTokenRequestAsync( new HttpRequestWrapper(context.Request), context.Response.ClientDisconnectedToken); @@ -27,17 +28,5 @@ context.Response.ClientDisconnectedToken); await httpResponseMessage.SendAsync(); } - - public void ProcessRequest(HttpContext context) { - this.ProcessRequestAsync(context).GetAwaiter().GetResult(); - } - - public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { - return this.ProcessRequestAsync(context).ToApm(cb, extraData); - } - - public void EndProcessRequest(IAsyncResult result) { - ((Task)result).Wait(); // rethrows exceptions - } } } |