diff options
59 files changed, 493 insertions, 360 deletions
diff --git a/projecttemplates/MvcRelyingParty/Code/OpenIdRelyingPartyService.cs b/projecttemplates/MvcRelyingParty/Code/OpenIdRelyingPartyService.cs index d684116..0506286 100644 --- a/projecttemplates/MvcRelyingParty/Code/OpenIdRelyingPartyService.cs +++ b/projecttemplates/MvcRelyingParty/Code/OpenIdRelyingPartyService.cs @@ -23,9 +23,7 @@ Task<string> PreloadDiscoveryResultsAsync(Realm realm, Uri returnTo, Uri privacyPolicy, CancellationToken cancellationToken = default(CancellationToken), params Identifier[] identifiers); - Task<ActionResult> ProcessAjaxOpenIdResponseAsync(CancellationToken cancellationToken = default(CancellationToken)); - - Task<IAuthenticationResponse> GetResponseAsync(CancellationToken cancellationToken = default(CancellationToken)); + Task<ActionResult> ProcessAjaxOpenIdResponseAsync(HttpRequestBase request, CancellationToken cancellationToken = default(CancellationToken)); Task<IAuthenticationResponse> GetResponseAsync(HttpRequestBase request, CancellationToken cancellationToken = default(CancellationToken)); } @@ -96,12 +94,8 @@ return await relyingParty.AsAjaxPreloadedDiscoveryResultAsync(results, cancellationToken); } - public async Task<ActionResult> ProcessAjaxOpenIdResponseAsync(CancellationToken cancellationToken = default(CancellationToken)) { - return (await relyingParty.ProcessResponseFromPopupAsync(cancellationToken)).AsActionResult(); - } - - public Task<IAuthenticationResponse> GetResponseAsync(CancellationToken cancellationToken = default(CancellationToken)) { - return relyingParty.GetResponseAsync(cancellationToken); + public async Task<ActionResult> ProcessAjaxOpenIdResponseAsync(HttpRequestBase request, CancellationToken cancellationToken = default(CancellationToken)) { + return (await relyingParty.ProcessResponseFromPopupAsync(request, cancellationToken)).AsActionResult(); } public Task<IAuthenticationResponse> GetResponseAsync(HttpRequestBase request, CancellationToken cancellationToken = default(CancellationToken)) { diff --git a/projecttemplates/MvcRelyingParty/Controllers/AccountController.cs b/projecttemplates/MvcRelyingParty/Controllers/AccountController.cs index e95c22a..bf45838 100644 --- a/projecttemplates/MvcRelyingParty/Controllers/AccountController.cs +++ b/projecttemplates/MvcRelyingParty/Controllers/AccountController.cs @@ -76,7 +76,7 @@ } [Authorize, AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken] - public async Task<ActionResult> AuthorizeResponseAsync(bool isApproved) { + public async Task<ActionResult> AuthorizeResponse(bool isApproved) { var pendingRequest = await OAuthServiceProvider.AuthorizationServer.ReadAuthorizationRequestAsync(Request, Response.ClientDisconnectedToken); if (pendingRequest == null) { throw new HttpException((int)HttpStatusCode.BadRequest, "Missing authorization request."); diff --git a/projecttemplates/MvcRelyingParty/Controllers/AuthController.cs b/projecttemplates/MvcRelyingParty/Controllers/AuthController.cs index 35c39ea..926c254 100644 --- a/projecttemplates/MvcRelyingParty/Controllers/AuthController.cs +++ b/projecttemplates/MvcRelyingParty/Controllers/AuthController.cs @@ -106,7 +106,7 @@ namespace MvcRelyingParty.Controllers { /// </remarks> [AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post), ValidateInput(false)] public Task<ActionResult> PopUpReturnTo() { - return this.RelyingParty.ProcessAjaxOpenIdResponseAsync(Response.ClientDisconnectedToken); + return this.RelyingParty.ProcessAjaxOpenIdResponseAsync(this.Request, this.Response.ClientDisconnectedToken); } /// <summary> @@ -128,7 +128,7 @@ namespace MvcRelyingParty.Controllers { HttpRequestBase clientResponseInfo = HttpRequestInfo.Create("GET", auth, headers: Request.Headers); response = await this.RelyingParty.GetResponseAsync(clientResponseInfo, Response.ClientDisconnectedToken); } else { - response = await this.RelyingParty.GetResponseAsync(Response.ClientDisconnectedToken); + response = await this.RelyingParty.GetResponseAsync(Request, Response.ClientDisconnectedToken); } if (response != null) { switch (response.Status) { diff --git a/projecttemplates/MvcRelyingParty/OAuthTokenEndpoint.ashx.cs b/projecttemplates/MvcRelyingParty/OAuthTokenEndpoint.ashx.cs index f9a98f7..190c1f8 100644 --- a/projecttemplates/MvcRelyingParty/OAuthTokenEndpoint.ashx.cs +++ b/projecttemplates/MvcRelyingParty/OAuthTokenEndpoint.ashx.cs @@ -40,7 +40,7 @@ namespace MvcRelyingParty { 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); + await response.SendAsync(new HttpContextWrapper(context), context.Response.ClientDisconnectedToken); } } } diff --git a/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.cs b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.cs index b0b29a7..188a207 100644 --- a/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.cs +++ b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.cs @@ -39,7 +39,7 @@ namespace WebFormsRelyingParty.Members { if (((OAuthAuthorizationServer)OAuthServiceProvider.AuthorizationServer.AuthorizationServerServices).CanBeAutoApproved(this.pendingRequest)) { var response = OAuthServiceProvider.AuthorizationServer.PrepareApproveAuthorizationRequest(this.pendingRequest, HttpContext.Current.User.Identity.Name); var responseMessage = await OAuthServiceProvider.AuthorizationServer.Channel.PrepareResponseAsync(response, Response.ClientDisconnectedToken); - await responseMessage.SendAsync(new HttpResponseWrapper(Response), Response.ClientDisconnectedToken); + await responseMessage.SendAsync(new HttpContextWrapper(this.Context), Response.ClientDisconnectedToken); } this.ViewState["AuthRequest"] = this.pendingRequest; } else { @@ -59,13 +59,13 @@ namespace WebFormsRelyingParty.Members { }); var response = OAuthServiceProvider.AuthorizationServer.PrepareApproveAuthorizationRequest(this.pendingRequest, HttpContext.Current.User.Identity.Name); var responseMessage = await OAuthServiceProvider.AuthorizationServer.Channel.PrepareResponseAsync(response, Response.ClientDisconnectedToken); - await responseMessage.SendAsync(new HttpResponseWrapper(Response), Response.ClientDisconnectedToken); + await responseMessage.SendAsync(new HttpContextWrapper(this.Context), Response.ClientDisconnectedToken); } protected async void noButton_Click(object sender, EventArgs e) { var response = OAuthServiceProvider.AuthorizationServer.PrepareRejectAuthorizationRequest(this.pendingRequest); var responseMessage = await OAuthServiceProvider.AuthorizationServer.Channel.PrepareResponseAsync(response, Response.ClientDisconnectedToken); - await responseMessage.SendAsync(new HttpResponseWrapper(Response), Response.ClientDisconnectedToken); + await responseMessage.SendAsync(new HttpContextWrapper(this.Context), Response.ClientDisconnectedToken); } } } diff --git a/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx.cs b/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx.cs index bada3e4..2c84fc6 100644 --- a/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx.cs +++ b/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx.cs @@ -40,7 +40,7 @@ namespace WebFormsRelyingParty { 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); + await response.SendAsync(new HttpContextWrapper(context), context.Response.ClientDisconnectedToken); } } } diff --git a/samples/OAuthClient/Facebook.aspx.cs b/samples/OAuthClient/Facebook.aspx.cs index c265ce2..d174a8f 100644 --- a/samples/OAuthClient/Facebook.aspx.cs +++ b/samples/OAuthClient/Facebook.aspx.cs @@ -19,7 +19,7 @@ if (authorization == null) { // Kick off authorization request var request = await client.PrepareRequestUserAuthorizationAsync(cancellationToken: Response.ClientDisconnectedToken); - await request.SendAsync(new HttpResponseWrapper(Response), Response.ClientDisconnectedToken); + await request.SendAsync(new HttpContextWrapper(Context), Response.ClientDisconnectedToken); } else { var request = WebRequest.Create("https://graph.facebook.com/me?access_token=" + Uri.EscapeDataString(authorization.AccessToken)); using (var response = request.GetResponse()) { diff --git a/samples/OAuthClient/WindowsLive.aspx.cs b/samples/OAuthClient/WindowsLive.aspx.cs index b9e094a..cd7f5b4 100644 --- a/samples/OAuthClient/WindowsLive.aspx.cs +++ b/samples/OAuthClient/WindowsLive.aspx.cs @@ -30,7 +30,7 @@ if (authorization == null) { // Kick off authorization request var request = await client.PrepareRequestUserAuthorizationAsync(scopes: new[] { WindowsLiveClient.Scopes.Basic }); // this scope isn't even required just to log in - await request.SendAsync(new HttpResponseWrapper(Response), Response.ClientDisconnectedToken); + await request.SendAsync(new HttpContextWrapper(this.Context), Response.ClientDisconnectedToken); } else { var request = WebRequest.Create("https://apis.live.net/v5.0/me?access_token=" + Uri.EscapeDataString(authorization.AccessToken)); diff --git a/samples/OAuthConsumer/GoogleApps2Legged.aspx.cs b/samples/OAuthConsumer/GoogleApps2Legged.aspx.cs index cff24ea..d9a886e 100644 --- a/samples/OAuthConsumer/GoogleApps2Legged.aspx.cs +++ b/samples/OAuthConsumer/GoogleApps2Legged.aspx.cs @@ -19,5 +19,9 @@ await httpClient.GetAsync("http://someUri", Response.ClientDisconnectedToken); } } + + protected void getAddressBookButton_Click(object sender, EventArgs e) { + throw new NotImplementedException(); + } } }
\ No newline at end of file diff --git a/samples/OAuthConsumer/SignInWithTwitter.aspx b/samples/OAuthConsumer/SignInWithTwitter.aspx index e5ccc97..b5c6ad6 100644 --- a/samples/OAuthConsumer/SignInWithTwitter.aspx +++ b/samples/OAuthConsumer/SignInWithTwitter.aspx @@ -33,6 +33,7 @@ </asp:Panel> </asp:View> </asp:MultiView> + </div> </form> </body> </html> diff --git a/samples/OAuthResourceServer/OAuthResourceServer.csproj b/samples/OAuthResourceServer/OAuthResourceServer.csproj index db40ea3..4afe367 100644 --- a/samples/OAuthResourceServer/OAuthResourceServer.csproj +++ b/samples/OAuthResourceServer/OAuthResourceServer.csproj @@ -132,6 +132,18 @@ <Project>{56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}</Project> <Name>DotNetOpenAuth.OAuth2</Name> </ProjectReference> + <ProjectReference Include="..\..\src\DotNetOpenAuth.OpenId.RelyingParty.UI\DotNetOpenAuth.OpenId.RelyingParty.UI.csproj"> + <Project>{1ed8d424-f8ab-4050-aceb-f27f4f909484}</Project> + <Name>DotNetOpenAuth.OpenId.RelyingParty.UI</Name> + </ProjectReference> + <ProjectReference Include="..\..\src\DotNetOpenAuth.OpenId.RelyingParty\DotNetOpenAuth.OpenId.RelyingParty.csproj"> + <Project>{f458ab60-ba1c-43d9-8cef-ec01b50be87b}</Project> + <Name>DotNetOpenAuth.OpenId.RelyingParty</Name> + </ProjectReference> + <ProjectReference Include="..\..\src\DotNetOpenAuth.OpenId\DotNetOpenAuth.OpenId.csproj"> + <Project>{3896a32a-e876-4c23-b9b8-78e17d134cd3}</Project> + <Name>DotNetOpenAuth.OpenId</Name> + </ProjectReference> </ItemGroup> <ItemGroup> <Content Include="packages.config" /> diff --git a/samples/OAuthServiceProvider/OAuth.ashx b/samples/OAuthServiceProvider/OAuth.ashx index 7b3dc75..003735c 100644 --- a/samples/OAuthServiceProvider/OAuth.ashx +++ b/samples/OAuthServiceProvider/OAuth.ashx @@ -31,14 +31,14 @@ public class OAuth : HttpAsyncHandlerBase, IRequiresSessionState { if ((requestToken = request as RequestScopedTokenMessage) != null) { var response = sp.PrepareUnauthorizedTokenMessage(requestToken); var responseMessage = await sp.Channel.PrepareResponseAsync(response); - await responseMessage.SendAsync(new HttpResponseWrapper(context.Response)); + await responseMessage.SendAsync(new HttpContextWrapper(context)); } 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); var responseMessage = await sp.Channel.PrepareResponseAsync(response); - await responseMessage.SendAsync(new HttpResponseWrapper(context.Response)); + await responseMessage.SendAsync(new HttpContextWrapper(context)); } else { throw new InvalidOperationException(); } diff --git a/samples/OpenIdProviderWebForms/Code/Util.cs b/samples/OpenIdProviderWebForms/Code/Util.cs index 552a80a..5333124 100644 --- a/samples/OpenIdProviderWebForms/Code/Util.cs +++ b/samples/OpenIdProviderWebForms/Code/Util.cs @@ -54,7 +54,7 @@ namespace OpenIdProviderWebForms.Code { // add extension responses here. } } else { - HttpContext.Current.Response.Redirect("~/decide.aspx", true); + HttpContext.Current.Response.Redirect("~/decide.aspx", false); } } @@ -70,7 +70,7 @@ namespace OpenIdProviderWebForms.Code { // These would typically be filled in from a user database } } else { - HttpContext.Current.Response.Redirect("~/decide.aspx", true); + HttpContext.Current.Response.Redirect("~/decide.aspx", false); } } diff --git a/samples/OpenIdProviderWebForms/OpenIdProviderWebForms.csproj b/samples/OpenIdProviderWebForms/OpenIdProviderWebForms.csproj index d756726..c95ee15 100644 --- a/samples/OpenIdProviderWebForms/OpenIdProviderWebForms.csproj +++ b/samples/OpenIdProviderWebForms/OpenIdProviderWebForms.csproj @@ -100,6 +100,7 @@ <SubType>Designer</SubType> </Content> <Content Include="user_xrds.aspx" /> + <Content Include="WebForm1.aspx" /> </ItemGroup> <ItemGroup> <Compile Include="access_token.ashx.cs"> @@ -175,6 +176,13 @@ <Compile Include="user.aspx.designer.cs"> <DependentUpon>user.aspx</DependentUpon> </Compile> + <Compile Include="WebForm1.aspx.cs"> + <DependentUpon>WebForm1.aspx</DependentUpon> + <SubType>ASPXCodeBehind</SubType> + </Compile> + <Compile Include="WebForm1.aspx.designer.cs"> + <DependentUpon>WebForm1.aspx</DependentUpon> + </Compile> </ItemGroup> <ItemGroup> <Content Include="favicon.ico" /> @@ -251,7 +259,7 @@ <VisualStudio> <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}"> <WebProjectProperties> - <UseIIS>True</UseIIS> + <UseIIS>False</UseIIS> <AutoAssignPort>False</AutoAssignPort> <DevelopmentServerPort>4860</DevelopmentServerPort> <DevelopmentServerVPath>/</DevelopmentServerVPath> diff --git a/samples/OpenIdProviderWebForms/ProfileFields.ascx.cs b/samples/OpenIdProviderWebForms/ProfileFields.ascx.cs index 6954aa6..e27f794 100644 --- a/samples/OpenIdProviderWebForms/ProfileFields.ascx.cs +++ b/samples/OpenIdProviderWebForms/ProfileFields.ascx.cs @@ -25,15 +25,15 @@ namespace OpenIdProviderWebForms { public DateTime? DateOfBirth { get { - try { - int day = Convert.ToInt32(this.dobDayDropdownlist.SelectedValue); - int month = Convert.ToInt32(this.dobMonthDropdownlist.SelectedValue); - int year = Convert.ToInt32(this.dobYearDropdownlist.SelectedValue); - DateTime newDate = new DateTime(year, month, day); + int day, month, year; + if (int.TryParse(this.dobDayDropdownlist.SelectedValue, out day) + && int.TryParse(this.dobMonthDropdownlist.SelectedValue, out month) + && int.TryParse(this.dobYearDropdownlist.SelectedValue, out year)) { + var newDate = new DateTime(year, month, day); return newDate; - } catch (Exception) { - return null; } + + return null; } set { diff --git a/samples/OpenIdProviderWebForms/Provider.ashx.cs b/samples/OpenIdProviderWebForms/Provider.ashx.cs index d5d747b..7022d80 100644 --- a/samples/OpenIdProviderWebForms/Provider.ashx.cs +++ b/samples/OpenIdProviderWebForms/Provider.ashx.cs @@ -24,7 +24,8 @@ } protected override async Task ProcessRequestAsync(HttpContext context) { - IRequest request = await ProviderEndpoint.Provider.GetRequestAsync(new HttpRequestWrapper(context.Request), context.Response.ClientDisconnectedToken); + var providerEndpoint = new ProviderEndpoint(); + 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. // But authentication requests cannot be responded to until something on @@ -57,12 +58,12 @@ // We DON'T use ProviderEndpoint.SendResponse because // that only sends responses to requests in PendingAuthenticationRequest, // but we don't set that for associate and other non-checkid requests. - var response = await ProviderEndpoint.Provider.PrepareResponseAsync(request, context.Response.ClientDisconnectedToken); + var response = await providerEndpoint.Provider.PrepareResponseAsync(request, context.Response.ClientDisconnectedToken); // Make sure that any PendingAuthenticationRequest that MAY be set is cleared. ProviderEndpoint.PendingRequest = null; - await response.SendAsync(new HttpResponseWrapper(context.Response)); + await response.SendAsync(new HttpContextWrapper(context)); } } } diff --git a/samples/OpenIdProviderWebForms/Web.config b/samples/OpenIdProviderWebForms/Web.config index d9e005d..c028df1 100644 --- a/samples/OpenIdProviderWebForms/Web.config +++ b/samples/OpenIdProviderWebForms/Web.config @@ -89,7 +89,7 @@ Medium: doesn't work unless originUrl=".*" or WebPermission.Connect is extended, and Google Apps doesn't work. Low: doesn't work because WebPermission.Connect is denied. --> - <trust level="Medium" originUrl=".*"/> + <trust level="Full" originUrl=".*"/> <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/> </system.web> <location path="decide.aspx"> diff --git a/samples/OpenIdProviderWebForms/WebForm1.aspx b/samples/OpenIdProviderWebForms/WebForm1.aspx new file mode 100644 index 0000000..8639049 --- /dev/null +++ b/samples/OpenIdProviderWebForms/WebForm1.aspx @@ -0,0 +1,16 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="OpenIdProviderWebForms.WebForm1" Async="true" %> + +<!DOCTYPE html> + +<html xmlns="http://www.w3.org/1999/xhtml"> +<head runat="server"> + <title></title> +</head> +<body> + <form id="form1" runat="server"> + <div> + + </div> + </form> +</body> +</html> diff --git a/samples/OpenIdProviderWebForms/WebForm1.aspx.cs b/samples/OpenIdProviderWebForms/WebForm1.aspx.cs new file mode 100644 index 0000000..6ec73e6 --- /dev/null +++ b/samples/OpenIdProviderWebForms/WebForm1.aspx.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; + +namespace OpenIdProviderWebForms { + public partial class WebForm1 : System.Web.UI.Page { + protected async void Page_Load(object sender, EventArgs e) { + object oldValue = Session["Hi"]; + Session["Hi"] = new object(); + } + } +}
\ No newline at end of file diff --git a/samples/OpenIdProviderWebForms/WebForm1.aspx.designer.cs b/samples/OpenIdProviderWebForms/WebForm1.aspx.designer.cs new file mode 100644 index 0000000..0e75ef7 --- /dev/null +++ b/samples/OpenIdProviderWebForms/WebForm1.aspx.designer.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// <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 OpenIdProviderWebForms { + + + public partial class WebForm1 { + + /// <summary> + /// form1 control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.HtmlControls.HtmlForm form1; + } +} diff --git a/samples/OpenIdProviderWebForms/decide.aspx b/samples/OpenIdProviderWebForms/decide.aspx index b6219ff..ddae8e7 100644 --- a/samples/OpenIdProviderWebForms/decide.aspx +++ b/samples/OpenIdProviderWebForms/decide.aspx @@ -1,5 +1,5 @@ <%@ Page Language="C#" AutoEventWireup="true" Inherits="OpenIdProviderWebForms.decide" - CodeBehind="decide.aspx.cs" MasterPageFile="~/Site.Master" Async="true" %> + CodeBehind="decide.aspx.cs" MasterPageFile="~/Site.Master" Async="true" EnableSessionState="true" %> <%@ Register Src="ProfileFields.ascx" TagName="ProfileFields" TagPrefix="uc1" %> <asp:Content runat="server" ContentPlaceHolderID="Main"> diff --git a/samples/OpenIdProviderWebForms/decide.aspx.cs b/samples/OpenIdProviderWebForms/decide.aspx.cs index 88f222f..d3ef6d2 100644 --- a/samples/OpenIdProviderWebForms/decide.aspx.cs +++ b/samples/OpenIdProviderWebForms/decide.aspx.cs @@ -1,6 +1,7 @@ namespace OpenIdProviderWebForms { using System; using System.Diagnostics; + using System.Net; using System.Web.Security; using System.Web.UI; using DotNetOpenAuth.Messaging; @@ -15,7 +16,11 @@ namespace OpenIdProviderWebForms { public partial class decide : Page { protected async void Page_Load(object src, EventArgs e) { if (ProviderEndpoint.PendingRequest == null) { - Response.Redirect("~/"); + // Response.Redirect(string) throws ThreadInterruptedException, and "async void Page_Load" doesn't properly catch it. + this.Response.RedirectLocation = "/"; + this.Response.StatusCode = (int)HttpStatusCode.Redirect; + this.Context.ApplicationInstance.CompleteRequest(); + return; } this.relyingPartyVerificationResultLabel.Text = @@ -63,7 +68,7 @@ namespace OpenIdProviderWebForms { } protected async void Yes_Click(object sender, EventArgs e) { - if (!Page.IsValid) { + if (!Page.IsValid || ProviderEndpoint.PendingRequest == null) { return; } @@ -99,18 +104,25 @@ namespace OpenIdProviderWebForms { ProviderEndpoint.PendingAnonymousRequest.IsApproved = true; } Debug.Assert(ProviderEndpoint.PendingRequest.IsResponseReady, "Setting authentication should be all that's necessary."); - var response = await ProviderEndpoint.PrepareResponseAsync(this.Response.ClientDisconnectedToken); + + var provider = new ProviderEndpoint(); + var response = await provider.PrepareResponseAsync(); await response.SendAsync(); } protected async void No_Click(object sender, EventArgs e) { + if (ProviderEndpoint.PendingRequest == null) { + return; + } + if (ProviderEndpoint.PendingAuthenticationRequest != null) { ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = false; } else { ProviderEndpoint.PendingAnonymousRequest.IsApproved = false; } Debug.Assert(ProviderEndpoint.PendingRequest.IsResponseReady, "Setting authentication should be all that's necessary."); - var response = await ProviderEndpoint.PrepareResponseAsync(this.Response.ClientDisconnectedToken); + var provider = new ProviderEndpoint(); + var response = await provider.PrepareResponseAsync(); await response.SendAsync(); } } diff --git a/samples/OpenIdProviderWebForms/server.aspx b/samples/OpenIdProviderWebForms/server.aspx index 101aeee..946f044 100644 --- a/samples/OpenIdProviderWebForms/server.aspx +++ b/samples/OpenIdProviderWebForms/server.aspx @@ -1,4 +1,4 @@ -<%@ Page Language="C#" AutoEventWireup="true" Inherits="OpenIdProviderWebForms.server" CodeBehind="server.aspx.cs" ValidateRequest="false" %> +<%@ Page Language="C#" AutoEventWireup="true" Inherits="OpenIdProviderWebForms.server" CodeBehind="server.aspx.cs" ValidateRequest="false" Async="true" EnableSessionState="true" %> <%@ Register Assembly="DotNetOpenAuth.OpenId.Provider.UI" Namespace="DotNetOpenAuth.OpenId.Provider" TagPrefix="openid" %> <html> diff --git a/samples/OpenIdProviderWebForms/server.aspx.cs b/samples/OpenIdProviderWebForms/server.aspx.cs index 89e14f4..e613192 100644 --- a/samples/OpenIdProviderWebForms/server.aspx.cs +++ b/samples/OpenIdProviderWebForms/server.aspx.cs @@ -7,15 +7,27 @@ namespace OpenIdProviderWebForms { /// This page is responsible for handling all open-id compliant requests. /// </summary> public partial class server : System.Web.UI.Page { - protected void Page_Load(object src, System.EventArgs evt) { + protected void Page_Load(object src, EventArgs evt) { this.serverEndpointUrl.Text = Request.Url.ToString(); } protected void provider_AuthenticationChallenge(object sender, AuthenticationChallengeEventArgs e) { + // We store the request in the user's session so that + // redirects and user prompts can appear and eventually some page can decide + // to respond to the OpenID authentication request either affirmatively or + // negatively. + ProviderEndpoint.PendingRequest = e.Request; + Code.Util.ProcessAuthenticationChallenge(e.Request); } protected void provider_AnonymousRequest(object sender, AnonymousRequestEventArgs e) { + // We store the request in the user's session so that + // redirects and user prompts can appear and eventually some page can decide + // to respond to the OpenID authentication request either affirmatively or + // negatively. + ProviderEndpoint.PendingRequest = e.Request; + Code.Util.ProcessAnonymousRequest(e.Request); } } diff --git a/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj b/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj index 7c6d03a..03675d2 100644 --- a/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj +++ b/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj @@ -72,6 +72,8 @@ <Reference Include="System.Data" /> <Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Drawing" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Net.Http.WebRequest" /> <Reference Include="System.Web" /> <Reference Include="System.Web.ApplicationServices" /> <Reference Include="System.Web.DynamicData" /> diff --git a/samples/OpenIdRelyingPartyWebForms/Web.config b/samples/OpenIdRelyingPartyWebForms/Web.config index 178687f..f5fada9 100644 --- a/samples/OpenIdRelyingPartyWebForms/Web.config +++ b/samples/OpenIdRelyingPartyWebForms/Web.config @@ -84,7 +84,7 @@ Medium: doesn't work unless originUrl=".*" or WebPermission.Connect is extended, and Google Apps doesn't work. Low: doesn't work because WebPermission.Connect is denied. --> - <trust level="Medium" originUrl=".*"/> + <trust level="Full" originUrl=".*"/> <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/> </system.web> diff --git a/samples/OpenIdRelyingPartyWebForms/packages.config b/samples/OpenIdRelyingPartyWebForms/packages.config index 6562527..8e40260 100644 --- a/samples/OpenIdRelyingPartyWebForms/packages.config +++ b/samples/OpenIdRelyingPartyWebForms/packages.config @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="log4net" version="2.0.0" targetFramework="net45" /> + <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx.vb b/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx.vb index 537fd96..b3c1620 100644 --- a/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx.vb +++ b/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx.vb @@ -54,7 +54,7 @@ Public Class LoginProgrammatic builder.Query = Nothing Me.Response.Redirect(builder.Uri.AbsoluteUri) End If - Dim response As IAuthenticationResponse = Await relyingParty.GetResponseAsync(CancellationToken.None) + Dim response As IAuthenticationResponse = Await relyingParty.GetResponseAsync(New HttpRequestWrapper(Request)) If response IsNot Nothing Then Select Case response.Status Case AuthenticationStatus.Authenticated diff --git a/samples/OpenIdWebRingSsoProvider/Code/Util.cs b/samples/OpenIdWebRingSsoProvider/Code/Util.cs index bd01638..13ecf41 100644 --- a/samples/OpenIdWebRingSsoProvider/Code/Util.cs +++ b/samples/OpenIdWebRingSsoProvider/Code/Util.cs @@ -54,7 +54,8 @@ namespace OpenIdWebRingSsoProvider.Code { internal static async Task ProcessAuthenticationChallengeAsync(IAuthenticationRequest idrequest, CancellationToken cancellationToken) { // Verify that RP discovery is successful. - if (await idrequest.IsReturnUrlDiscoverableAsync(ProviderEndpoint.Provider.Channel.HostFactories, cancellationToken) != RelyingPartyDiscoveryResult.Success) { + var providerEndpoint = new ProviderEndpoint(); + if (await idrequest.IsReturnUrlDiscoverableAsync(providerEndpoint.Provider.Channel.HostFactories, cancellationToken) != RelyingPartyDiscoveryResult.Success) { idrequest.IsAuthenticated = false; return; } diff --git a/samples/OpenIdWebRingSsoProvider/Default.aspx.cs b/samples/OpenIdWebRingSsoProvider/Default.aspx.cs index 9b32fe0..a659aeb 100644 --- a/samples/OpenIdWebRingSsoProvider/Default.aspx.cs +++ b/samples/OpenIdWebRingSsoProvider/Default.aspx.cs @@ -15,8 +15,9 @@ if (User.Identity.IsAuthenticated && ProviderEndpoint.PendingAuthenticationRequest != null) { await Util.ProcessAuthenticationChallengeAsync(ProviderEndpoint.PendingAuthenticationRequest, Response.ClientDisconnectedToken); if (ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated.HasValue) { - var responseMessage = await ProviderEndpoint.PrepareResponseAsync(this.Response.ClientDisconnectedToken); - await responseMessage.SendAsync(new HttpResponseWrapper(this.Response), this.Response.ClientDisconnectedToken); + var providerEndpoint = new ProviderEndpoint(); + var responseMessage = await providerEndpoint.PrepareResponseAsync(this.Response.ClientDisconnectedToken); + await responseMessage.SendAsync(new HttpContextWrapper(this.Context), this.Response.ClientDisconnectedToken); } } } diff --git a/samples/OpenIdWebRingSsoProvider/Login.aspx.cs b/samples/OpenIdWebRingSsoProvider/Login.aspx.cs index c85e490..43279c9 100644 --- a/samples/OpenIdWebRingSsoProvider/Login.aspx.cs +++ b/samples/OpenIdWebRingSsoProvider/Login.aspx.cs @@ -39,8 +39,9 @@ var req = ProviderEndpoint.PendingAuthenticationRequest; if (req != null) { req.IsAuthenticated = false; - var response = await ProviderEndpoint.PrepareResponseAsync(Response.ClientDisconnectedToken); - await response.SendAsync(new HttpResponseWrapper(Response), Response.ClientDisconnectedToken); + var providerEndpoint = new ProviderEndpoint(); + var response = await providerEndpoint.PrepareResponseAsync(Response.ClientDisconnectedToken); + await response.SendAsync(new HttpContextWrapper(this.Context), Response.ClientDisconnectedToken); } } } diff --git a/samples/OpenIdWebRingSsoRelyingParty/OpenIdWebRingSsoRelyingParty.csproj b/samples/OpenIdWebRingSsoRelyingParty/OpenIdWebRingSsoRelyingParty.csproj index a2618e7..9eaeb65 100644 --- a/samples/OpenIdWebRingSsoRelyingParty/OpenIdWebRingSsoRelyingParty.csproj +++ b/samples/OpenIdWebRingSsoRelyingParty/OpenIdWebRingSsoRelyingParty.csproj @@ -9,6 +9,7 @@ <IISExpressAnonymousAuthentication /> <IISExpressWindowsAuthentication /> <IISExpressUseClassicPipelineMode /> + <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\src\</SolutionDir> </PropertyGroup> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> @@ -53,6 +54,8 @@ <Reference Include="System.Data" /> <Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Drawing" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Net.Http.WebRequest" /> <Reference Include="System.Web" /> <Reference Include="System.Web.ApplicationServices" /> <Reference Include="System.Web.DynamicData" /> @@ -121,6 +124,9 @@ <Name>DotNetOpenAuth.OpenId</Name> </ProjectReference> </ItemGroup> + <ItemGroup> + <Content Include="packages.config" /> + </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" /> <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" /> @@ -150,4 +156,5 @@ </VisualStudio> </ProjectExtensions> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " /> + <Import Project="$(SolutionDir)\.nuget\nuget.targets" /> </Project>
\ No newline at end of file diff --git a/samples/OpenIdWebRingSsoRelyingParty/packages.config b/samples/OpenIdWebRingSsoRelyingParty/packages.config new file mode 100644 index 0000000..d8ffcb7 --- /dev/null +++ b/samples/OpenIdWebRingSsoRelyingParty/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" /> +</packages>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.Core/Messaging/Channel.cs b/src/DotNetOpenAuth.Core/Messaging/Channel.cs index e36bb94..8564e38 100644 --- a/src/DotNetOpenAuth.Core/Messaging/Channel.cs +++ b/src/DotNetOpenAuth.Core/Messaging/Channel.cs @@ -325,21 +325,6 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Gets the protocol message embedded in the given HTTP request, if present. /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns> - /// The deserialized message, if one is found. Null otherwise. - /// </returns> - /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current" /> is null.</exception> - /// <remarks> - /// Requires an HttpContext.Current context. - /// </remarks> - public Task<IDirectedProtocolMessage> ReadFromRequestAsync(CancellationToken cancellationToken) { - return this.ReadFromRequestAsync(this.GetRequestFromContext(), cancellationToken); - } - - /// <summary> - /// Gets the protocol message embedded in the given HTTP request, if present. - /// </summary> /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="httpRequest">The request to search for an embedded message.</param> @@ -348,9 +333,9 @@ namespace DotNetOpenAuth.Messaging { /// </returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current" /> is null.</exception> /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception> - public async Task<TRequest> TryReadFromRequestAsync<TRequest>(CancellationToken cancellationToken, HttpRequestBase httpRequest = null) + public async Task<TRequest> TryReadFromRequestAsync<TRequest>(HttpRequestMessage httpRequest, CancellationToken cancellationToken) where TRequest : class, IProtocolMessage { - httpRequest = httpRequest ?? this.GetRequestFromContext(); + Requires.NotNull(httpRequest, "httpRequest"); IProtocolMessage untypedRequest = await this.ReadFromRequestAsync(httpRequest, cancellationToken); if (untypedRequest == null) { @@ -373,10 +358,11 @@ namespace DotNetOpenAuth.Messaging { /// </returns> /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception> [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")] - public async Task<TRequest> ReadFromRequestAsync<TRequest>(CancellationToken cancellationToken, HttpRequestBase httpRequest = null) + public async Task<TRequest> ReadFromRequestAsync<TRequest>(HttpRequestMessage httpRequest, CancellationToken cancellationToken) where TRequest : class, IProtocolMessage { - httpRequest = httpRequest ?? this.GetRequestFromContext(); - TRequest request = await this.TryReadFromRequestAsync<TRequest>(cancellationToken, httpRequest); + Requires.NotNull(httpRequest, "httpRequest"); + + TRequest request = await this.TryReadFromRequestAsync<TRequest>(httpRequest, cancellationToken); ErrorUtilities.VerifyProtocol(request != null, MessagingStrings.ExpectedMessageNotReceived, typeof(TRequest)); return request; } @@ -389,20 +375,20 @@ namespace DotNetOpenAuth.Messaging { /// <returns> /// The deserialized message, if one is found. Null otherwise. /// </returns> - public async Task<IDirectedProtocolMessage> ReadFromRequestAsync(HttpRequestBase httpRequest, CancellationToken cancellationToken) { + public async Task<IDirectedProtocolMessage> ReadFromRequestAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken) { Requires.NotNull(httpRequest, "httpRequest"); - if (Logger.Channel.IsInfoEnabled && httpRequest.GetPublicFacingUrl() != null) { - Logger.Channel.InfoFormat("Scanning incoming request for messages: {0}", httpRequest.GetPublicFacingUrl().AbsoluteUri); + if (Logger.Channel.IsInfoEnabled && httpRequest.RequestUri != null) { + Logger.Channel.InfoFormat("Scanning incoming request for messages: {0}", httpRequest.RequestUri.AbsoluteUri); } - IDirectedProtocolMessage requestMessage = this.ReadFromRequestCore(httpRequest, cancellationToken); + IDirectedProtocolMessage requestMessage = await this.ReadFromRequestCoreAsync(httpRequest, cancellationToken); if (requestMessage != null) { Logger.Channel.DebugFormat("Incoming request received: {0}", requestMessage.GetType().Name); var directRequest = requestMessage as IHttpDirectRequest; if (directRequest != null) { - foreach (string header in httpRequest.Headers) { - directRequest.Headers[header] = httpRequest.Headers[header]; + foreach (var header in httpRequest.Headers) { + directRequest.Headers.Add(header.Key, header.Value); } } @@ -539,6 +525,21 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Parses the URL encoded form content. + /// </summary> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>A sequence of key=value pairs found in the request's entity; or an empty sequence if none are found.</returns> + protected internal static async Task<IEnumerable<KeyValuePair<string, string>>> ParseUrlEncodedFormContentAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + if (request.Content != null && request.Content.Headers.ContentType != null + && request.Content.Headers.ContentType.MediaType.Equals(HttpFormUrlEncoded)) { + return HttpUtility.ParseQueryString(await request.Content.ReadAsStringAsync()).AsKeyValuePairs(); + } + + return Enumerable.Empty<KeyValuePair<string, string>>(); + } + + /// <summary> /// Gets the HTTP context for the current HTTP request. /// </summary> /// <returns>An HttpContextBase instance.</returns> @@ -671,8 +672,8 @@ namespace DotNetOpenAuth.Messaging { var webRequest = this.CreateHttpRequest(request); var directRequest = request as IHttpDirectRequest; if (directRequest != null) { - foreach (string header in directRequest.Headers) { - webRequest.Headers.Add(header, directRequest.Headers[header]); + foreach (var header in directRequest.Headers) { + webRequest.Headers.Add(header.Key, header.Value); } } @@ -741,16 +742,18 @@ namespace DotNetOpenAuth.Messaging { /// <param name="request">The request to search for an embedded message.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The deserialized message, if one is found. Null otherwise.</returns> - protected virtual IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request, CancellationToken cancellationToken) { + protected virtual async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Requires.NotNull(request, "request"); - Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.GetPublicFacingUrl().AbsoluteUri); + Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.Method, request.RequestUri.AbsoluteUri); + + var fields = new Dictionary<string, string>(); // Search Form data first, and if nothing is there search the QueryString - Assumes.True(request.Form != null && request.GetQueryStringBeforeRewriting() != null); - var fields = request.Form.ToDictionary(); - if (fields.Count == 0 && request.HttpMethod != "POST") { // OpenID 2.0 section 4.1.2 - fields = request.GetQueryStringBeforeRewriting().ToDictionary(); + fields.AddRange(await ParseUrlEncodedFormContentAsync(request, cancellationToken)); + + if (fields.Count == 0 && request.Method.Method != "POST") { // OpenID 2.0 section 4.1.2 + fields.AddRange(HttpUtility.ParseQueryString(request.RequestUri.Query).AsKeyValuePairs()); } MessageReceivingEndpoint recipient; diff --git a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs index 7b26869..226102d 100644 --- a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs +++ b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs @@ -15,6 +15,6 @@ namespace DotNetOpenAuth.Messaging { /// Gets the HTTP headers of the request. /// </summary> /// <value>May be an empty collection, but must not be <c>null</c>.</value> - WebHeaderCollection Headers { get; } + System.Net.Http.Headers.HttpRequestHeaders Headers { get; } } } diff --git a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs index 38abe4a..25a56f4 100644 --- a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs @@ -360,7 +360,15 @@ namespace DotNetOpenAuth.Messaging { // HttpRequest.Url gives us the internal URL in a cloud environment, // So we use a variable that (at least from what I can tell) gives us // the public URL: - if (serverVariables["HTTP_HOST"] != null) { + string httpHost; + try { + httpHost = serverVariables["HTTP_HOST"]; + } catch (NullReferenceException) { + // The VS dev web server can throw this. :( + httpHost = null; + } + + if (httpHost != null) { ErrorUtilities.VerifySupported(request.Url.Scheme == Uri.UriSchemeHttps || request.Url.Scheme == Uri.UriSchemeHttp, "Only HTTP and HTTPS are supported protocols."); string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ?? request.Url.Scheme; Uri hostAndPort = new Uri(scheme + Uri.SchemeDelimiter + serverVariables["HTTP_HOST"]); @@ -412,21 +420,45 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Wraps an instance of <see cref="HttpRequestBase"/> as an <see cref="HttpRequestMessage"/> instance. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>An instance of <see cref="HttpRequestMessage"/></returns> + public static HttpRequestMessage AsHttpRequestMessage(this HttpRequestBase request) { + Requires.NotNull(request, "request"); + + Uri publicFacingUrl = request.GetPublicFacingUrl(); + var httpRequest = new HttpRequestMessage(new HttpMethod(request.HttpMethod), publicFacingUrl); + + if (request.Form != null) { + // Avoid a request message that will try to read the request stream twice for already parsed data. + httpRequest.Content = new FormUrlEncodedContent(request.Form.AsKeyValuePairs()); + } else if (request.InputStream != null) { + httpRequest.Content = new StreamContent(request.InputStream); + } + + request.ApplyHeaders(httpRequest); + + return httpRequest; + } + + /// <summary> /// Sends a response message to the HTTP client. /// </summary> /// <param name="response">The response message.</param> - /// <param name="responseContext">The response context to send the response with.</param> + /// <param name="context">The HTTP context to send the response with.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// A task that completes with the asynchronous operation. /// </returns> - public static async Task SendAsync(this HttpResponseMessage response, HttpResponseBase responseContext = null, CancellationToken cancellationToken = default(CancellationToken)) { + public static async Task SendAsync(this HttpResponseMessage response, HttpContextBase context = null, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(response, "response"); - if (responseContext == null) { + if (context == null) { ErrorUtilities.VerifyHttpContext(); - responseContext = new HttpResponseWrapper(HttpContext.Current.Response); + context = new HttpContextWrapper(HttpContext.Current); } + var responseContext = context.Response; responseContext.StatusCode = (int)response.StatusCode; responseContext.StatusDescription = response.ReasonPhrase; foreach (var header in response.Headers) { @@ -438,16 +470,11 @@ namespace DotNetOpenAuth.Messaging { if (response.Content != null) { await response.Content.CopyToAsync(responseContext.OutputStream).ConfigureAwait(false); } - } - /// <summary> - /// Sends a response message to the HTTP client. - /// </summary> - /// <param name="response">The response message.</param> - /// <param name="responseContext">The response context to send the response with.</param> - /// <param name="cancellationToken">The cancellation token.</param> - public static void Send(this HttpResponseMessage response, HttpResponseBase responseContext = null, CancellationToken cancellationToken = default(CancellationToken)) { - SendAsync(response, responseContext, cancellationToken).GetAwaiter().GetResult(); + // This prevents a hosting ASP.NET web forms page from rendering HTML after a control + // has taken control of the response stream. + context.ApplicationInstance.CompleteRequest(); + context.Response.End(); } /// <summary> @@ -587,24 +614,15 @@ namespace DotNetOpenAuth.Messaging { /// <param name="scheme">The scheme. Must not be null or empty.</param> /// <param name="authorizationHeader">The authorization header. May be null or empty.</param> /// <returns>A sequence of key=value pairs discovered in the header. Never null, but may be empty.</returns> - internal static IEnumerable<KeyValuePair<string, string>> ParseAuthorizationHeader(string scheme, string authorizationHeader) { + internal static IEnumerable<KeyValuePair<string, string>> ParseAuthorizationHeader(string scheme, AuthenticationHeaderValue authorizationHeader) { Requires.NotNullOrEmpty(scheme, "scheme"); - string prefix = scheme + " "; - if (authorizationHeader != null) { - // The authorization header may have multiple sections. Look for the appropriate one. - string[] authorizationSections = new string[] { authorizationHeader }; // what is the right delimiter, if any? - foreach (string authorization in authorizationSections) { - string trimmedAuth = authorization.Trim(); - if (trimmedAuth.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { // RFC 2617 says this is case INsensitive - string data = trimmedAuth.Substring(prefix.Length); - return from element in data.Split(CommaArray) - let parts = element.Trim().Split(EqualsArray, 2) - let key = Uri.UnescapeDataString(parts[0]) - let value = Uri.UnescapeDataString(parts[1].Trim(QuoteArray)) - select new KeyValuePair<string, string>(key, value); - } - } + if (authorizationHeader != null && authorizationHeader.Scheme.Equals(scheme, StringComparison.OrdinalIgnoreCase)) { // RFC 2617 says this is case INsensitive + return from element in authorizationHeader.Parameter.Split(CommaArray) + let parts = element.Trim().Split(EqualsArray, 2) + let key = Uri.UnescapeDataString(parts[0]) + let value = Uri.UnescapeDataString(parts[1].Trim(QuoteArray)) + select new KeyValuePair<string, string>(key, value); } return Enumerable.Empty<KeyValuePair<string, string>>(); @@ -1184,80 +1202,45 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Clones an <see cref="HttpWebRequest"/> in order to send it again. - /// </summary> - /// <param name="request">The request to clone.</param> - /// <returns>The newly created instance.</returns> - internal static HttpWebRequest Clone(this HttpWebRequest request) { - Requires.NotNull(request, "request"); - Requires.That(request.RequestUri != null, "request", "request.RequestUri cannot be null."); - return Clone(request, request.RequestUri); - } - - /// <summary> - /// Clones an <see cref="HttpWebRequest"/> in order to send it again. + /// Clones an <see cref="HttpWebRequest" /> in order to send it again. /// </summary> - /// <param name="request">The request to clone.</param> - /// <param name="newRequestUri">The new recipient of the request.</param> - /// <returns>The newly created instance.</returns> - internal static HttpWebRequest Clone(this HttpWebRequest request, Uri newRequestUri) { + /// <param name="request">The request with headers to clone.</param> + /// <param name="message">The message to set headers on.</param> + internal static void ApplyHeaders(this HttpRequestBase request, HttpRequestMessage message) { Requires.NotNull(request, "request"); - Requires.NotNull(newRequestUri, "newRequestUri"); - - var newRequest = (HttpWebRequest)WebRequest.Create(newRequestUri); + Requires.NotNull(message, "message"); - // First copy headers. Only set those that are explicitly set on the original request, + // First copy headers. Only set those that are explicitly set on the original requestHeaders, // because some properties (like IfModifiedSince) activate special behavior when set, // even when set to their "original" values. foreach (string headerName in request.Headers) { switch (headerName) { - case "Accept": newRequest.Accept = request.Accept; break; + case "Accept": message.Headers.Accept.AddRange(request.AcceptTypes.Select(at => new MediaTypeWithQualityHeaderValue(at))); break; case "Connection": break; // Keep-Alive controls this - case "Content-Length": newRequest.ContentLength = request.ContentLength; break; - case "Content-Type": newRequest.ContentType = request.ContentType; break; - case "Expect": newRequest.Expect = request.Expect; break; + case "Content-Length": message.Content.Headers.ContentLength = request.ContentLength; break; + case "Content-Type": message.Content.Headers.ContentType = new MediaTypeHeaderValue(request.ContentType); break; + case "Expect": message.Headers.Expect.Add(new NameValueWithParametersHeaderValue(request.Headers[headerName])); break; case "Host": break; // implicitly copied as part of the RequestUri - case "If-Modified-Since": newRequest.IfModifiedSince = request.IfModifiedSince; break; - case "Keep-Alive": newRequest.KeepAlive = request.KeepAlive; break; - case "Proxy-Connection": break; // no property equivalent? - case "Referer": newRequest.Referer = request.Referer; break; - case "Transfer-Encoding": newRequest.TransferEncoding = request.TransferEncoding; break; - case "User-Agent": newRequest.UserAgent = request.UserAgent; break; - default: newRequest.Headers[headerName] = request.Headers[headerName]; break; - } - } - - newRequest.AllowAutoRedirect = request.AllowAutoRedirect; - newRequest.AllowWriteStreamBuffering = request.AllowWriteStreamBuffering; - newRequest.AuthenticationLevel = request.AuthenticationLevel; - newRequest.AutomaticDecompression = request.AutomaticDecompression; - newRequest.CachePolicy = request.CachePolicy; - newRequest.ClientCertificates = request.ClientCertificates; - newRequest.ConnectionGroupName = request.ConnectionGroupName; - newRequest.ContinueDelegate = request.ContinueDelegate; - newRequest.CookieContainer = request.CookieContainer; - newRequest.Credentials = request.Credentials; - newRequest.ImpersonationLevel = request.ImpersonationLevel; - newRequest.MaximumAutomaticRedirections = request.MaximumAutomaticRedirections; - newRequest.MaximumResponseHeadersLength = request.MaximumResponseHeadersLength; - newRequest.MediaType = request.MediaType; - newRequest.Method = request.Method; - newRequest.Pipelined = request.Pipelined; - newRequest.PreAuthenticate = request.PreAuthenticate; - newRequest.ProtocolVersion = request.ProtocolVersion; - newRequest.ReadWriteTimeout = request.ReadWriteTimeout; - newRequest.SendChunked = request.SendChunked; - newRequest.Timeout = request.Timeout; - newRequest.UseDefaultCredentials = request.UseDefaultCredentials; + case "If-Modified-Since": break; // we don't care + case "Keep-Alive": message.Headers.Connection.Add(request.Headers[headerName]); break; + case "Proxy-Connection": break; // we don't care + case "Referer": message.Headers.Referrer = request.UrlReferrer; break; + case "Transfer-Encoding": message.Headers.TransferEncoding.Add(new TransferCodingHeaderValue(request.Headers[headerName])); break; + case "User-Agent": message.Headers.UserAgent.Add(new ProductInfoHeaderValue(request.UserAgent)); break; + default: + HttpHeaders headers = headerName.StartsWith("Content-", StringComparison.Ordinal) && message.Content != null + ? (HttpHeaders)message.Content.Headers + : message.Headers; + var values = request.Headers.GetValues(headerName); + if (values.Length == 1) { + headers.Add(headerName, values[0]); + } else { + headers.Add(headerName, values); + } - try { - newRequest.Proxy = request.Proxy; - newRequest.UnsafeAuthenticatedConnectionSharing = request.UnsafeAuthenticatedConnectionSharing; - } catch (SecurityException) { - Logger.Messaging.Warn("Unable to clone some HttpWebRequest properties due to partial trust."); + break; + } } - - return newRequest; } /// <summary> @@ -1508,8 +1491,8 @@ namespace DotNetOpenAuth.Messaging { /// <param name="request">The request to get recipient information from.</param> /// <returns>The recipient.</returns> /// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception> - internal static MessageReceivingEndpoint GetRecipient(this HttpRequestBase request) { - return new MessageReceivingEndpoint(request.GetPublicFacingUrl(), GetHttpDeliveryMethod(request.HttpMethod)); + internal static MessageReceivingEndpoint GetRecipient(this HttpRequestMessage request) { + return new MessageReceivingEndpoint(request.RequestUri, GetHttpDeliveryMethod(request.Method.Method)); } /// <summary> @@ -2111,7 +2094,8 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="context">The context in which the result is executed. The context information includes the controller, HTTP content, request context, and route data.</param> public override void ExecuteResult(ControllerContext context) { - this.response.Send(context.HttpContext.Response); + // TODO: fix this to be asynchronous. + this.response.SendAsync(context.HttpContext).GetAwaiter().GetResult(); } } } diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs index 5e6cfb3..3f677b8 100644 --- a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs +++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs @@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OAuth { using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; + using System.Net.Http; using System.Security.Principal; using System.ServiceModel.Channels; using System.Threading; @@ -214,7 +215,23 @@ namespace DotNetOpenAuth.OAuth { /// Requires HttpContext.Current. /// </remarks> public Task<IDirectedProtocolMessage> ReadRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - return this.Channel.ReadFromRequestAsync(request ?? this.Channel.GetRequestFromContext(), cancellationToken); + return this.ReadRequestAsync((request ?? this.Channel.GetRequestFromContext()).AsHttpRequestMessage(), cancellationToken); + } + + /// <summary> + /// Reads any incoming OAuth message. + /// </summary> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The deserialized message. + /// </returns> + /// <remarks> + /// Requires HttpContext.Current. + /// </remarks> + public Task<IDirectedProtocolMessage> ReadRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); + return this.Channel.ReadFromRequestAsync(request, cancellationToken); } /// <summary> @@ -226,8 +243,24 @@ namespace DotNetOpenAuth.OAuth { /// The incoming request, or null if no OAuth message was attached. /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> - public async Task<UnauthorizedTokenRequest> ReadTokenRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - var message = await this.Channel.TryReadFromRequestAsync<UnauthorizedTokenRequest>(cancellationToken, request); + public Task<UnauthorizedTokenRequest> ReadTokenRequestAsync( + HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + return this.ReadTokenRequestAsync((request ?? this.channel.GetRequestFromContext()).AsHttpRequestMessage(), cancellationToken); + } + + /// <summary> + /// Reads a request for an unauthorized token from the incoming HTTP request. + /// </summary> + /// <param name="request">The HTTP request to read from.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The incoming request, or null if no OAuth message was attached. + /// </returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + public async Task<UnauthorizedTokenRequest> ReadTokenRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); + + var message = await this.Channel.TryReadFromRequestAsync<UnauthorizedTokenRequest>(request, cancellationToken); if (message != null) { ErrorUtilities.VerifyProtocol(message.Version >= Protocol.Lookup(this.SecuritySettings.MinimumRequiredOAuthVersion).Version, OAuthStrings.MinimumConsumerVersionRequirementNotMet, this.SecuritySettings.MinimumRequiredOAuthVersion, message.Version); } @@ -261,8 +294,9 @@ namespace DotNetOpenAuth.OAuth { /// The incoming request, or null if no OAuth message was attached. /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> - public Task<UserAuthorizationRequest> ReadAuthorizationRequestAsync(HttpRequestBase request, CancellationToken cancellationToken = default(CancellationToken)) { - return this.Channel.TryReadFromRequestAsync<UserAuthorizationRequest>(cancellationToken, request); + public Task<UserAuthorizationRequest> ReadAuthorizationRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + request = request ?? this.channel.GetRequestFromContext(); + return this.Channel.TryReadFromRequestAsync<UserAuthorizationRequest>(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> @@ -337,7 +371,8 @@ namespace DotNetOpenAuth.OAuth { /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> public Task<AuthorizedTokenRequest> ReadAccessTokenRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - return this.Channel.TryReadFromRequestAsync<AuthorizedTokenRequest>(cancellationToken, request); + request = request ?? this.Channel.GetRequestFromContext(); + return this.Channel.TryReadFromRequestAsync<AuthorizedTokenRequest>(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> @@ -391,9 +426,8 @@ namespace DotNetOpenAuth.OAuth { /// </remarks> /// <exception cref="ProtocolException">Thrown if an unexpected message is attached to the request.</exception> public async Task<AccessProtectedResourceRequest> ReadProtectedResourceAuthorizationAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - Requires.NotNull(request, "request"); - - var accessMessage = await this.Channel.TryReadFromRequestAsync<AccessProtectedResourceRequest>(cancellationToken, request); + request = request ?? this.Channel.GetRequestFromContext(); + var accessMessage = await this.Channel.TryReadFromRequestAsync<AccessProtectedResourceRequest>(request.AsHttpRequestMessage(), cancellationToken); if (accessMessage != null) { if (this.TokenManager.GetTokenType(accessMessage.AccessToken) != TokenType.AccessToken) { throw new ProtocolException( diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuthReporting.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuthReporting.cs index 8171e1a..5b00c1a 100644 --- a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuthReporting.cs +++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuthReporting.cs @@ -45,7 +45,7 @@ namespace DotNetOpenAuth { builder.Append(nonceStore.GetType().Name); } builder.Append(" "); - builder.Append(service.UserAuthorizationEndpoint.Location); + builder.Append(service.UserAuthorizationEndpoint != null ? service.UserAuthorizationEndpoint.Location.AbsoluteUri : string.Empty); Reporting.ObservedFeatures.Add(builder.ToString()); Reporting.Touch(); } diff --git a/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs index 708c9c9..0925402 100644 --- a/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -118,28 +118,23 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <param name="request">The HTTP request to search.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The deserialized message, if one is found. Null otherwise.</returns> - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request, CancellationToken cancellationToken) { + protected override async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // First search the Authorization header. - string authorization = request.Headers[HttpRequestHeaders.Authorization]; + var authorization = request.Headers.Authorization; var fields = MessagingUtilities.ParseAuthorizationHeader(Protocol.AuthorizationHeaderScheme, authorization).ToDictionary(); fields.Remove("realm"); // ignore the realm parameter, since we don't use it, and it must be omitted from signature base string. // Scrape the entity - if (!string.IsNullOrEmpty(request.Headers[HttpRequestHeaders.ContentType])) { - var contentType = new ContentType(request.Headers[HttpRequestHeaders.ContentType]); - if (string.Equals(contentType.MediaType, HttpFormUrlEncoded, StringComparison.Ordinal)) { - foreach (string key in request.Form) { - if (key != null) { - fields.Add(key, request.Form[key]); - } else { - Logger.OAuth.WarnFormat("Ignoring query string parameter '{0}' since it isn't a standard name=value parameter.", request.Form[key]); - } - } + foreach (var pair in await ParseUrlEncodedFormContentAsync(request, cancellationToken)) { + if (pair.Key != null) { + fields.Add(pair.Key, pair.Value); + } else { + Logger.OAuth.WarnFormat("Ignoring query string parameter '{0}' since it isn't a standard name=value parameter.", pair.Value); } } // Scrape the query string - var qs = request.GetQueryStringBeforeRewriting(); + var qs = HttpUtility.ParseQueryString(request.RequestUri.Query); foreach (string key in qs) { if (key != null) { fields.Add(key, qs[key]); @@ -162,8 +157,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { // Add receiving HTTP transport information required for signature generation. var signedMessage = message as ITamperResistantOAuthMessage; if (signedMessage != null) { - signedMessage.Recipient = request.GetPublicFacingUrl(); - signedMessage.HttpMethod = new HttpMethod(request.HttpMethod); + signedMessage.Recipient = request.RequestUri; + signedMessage.HttpMethod = request.Method; } return message; diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs index cd603eb..421a09e 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs @@ -93,11 +93,8 @@ namespace DotNetOpenAuth.OAuth2 { /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "unauthorizedclient", Justification = "Protocol required.")] public async Task<EndUserAuthorizationRequest> ReadAuthorizationRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - if (request == null) { - request = this.Channel.GetRequestFromContext(); - } - - var message = await this.Channel.TryReadFromRequestAsync<EndUserAuthorizationRequest>(cancellationToken, request); + request = request ?? this.Channel.GetRequestFromContext(); + var message = await this.Channel.TryReadFromRequestAsync<EndUserAuthorizationRequest>(request.AsHttpRequestMessage(), cancellationToken); if (message != null) { if (message.ResponseType == EndUserAuthorizationResponseType.AuthorizationCode) { // Clients with no secrets can only request implicit grant types. @@ -142,14 +139,12 @@ namespace DotNetOpenAuth.OAuth2 { /// The HTTP response to send to the client. /// </returns> public async Task<HttpResponseMessage> HandleTokenRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - if (request == null) { - request = this.Channel.GetRequestFromContext(); - } + request = request ?? this.Channel.GetRequestFromContext(); AccessTokenRequestBase requestMessage; IProtocolMessage responseMessage; try { - requestMessage = await this.Channel.TryReadFromRequestAsync<AccessTokenRequestBase>(cancellationToken, request); + requestMessage = await this.Channel.TryReadFromRequestAsync<AccessTokenRequestBase>(request.AsHttpRequestMessage(), cancellationToken); if (requestMessage != null) { var accessTokenResult = this.AuthorizationServerServices.CreateAccessToken(requestMessage); ErrorUtilities.VerifyHost(accessTokenResult != null, "IAuthorizationServerHost.CreateAccessToken must not return null."); diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs index 2a7f414..3fcbf67 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs @@ -96,9 +96,11 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// <returns> /// The deserialized message, if one is found. Null otherwise. /// </returns> - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request, CancellationToken cancellationToken) { - if (!string.IsNullOrEmpty(request.Url.Fragment)) { - var fields = HttpUtility.ParseQueryString(request.Url.Fragment.Substring(1)).ToDictionary(); + protected override async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + Requires.NotNull(request, "request"); + + if (!string.IsNullOrEmpty(request.RequestUri.Fragment)) { + var fields = HttpUtility.ParseQueryString(request.RequestUri.Fragment.Substring(1)).ToDictionary(); MessageReceivingEndpoint recipient; try { @@ -111,7 +113,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { return (IDirectedProtocolMessage)this.Receive(fields, recipient); } - return base.ReadFromRequestCore(request, cancellationToken); + return await base.ReadFromRequestCoreAsync(request, cancellationToken); } /// <summary> diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs index 65f3e1d..ae1a823 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs @@ -17,6 +17,8 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.Messages; + using Validation; + /// <summary> /// The messaging channel used by OAuth 2.0 Clients. /// </summary> @@ -112,18 +114,20 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// <returns> /// The deserialized message, if one is found. Null otherwise. /// </returns> - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request, CancellationToken cancellationToken) { - Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.GetPublicFacingUrl().AbsoluteUri); + protected override async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + Requires.NotNull(request, "request"); + + Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.Method, request.RequestUri.AbsoluteUri); - var fields = request.GetQueryStringBeforeRewriting().ToDictionary(); + var fields = HttpUtility.ParseQueryString(request.RequestUri.Query).ToDictionary(); // Also read parameters from the fragment, if it's available. // Typically the fragment is not available because the browser doesn't send it to a web server // but this request may have been fabricated by an installed desktop app, in which case // the fragment is available. - string fragment = request.Url.Fragment; + string fragment = request.RequestUri.Fragment; if (!string.IsNullOrEmpty(fragment)) { - foreach (var pair in HttpUtility.ParseQueryString(fragment.Substring(1)).ToDictionary()) { + foreach (var pair in HttpUtility.ParseQueryString(fragment.Substring(1)).AsKeyValuePairs()) { fields.Add(pair.Key, pair.Value); } } diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs index ffd896e..d56b308 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth2 { using System; using System.Collections.Generic; using System.Linq; + using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -127,7 +128,7 @@ namespace DotNetOpenAuth.OAuth2 { authorizationState = new AuthorizationState(); } - var carrier = new HttpRequestInfo("GET", actualRedirectUrl); + var carrier = new HttpRequestMessage(HttpMethod.Get, actualRedirectUrl); IDirectedProtocolMessage response = await this.Channel.ReadFromRequestAsync(carrier, cancellationToken); if (response == null) { return null; diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs index 0476521..c8eb69d 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs @@ -135,13 +135,10 @@ namespace DotNetOpenAuth.OAuth2 { RequiresEx.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), Strings.RequiredPropertyNotYetPreset, "ClientIdentifier"); RequiresEx.ValidState(this.ClientCredentialApplicator != null, Strings.RequiredPropertyNotYetPreset, "ClientCredentialApplicator"); - if (request == null) { - request = this.Channel.GetRequestFromContext(); - } - - var response = await this.Channel.TryReadFromRequestAsync<IMessageWithClientState>(cancellationToken, request); + request = request ?? this.Channel.GetRequestFromContext(); + var response = await this.Channel.TryReadFromRequestAsync<IMessageWithClientState>(request.AsHttpRequestMessage(), cancellationToken); if (response != null) { - Uri callback = MessagingUtilities.StripMessagePartsFromQueryString(request.GetPublicFacingUrl(), this.Channel.MessageDescriptions.Get(response)); + Uri callback = request.GetPublicFacingUrl().StripMessagePartsFromQueryString(this.Channel.MessageDescriptions.Get(response)); IAuthorizationState authorizationState; if (this.AuthorizationTracker != null) { authorizationState = this.AuthorizationTracker.GetAuthorizationState(callback, response.ClientState); diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj index bfbacff..d0f7bcf 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj @@ -79,6 +79,8 @@ <None Include="packages.config" /> </ItemGroup> <ItemGroup> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Net.Http.WebRequest" /> <Reference Include="Validation, Version=2.0.0.0, Culture=neutral, PublicKeyToken=2fc06f0d701809a7, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\Validation.2.0.2.13022\lib\portable-windows8+net40+sl5+windowsphone8\Validation.dll</HintPath> diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs index 96eecbb..cda9a1f 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs @@ -7,6 +7,8 @@ namespace DotNetOpenAuth.OAuth2.Messages { using System; using System.Net; + using System.Net.Http; + using DotNetOpenAuth.Messaging; /// <summary> @@ -16,7 +18,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// <summary> /// The backing for the <see cref="Headers"/> property. /// </summary> - private readonly WebHeaderCollection headers = new WebHeaderCollection(); + private readonly System.Net.Http.Headers.HttpRequestHeaders headers = new HttpRequestMessage().Headers; /// <summary> /// Initializes a new instance of the <see cref="AuthenticatedClientRequestBase"/> class. @@ -51,7 +53,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// Gets the HTTP headers of the request. /// </summary> /// <value>May be an empty collection, but must not be <c>null</c>.</value> - public WebHeaderCollection Headers { + public System.Net.Http.Headers.HttpRequestHeaders Headers { get { return this.headers; } } } diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/packages.config b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/packages.config index e3309bc..d32d62f 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/packages.config +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/packages.config @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> + <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" /> <package id="Validation" version="2.0.2.13022" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs index 363b8e0..c645753 100644 --- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs @@ -55,10 +55,12 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// <returns> /// The deserialized message, if one is found. Null otherwise. /// </returns> - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request, CancellationToken cancellationToken) { + protected override async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + Requires.NotNull(request, "request"); + var fields = new Dictionary<string, string>(); string accessToken; - if ((accessToken = SearchForBearerAccessTokenInRequest(request)) != null) { + if ((accessToken = await SearchForBearerAccessTokenInRequestAsync(request, cancellationToken)) != null) { fields[Protocol.token_type] = Protocol.AccessTokenTypes.Bearer; fields[Protocol.access_token] = accessToken; } @@ -129,27 +131,24 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// </summary> /// <param name="request">The request.</param> /// <returns>The bearer access token, if one exists. Otherwise <c>null</c>.</returns> - private static string SearchForBearerAccessTokenInRequest(HttpRequestBase request) { + private static async Task<string> SearchForBearerAccessTokenInRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Requires.NotNull(request, "request"); // First search the authorization header. - string authorizationHeader = request.Headers[HttpRequestHeaders.Authorization]; - if (!string.IsNullOrEmpty(authorizationHeader) && authorizationHeader.StartsWith(Protocol.BearerHttpAuthorizationSchemeWithTrailingSpace, StringComparison.OrdinalIgnoreCase)) { - return authorizationHeader.Substring(Protocol.BearerHttpAuthorizationSchemeWithTrailingSpace.Length); + var authorizationHeader = request.Headers.Authorization; + if (authorizationHeader != null && string.Equals(authorizationHeader.Scheme, Protocol.BearerHttpAuthorizationScheme, StringComparison.OrdinalIgnoreCase)) { + return authorizationHeader.Parameter; } // Failing that, scan the entity - if (!string.IsNullOrEmpty(request.Headers[HttpRequestHeaders.ContentType])) { - var contentType = new ContentType(request.Headers[HttpRequestHeaders.ContentType]); - if (string.Equals(contentType.MediaType, HttpFormUrlEncoded, StringComparison.Ordinal)) { - if (request.Form[Protocol.BearerTokenEncodedUrlParameterName] != null) { - return request.Form[Protocol.BearerTokenEncodedUrlParameterName]; - } + foreach (var pair in await ParseUrlEncodedFormContentAsync(request, cancellationToken)) { + if (string.Equals(pair.Key, Protocol.BearerTokenEncodedUrlParameterName, StringComparison.Ordinal)) { + return pair.Value; } } // Finally, check the least desirable location: the query string - var unrewrittenQuery = request.GetQueryStringBeforeRewriting(); + var unrewrittenQuery = HttpUtility.ParseQueryString(request.RequestUri.Query); if (!string.IsNullOrEmpty(unrewrittenQuery[Protocol.BearerTokenEncodedUrlParameterName])) { return unrewrittenQuery[Protocol.BearerTokenEncodedUrlParameterName]; } diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs index 88ce451..e990e0b 100644 --- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs @@ -88,17 +88,34 @@ namespace DotNetOpenAuth.OAuth2 { /// </returns> /// <exception cref="ProtocolFaultResponseException">Thrown when the client is not authorized. This exception should be caught and the /// <see cref="ProtocolFaultResponseException.ErrorResponseMessage" /> message should be returned to the client.</exception> - public virtual async Task<AccessToken> GetAccessTokenAsync(HttpRequestBase httpRequestInfo = null, CancellationToken cancellationToken = default(CancellationToken), params string[] requiredScopes) { + public virtual Task<AccessToken> GetAccessTokenAsync(HttpRequestBase httpRequestInfo = null, CancellationToken cancellationToken = default(CancellationToken), params string[] requiredScopes) { + Requires.NotNull(requiredScopes, "requiredScopes"); + RequiresEx.ValidState(this.ScopeSatisfiedCheck != null, Strings.RequiredPropertyNotYetPreset); + + httpRequestInfo = httpRequestInfo ?? this.Channel.GetRequestFromContext(); + return this.GetAccessTokenAsync(httpRequestInfo.AsHttpRequestMessage(), cancellationToken, requiredScopes); + } + + /// <summary> + /// Discovers what access the client should have considering the access token in the current request. + /// </summary> + /// <param name="request">The HTTP request message.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="requiredScopes">The set of scopes required to approve this request.</param> + /// <returns> + /// The access token describing the authorization the client has. Never <c>null</c>. + /// </returns> + /// <exception cref="ProtocolFaultResponseException">Thrown when the client is not authorized. This exception should be caught and the + /// <see cref="ProtocolFaultResponseException.ErrorResponseMessage" /> message should be returned to the client.</exception> + public virtual async Task<AccessToken> GetAccessTokenAsync(HttpRequestMessage requestMessage, CancellationToken cancellationToken = default(CancellationToken), params string[] requiredScopes) { + Requires.NotNull(requestMessage, "requestMessage"); Requires.NotNull(requiredScopes, "requiredScopes"); RequiresEx.ValidState(this.ScopeSatisfiedCheck != null, Strings.RequiredPropertyNotYetPreset); - if (httpRequestInfo == null) { - httpRequestInfo = this.Channel.GetRequestFromContext(); - } AccessToken accessToken; AccessProtectedResourceRequest request = null; try { - request = await this.Channel.TryReadFromRequestAsync<AccessProtectedResourceRequest>(cancellationToken, httpRequestInfo); + request = await this.Channel.TryReadFromRequestAsync<AccessProtectedResourceRequest>(requestMessage, cancellationToken); if (request != null) { accessToken = this.AccessTokenAnalyzer.DeserializeAccessToken(request, request.AccessToken); ErrorUtilities.VerifyHost(accessToken != null, "IAccessTokenAnalyzer.DeserializeAccessToken returned a null reslut."); @@ -133,22 +150,6 @@ namespace DotNetOpenAuth.OAuth2 { /// <summary> /// Discovers what access the client should have considering the access token in the current request. /// </summary> - /// <param name="request">The HTTP request message.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="requiredScopes">The set of scopes required to approve this request.</param> - /// <returns> - /// The access token describing the authorization the client has. Never <c>null</c>. - /// </returns> - /// <exception cref="ProtocolFaultResponseException">Thrown when the client is not authorized. This exception should be caught and the - /// <see cref="ProtocolFaultResponseException.ErrorResponseMessage" /> message should be returned to the client.</exception> - public virtual Task<AccessToken> GetAccessTokenAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken), params string[] requiredScopes) { - Requires.NotNull(request, "request"); - return this.GetAccessTokenAsync(new HttpRequestInfo(request), cancellationToken, requiredScopes); - } - - /// <summary> - /// Discovers what access the client should have considering the access token in the current request. - /// </summary> /// <param name="httpRequestInfo">The HTTP request info.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="requiredScopes">The set of scopes required to approve this request.</param> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs index e51eb24..aeba8d7 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs +++ b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs @@ -180,12 +180,12 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="headers">The incoming web headers.</param> /// <returns>The network credentials; or <c>null</c> if none could be discovered in the request.</returns> - internal static NetworkCredential ParseHttpBasicAuth(WebHeaderCollection headers) { + internal static NetworkCredential ParseHttpBasicAuth(System.Net.Http.Headers.HttpRequestHeaders headers) { Requires.NotNull(headers, "headers"); - string authorizationHeader = headers[HttpRequestHeaders.Authorization]; - if (authorizationHeader != null && authorizationHeader.StartsWith(HttpBasicAuthScheme, StringComparison.Ordinal)) { - string base64 = authorizationHeader.Substring(HttpBasicAuthScheme.Length); + var authorizationHeader = headers.Authorization; + if (authorizationHeader != null && string.Equals(authorizationHeader.Scheme, HttpBasicAuthScheme, StringComparison.Ordinal)) { + string base64 = authorizationHeader.Parameter; byte[] bits = Convert.FromBase64String(base64); string usernameColonPassword = HttpBasicEncoding.GetString(bits); string[] usernameAndPassword = usernameColonPassword.Split(ColonSeparator, 2); diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs index c5c3540..fb1aee3 100644 --- a/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs +++ b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs @@ -46,12 +46,14 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <summary> /// Backing field for the <see cref="Provider"/> property. /// </summary> - private static OpenIdProvider provider; + private Lazy<OpenIdProvider> provider; /// <summary> - /// The lock that must be obtained when initializing the provider field. + /// Initializes a new instance of the <see cref="ProviderEndpoint"/> class. /// </summary> - private static object providerInitializerLock = new object(); + public ProviderEndpoint() { + this.provider = new Lazy<OpenIdProvider>(this.CreateProvider); + } /// <summary> /// Fired when an incoming OpenID request is an authentication challenge @@ -70,22 +72,14 @@ namespace DotNetOpenAuth.OpenId.Provider { /// Gets or sets the <see cref="OpenIdProvider"/> instance to use for all instances of this control. /// </summary> /// <value>The default value is an <see cref="OpenIdProvider"/> instance initialized according to the web.config file.</value> - public static OpenIdProvider Provider { + public OpenIdProvider Provider { get { - if (provider == null) { - lock (providerInitializerLock) { - if (provider == null) { - provider = CreateProvider(); - } - } - } - - return provider; + return this.provider.Value; } set { Requires.NotNull(value, "value"); - provider = value; + this.provider = new Lazy<OpenIdProvider>(() => value, LazyThreadSafetyMode.PublicationOnly); } } @@ -181,10 +175,10 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <returns> /// The response message. /// </returns> - public static Task<HttpResponseMessage> PrepareResponseAsync(CancellationToken cancellationToken = default(CancellationToken)) { + public Task<HttpResponseMessage> PrepareResponseAsync(CancellationToken cancellationToken = default(CancellationToken)) { var pendingRequest = PendingRequest; PendingRequest = null; - return Provider.PrepareResponseAsync(pendingRequest, cancellationToken); + return this.Provider.PrepareResponseAsync(pendingRequest, cancellationToken); } /// <summary> @@ -196,46 +190,45 @@ namespace DotNetOpenAuth.OpenId.Provider { protected override void OnLoad(EventArgs e) { base.OnLoad(e); - Task.Run( - async delegate { - // There is the unusual scenario that this control is hosted by - // an ASP.NET web page that has other UI on it to that the user - // might see, including controls that cause a postback to occur. - // We definitely want to ignore postbacks, since any openid messages - // they contain will be old. - if (this.Enabled && !this.Page.IsPostBack) { - // Use the explicitly given state store on this control if there is one. - // Then try the configuration file specified one. Finally, use the default - // in-memory one that's built into OpenIdProvider. - // determine what incoming message was received - IRequest request = await Provider.GetRequestAsync(new HttpRequestWrapper(this.Context.Request), CancellationToken.None); - if (request != null) { - PendingRequest = null; + this.Page.RegisterAsyncTask(new PageAsyncTask(async cancellationToken => { + // There is the unusual scenario that this control is hosted by + // an ASP.NET web page that has other UI on it to that the user + // might see, including controls that cause a postback to occur. + // We definitely want to ignore postbacks, since any openid messages + // they contain will be old. + if (this.Enabled && !this.Page.IsPostBack) { + // Use the explicitly given state store on this control if there is one. + // Then try the configuration file specified one. Finally, use the default + // in-memory one that's built into OpenIdProvider. + // determine what incoming message was received + IRequest request = await Provider.GetRequestAsync(new HttpRequestWrapper(this.Context.Request), cancellationToken); + if (request != null) { + PendingRequest = null; - // process the incoming message appropriately and send the response - IAuthenticationRequest idrequest; - IAnonymousRequest anonRequest; - if ((idrequest = request as IAuthenticationRequest) != null) { - PendingAuthenticationRequest = idrequest; - this.OnAuthenticationChallenge(idrequest); - } else if ((anonRequest = request as IAnonymousRequest) != null) { - PendingAnonymousRequest = anonRequest; - if (!this.OnAnonymousRequest(anonRequest)) { - // This is a feature not supported by the OP, so - // go ahead and set disapproved so we can send a response. - Logger.OpenId.Warn( - "An incoming anonymous OpenID request message was detected, but the ProviderEndpoint.AnonymousRequest event is not handled, so returning cancellation message to relying party."); - anonRequest.IsApproved = false; - } - } - if (request.IsResponseReady) { - PendingAuthenticationRequest = null; - var response = await Provider.PrepareResponseAsync(request, CancellationToken.None); - response.Send(new HttpContextWrapper(this.Context).Response); + // process the incoming message appropriately and send the response + IAuthenticationRequest idrequest; + IAnonymousRequest anonRequest; + if ((idrequest = request as IAuthenticationRequest) != null) { + PendingAuthenticationRequest = idrequest; + this.OnAuthenticationChallenge(idrequest); + } else if ((anonRequest = request as IAnonymousRequest) != null) { + PendingAnonymousRequest = anonRequest; + if (!this.OnAnonymousRequest(anonRequest)) { + // This is a feature not supported by the OP, so + // go ahead and set disapproved so we can send a response. + Logger.OpenId.Warn( + "An incoming anonymous OpenID request message was detected, but the ProviderEndpoint.AnonymousRequest event is not handled, so returning cancellation message to relying party."); + anonRequest.IsApproved = false; } } + if (request.IsResponseReady) { + PendingAuthenticationRequest = null; + var response = await Provider.PrepareResponseAsync(request, cancellationToken); + await response.SendAsync(new HttpContextWrapper(this.Context), cancellationToken); + } } - }); + } + })); } /// <summary> @@ -268,8 +261,8 @@ namespace DotNetOpenAuth.OpenId.Provider { /// Creates the default OpenIdProvider to use. /// </summary> /// <returns>The new instance of OpenIdProvider.</returns> - private static OpenIdProvider CreateProvider() { - return new OpenIdProvider(OpenIdElement.Configuration.Provider.ApplicationStore.CreateInstance(OpenIdProvider.GetHttpApplicationStore(), null)); + private OpenIdProvider CreateProvider() { + return new OpenIdProvider(OpenIdElement.Configuration.Provider.ApplicationStore.CreateInstance(OpenIdProvider.GetHttpApplicationStore(new HttpContextWrapper(this.Context)), null)); } } } diff --git a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/HostProcessedRequest.cs b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/HostProcessedRequest.cs index 7671194..5f3bba2 100644 --- a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/HostProcessedRequest.cs +++ b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/HostProcessedRequest.cs @@ -31,6 +31,8 @@ namespace DotNetOpenAuth.OpenId.Provider { /// </summary> private RelyingPartyDiscoveryResult? realmDiscoveryResult; + private IHostFactories hostFactories; + /// <summary> /// Initializes a new instance of the <see cref="HostProcessedRequest"/> class. /// </summary> @@ -40,6 +42,7 @@ namespace DotNetOpenAuth.OpenId.Provider { : base(request, provider.SecuritySettings) { Requires.NotNull(provider, "provider"); + this.hostFactories = provider.Channel.HostFactories; this.negativeResponse = new Lazy<Task<NegativeAssertionResponse>>(() => NegativeAssertionResponse.CreateAsync(request, CancellationToken.None, provider.Channel)); Reporting.RecordEventOccurrence(this, request.Realm); } @@ -139,8 +142,8 @@ namespace DotNetOpenAuth.OpenId.Provider { /// Result of realm discovery. /// </returns> private async Task<RelyingPartyDiscoveryResult> IsReturnUrlDiscoverableCoreAsync(IHostFactories hostFactories, CancellationToken cancellationToken) { - Requires.NotNull(hostFactories, "hostFactories"); ErrorUtilities.VerifyInternal(this.Realm != null, "Realm should have been read or derived by now."); + hostFactories = hostFactories ?? this.hostFactories; try { if (this.SecuritySettings.RequireSsl && this.Realm.Scheme != Uri.UriSchemeHttps) { diff --git a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs index e71c291..a571e31 100644 --- a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs +++ b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs @@ -214,14 +214,15 @@ namespace DotNetOpenAuth.OpenId.Provider { /// on its own user database and policies.</para> /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para> /// </remarks> - public Task<IRequest> GetRequestAsync(CancellationToken cancellationToken) { - return this.GetRequestAsync(this.Channel.GetRequestFromContext(), cancellationToken); + public Task<IRequest> GetRequestAsync(HttpRequestBase request, CancellationToken cancellationToken) { + request = request ?? this.Channel.GetRequestFromContext(); + return this.GetRequestAsync(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> /// Gets the incoming OpenID request if there is one, or null if none was detected. /// </summary> - /// <param name="httpRequestInfo">The incoming HTTP request to extract the message from.</param> + /// <param name="request">The incoming HTTP request to extract the message from.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// The request that the hosting Provider should process and then transmit the response for. @@ -234,17 +235,17 @@ namespace DotNetOpenAuth.OpenId.Provider { /// be authentication requests where the Provider site has to make decisions based /// on its own user database and policies. /// </remarks> - public async Task<IRequest> GetRequestAsync(HttpRequestBase httpRequestInfo, CancellationToken cancellationToken) { - Requires.NotNull(httpRequestInfo, "httpRequestInfo"); + public async Task<IRequest> GetRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); IDirectedProtocolMessage incomingMessage = null; try { - incomingMessage = await this.Channel.ReadFromRequestAsync(httpRequestInfo, cancellationToken); + incomingMessage = await this.Channel.ReadFromRequestAsync(request, cancellationToken); if (incomingMessage == null) { // If the incoming request does not resemble an OpenID message at all, // it's probably a user who just navigated to this URL, and we should // just return null so the host can display a message to the user. - if (httpRequestInfo.HttpMethod == "GET" && !httpRequestInfo.GetPublicFacingUrl().QueryStringContainPrefixedParameters(Protocol.Default.openid.Prefix)) { + if (request.Method == HttpMethod.Get && !request.RequestUri.QueryStringContainPrefixedParameters(Protocol.Default.openid.Prefix)) { return null; } @@ -292,7 +293,7 @@ namespace DotNetOpenAuth.OpenId.Provider { throw ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessageReceivedOfMany); } catch (ProtocolException ex) { - IRequest errorResponse = this.GetErrorResponse(ex, httpRequestInfo, incomingMessage); + IRequest errorResponse = this.GetErrorResponse(ex, request, incomingMessage); if (errorResponse == null) { throw; } @@ -450,16 +451,16 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <returns> /// Either the <see cref="IRequest"/> to return to the host site or null to indicate no response could be reasonably created and that the caller should rethrow the exception. /// </returns> - private IRequest GetErrorResponse(ProtocolException ex, HttpRequestBase httpRequestInfo, IDirectedProtocolMessage incomingMessage) { + private IRequest GetErrorResponse(ProtocolException ex, HttpRequestMessage request, IDirectedProtocolMessage incomingMessage) { Requires.NotNull(ex, "ex"); - Requires.NotNull(httpRequestInfo, "httpRequestInfo"); + Requires.NotNull(request, "request"); Logger.OpenId.Error("An exception was generated while processing an incoming OpenID request.", ex); IErrorMessage errorMessage; // We must create the appropriate error message type (direct vs. indirect) // based on what we see in the request. - string returnTo = httpRequestInfo.QueryString[Protocol.Default.openid.return_to]; + string returnTo = HttpUtility.ParseQueryString(request.RequestUri.Query)[Protocol.Default.openid.return_to]; if (returnTo != null) { // An indirect request message from the RP // We need to return an indirect response error message so the RP can consume it. @@ -470,7 +471,7 @@ namespace DotNetOpenAuth.OpenId.Provider { } else { errorMessage = new IndirectErrorResponse(Protocol.Default.Version, new Uri(returnTo)); } - } else if (httpRequestInfo.HttpMethod == "POST") { + } else if (request.Method == HttpMethod.Post) { // A direct request message from the RP // We need to return a direct response error message so the RP can consume it. // Consistent with OpenID 2.0 section 5.1.2.2. diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs b/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs index defd15c..066b5a3 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs @@ -442,11 +442,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { }; HttpResponseMessage response = await this.RelyingParty.ProcessResponseFromPopupAsync( - new HttpRequestWrapper(this.Context.Request), + new HttpRequestWrapper(this.Context.Request).AsHttpRequestMessage(), callback, cancellationToken); - response.Send(); + await response.SendAsync(new HttpContextWrapper(this.Context), cancellationToken); } /// <summary> diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs b/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs index dc8ba54..77a5b44 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs @@ -677,7 +677,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// A task that completes with the asynchronous operation. /// </returns> protected virtual Task ScriptClosingPopupOrIFrameAsync(CancellationToken cancellationToken) { - return this.RelyingParty.ProcessResponseFromPopupAsync(cancellationToken); + return this.RelyingParty.ProcessResponseFromPopupAsync(new HttpRequestWrapper(this.Context.Request).AsHttpRequestMessage(), cancellationToken); } /// <summary> diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs index 6a1ef83..33bec3b 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs @@ -520,9 +520,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <remarks> /// Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context. /// </remarks> - public Task<IAuthenticationResponse> GetResponseAsync(CancellationToken cancellationToken = default(CancellationToken)) { - RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); - return this.GetResponseAsync(this.Channel.GetRequestFromContext(), cancellationToken); + public Task<IAuthenticationResponse> GetResponseAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + request = request ?? this.channel.GetRequestFromContext(); + return this.GetResponseAsync(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> @@ -533,10 +533,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <returns> /// The processed authentication response if there is any; <c>null</c> otherwise. /// </returns> - public async Task<IAuthenticationResponse> GetResponseAsync(HttpRequestBase httpRequestInfo, CancellationToken cancellationToken = default(CancellationToken)) { - Requires.NotNull(httpRequestInfo, "httpRequestInfo"); + public async Task<IAuthenticationResponse> GetResponseAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "httpRequestInfo"); try { - var message = await this.Channel.ReadFromRequestAsync(httpRequestInfo, cancellationToken); + var message = await this.Channel.ReadFromRequestAsync(request, cancellationToken); PositiveAssertionResponse positiveAssertion; NegativeAssertionResponse negativeAssertion; IndirectSignedResponse positiveExtensionOnly; @@ -572,6 +572,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Processes the response received in a popup window or iframe to an AJAX-directed OpenID authentication. /// </summary> + /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// The HTTP response to send to this HTTP request. @@ -579,10 +580,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <remarks> /// Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context. /// </remarks> - public Task<HttpResponseMessage> ProcessResponseFromPopupAsync(CancellationToken cancellationToken) { - RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); - - return this.ProcessResponseFromPopupAsync(this.Channel.GetRequestFromContext(), cancellationToken); + public Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + request = request ?? this.Channel.GetRequestFromContext(); + return this.ProcessResponseFromPopupAsync(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> @@ -593,9 +593,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <returns> /// The HTTP response to send to this HTTP request. /// </returns> - public Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestBase request, CancellationToken cancellationToken) { + public Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(request, "request"); - return this.ProcessResponseFromPopupAsync(request, null, cancellationToken); } @@ -680,11 +679,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// The HTTP response to send to this HTTP request. /// </returns> [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "OpenID", Justification = "real word"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "iframe", Justification = "Code contracts")] - internal async Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestBase request, Action<AuthenticationStatus> callback, CancellationToken cancellationToken) { + internal async Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestMessage request, Action<AuthenticationStatus> callback, CancellationToken cancellationToken) { Requires.NotNull(request, "request"); string extensionsJson = null; - var authResponse = await this.NonVerifyingRelyingParty.GetResponseAsync(cancellationToken); + var authResponse = await this.NonVerifyingRelyingParty.GetResponseAsync(request, cancellationToken); ErrorUtilities.VerifyProtocol(authResponse != null, OpenIdStrings.PopupRedirectMissingResponse); // Give the caller a chance to notify the hosting page and fill up the clientScriptExtensions collection. @@ -692,7 +691,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { callback(authResponse.Status); } - Logger.OpenId.DebugFormat("Popup or iframe callback from OP: {0}", request.Url); + Logger.OpenId.DebugFormat("Popup or iframe callback from OP: {0}", request.RequestUri); Logger.Controls.DebugFormat( "An authentication response was found in a popup window or iframe using a non-verifying RP with status: {0}", authResponse.Status); @@ -714,13 +713,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } string payload = "document.URL"; - if (request.HttpMethod == "POST") { + if (request.Method == HttpMethod.Post) { // Promote all form variables to the query string, but since it won't be passed // to any server (this is a javascript window-to-window transfer) the length of // it can be arbitrarily long, whereas it was POSTed here probably because it // was too long for HTTP transit. - UriBuilder payloadUri = new UriBuilder(request.Url); - payloadUri.AppendQueryArgs(request.Form.ToDictionary()); + UriBuilder payloadUri = new UriBuilder(request.RequestUri); + payloadUri.AppendQueryArgs(await Channel.ParseUrlEncodedFormContentAsync(request, cancellationToken)); payload = MessagingUtilities.GetSafeJavascriptValue(payloadUri.Uri.AbsoluteUri); } diff --git a/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs b/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs index e111655..5073897 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs @@ -97,7 +97,7 @@ namespace DotNetOpenAuth.OpenId { context = context ?? new HttpContextWrapper(HttpContext.Current); var response = await authenticationRequest.GetRedirectingResponseAsync(cancellationToken); - await response.SendAsync(context.Response); + await response.SendAsync(context, cancellationToken); } /// <summary> diff --git a/src/DotNetOpenAuth.TestWeb/OpenIdProviderEndpoint.ashx b/src/DotNetOpenAuth.TestWeb/OpenIdProviderEndpoint.ashx index 7225a01..70dc9af 100644 --- a/src/DotNetOpenAuth.TestWeb/OpenIdProviderEndpoint.ashx +++ b/src/DotNetOpenAuth.TestWeb/OpenIdProviderEndpoint.ashx @@ -61,7 +61,7 @@ public class OpenIdProviderEndpoint : IHttpAsyncHandler { } var response = await provider.PrepareResponseAsync(request, context.Response.ClientDisconnectedToken); - await response.SendAsync(new HttpResponseWrapper(context.Response), context.Response.ClientDisconnectedToken); + await response.SendAsync(new HttpContextWrapper(context), context.Response.ClientDisconnectedToken); } } }
\ No newline at end of file diff --git a/src/packages/repositories.config b/src/packages/repositories.config index fe99184..0d28c5f 100644 --- a/src/packages/repositories.config +++ b/src/packages/repositories.config @@ -18,6 +18,7 @@ <repository path="..\..\samples\OpenIdRelyingPartyWebForms\packages.config" /> <repository path="..\..\samples\OpenIdRelyingPartyWebFormsVB\packages.config" /> <repository path="..\..\samples\OpenIdWebRingSsoProvider\packages.config" /> + <repository path="..\..\samples\OpenIdWebRingSsoRelyingParty\packages.config" /> <repository path="..\DotNetOpenAuth.AspNet.Test\packages.config" /> <repository path="..\DotNetOpenAuth.AspNet\packages.config" /> <repository path="..\DotNetOpenAuth.Core.UI\packages.config" /> |