From 78cf7f99ffd0b18f5beac89ef37345e4aef66dc1 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 29 Jun 2015 10:43:43 -0700 Subject: [PATCH] Fix base path issue with OAuthHandler --- .../CookieAuthenticationHandler.cs | 2 +- .../OAuthAuthenticationHandler.cs | 13 +--- .../TwitterAuthenticationHandler.cs | 7 +-- .../AuthenticationHandler.cs | 17 ++++- .../Facebook/FacebookMiddlewareTests.cs | 63 +++++++++++++++++++ 5 files changed, 84 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationHandler.cs index 66fe679bd1..386daa0de8 100644 --- a/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationHandler.cs @@ -117,7 +117,7 @@ namespace Microsoft.AspNet.Authentication.Cookies { Domain = Options.CookieDomain, HttpOnly = Options.CookieHttpOnly, - Path = Options.CookiePath ?? (RequestPathBase.HasValue ? RequestPathBase.ToString() : "/"), + Path = Options.CookiePath ?? (OriginalPathBase.HasValue ? OriginalPathBase.ToString() : "/"), }; if (Options.CookieSecure == CookieSecureOption.SameAsRequest) { diff --git a/src/Microsoft.AspNet.Authentication.OAuth/OAuthAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OAuth/OAuthAuthenticationHandler.cs index 86d4954745..1be0ccff8e 100644 --- a/src/Microsoft.AspNet.Authentication.OAuth/OAuthAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OAuth/OAuthAuthenticationHandler.cs @@ -116,10 +116,7 @@ namespace Microsoft.AspNet.Authentication.OAuth return new AuthenticationTicket(properties, Options.AuthenticationScheme); } - var requestPrefix = Request.Scheme + "://" + Request.Host; - var redirectUri = requestPrefix + RequestPathBase + Options.CallbackPath; - - var tokens = await ExchangeCodeAsync(code, redirectUri); + var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath)); if (string.IsNullOrWhiteSpace(tokens.AccessToken)) { @@ -172,20 +169,16 @@ namespace Microsoft.AspNet.Authentication.OAuth protected override Task HandleUnauthorizedAsync([NotNull] ChallengeContext context) { - var baseUri = Request.Scheme + "://" + Request.Host + Request.PathBase; - var currentUri = baseUri + Request.Path + Request.QueryString; - var redirectUri = baseUri + Options.CallbackPath; - var properties = new AuthenticationProperties(context.Properties); if (string.IsNullOrEmpty(properties.RedirectUri)) { - properties.RedirectUri = currentUri; + properties.RedirectUri = CurrentUri; } // OAuth2 10.12 CSRF GenerateCorrelationId(properties); - var authorizationEndpoint = BuildChallengeUrl(properties, redirectUri); + var authorizationEndpoint = BuildChallengeUrl(properties, BuildRedirectUri(Options.CallbackPath)); var redirectContext = new OAuthApplyRedirectContext( Context, Options, diff --git a/src/Microsoft.AspNet.Authentication.Twitter/TwitterAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.Twitter/TwitterAuthenticationHandler.cs index 659b8b6aad..b9dc09c99a 100644 --- a/src/Microsoft.AspNet.Authentication.Twitter/TwitterAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.Twitter/TwitterAuthenticationHandler.cs @@ -120,16 +120,13 @@ namespace Microsoft.AspNet.Authentication.Twitter } protected override async Task HandleUnauthorizedAsync([NotNull] ChallengeContext context) { - var requestPrefix = Request.Scheme + "://" + Request.Host; - var callBackUrl = requestPrefix + RequestPathBase + Options.CallbackPath; - var properties = new AuthenticationProperties(context.Properties); if (string.IsNullOrEmpty(properties.RedirectUri)) { - properties.RedirectUri = requestPrefix + Request.PathBase + Request.Path + Request.QueryString; + properties.RedirectUri = CurrentUri; } - var requestToken = await ObtainRequestTokenAsync(Options.ConsumerKey, Options.ConsumerSecret, callBackUrl, properties); + var requestToken = await ObtainRequestTokenAsync(Options.ConsumerKey, Options.ConsumerSecret, BuildRedirectUri(Options.CallbackPath), properties); if (requestToken.CallbackConfirmed) { var twitterAuthenticationEndpoint = AuthenticationEndpoint + requestToken.Token; diff --git a/src/Microsoft.AspNet.Authentication/AuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication/AuthenticationHandler.cs index 8651c4b9a6..e226516ff1 100644 --- a/src/Microsoft.AspNet.Authentication/AuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication/AuthenticationHandler.cs @@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Authentication get { return Context.Response; } } - protected PathString RequestPathBase { get; private set; } + protected PathString OriginalPathBase { get; private set; } protected ILogger Logger { get; private set; } @@ -48,11 +48,19 @@ namespace Microsoft.AspNet.Authentication public IAuthenticationHandler PriorHandler { get; set; } + protected string CurrentUri + { + get + { + return Request.Scheme + "://" + Request.Host + Request.PathBase + Request.Path + Request.QueryString; + } + } + protected async Task BaseInitializeAsync([NotNull] AuthenticationOptions options, [NotNull] HttpContext context, [NotNull] ILogger logger, [NotNull] IUrlEncoder encoder) { _baseOptions = options; Context = context; - RequestPathBase = Request.PathBase; + OriginalPathBase = Request.PathBase; Logger = logger; UrlEncoder = encoder; @@ -70,6 +78,11 @@ namespace Microsoft.AspNet.Authentication } } + protected string BuildRedirectUri(string targetPath) + { + return Request.Scheme + "://" + Request.Host + OriginalPathBase + targetPath; + } + private static async Task OnStartingCallback(object state) { var handler = (AuthenticationHandler)state; diff --git a/test/Microsoft.AspNet.Authentication.Test/Facebook/FacebookMiddlewareTests.cs b/test/Microsoft.AspNet.Authentication.Test/Facebook/FacebookMiddlewareTests.cs index 7e01755674..8e4fe65225 100644 --- a/test/Microsoft.AspNet.Authentication.Test/Facebook/FacebookMiddlewareTests.cs +++ b/test/Microsoft.AspNet.Authentication.Test/Facebook/FacebookMiddlewareTests.cs @@ -6,8 +6,10 @@ using System.Net; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Authentication; using Microsoft.AspNet.TestHost; using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.WebEncoders; using Shouldly; using Xunit; @@ -61,6 +63,67 @@ namespace Microsoft.AspNet.Authentication.Facebook query.ShouldContain("custom=test"); } + [Fact] + public async Task NestedMapWillNotAffectRedirect() + { + var server = CreateServer(app => + app.Map("/base", map => { + map.UseFacebookAuthentication(); + map.Map("/login", signoutApp => signoutApp.Run(context => context.Authentication.ChallengeAsync("Facebook", new AuthenticationProperties() { RedirectUri = "/" }))); + }), + services => + { + services.AddAuthentication(); + services.ConfigureFacebookAuthentication(options => + { + options.AppId = "Test App Id"; + options.AppSecret = "Test App Secret"; + options.SignInScheme = "External"; + }); + }, + handler: null); + var transaction = await server.SendAsync("http://example.com/base/login"); + transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect); + var location = transaction.Response.Headers.Location.AbsoluteUri; + location.ShouldContain("https://www.facebook.com/v2.2/dialog/oauth"); + location.ShouldContain("response_type=code"); + location.ShouldContain("client_id="); + location.ShouldContain("redirect_uri=" + UrlEncoder.Default.UrlEncode("http://example.com/base/signin-facebook")); + location.ShouldContain("scope="); + location.ShouldContain("state="); + } + + [Fact] + public async Task MapWillNotAffectRedirect() + { + var server = CreateServer( + app => + { + app.UseFacebookAuthentication(); + app.Map("/login", signoutApp => signoutApp.Run(context => context.Authentication.ChallengeAsync("Facebook", new AuthenticationProperties() { RedirectUri = "/" }))); + }, + services => + { + services.AddAuthentication(); + services.ConfigureFacebookAuthentication(options => + { + options.AppId = "Test App Id"; + options.AppSecret = "Test App Secret"; + options.SignInScheme = "External"; + }); + }, + handler: null); + var transaction = await server.SendAsync("http://example.com/login"); + transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect); + var location = transaction.Response.Headers.Location.AbsoluteUri; + location.ShouldContain("https://www.facebook.com/v2.2/dialog/oauth"); + location.ShouldContain("response_type=code"); + location.ShouldContain("client_id="); + location.ShouldContain("redirect_uri="+ UrlEncoder.Default.UrlEncode("http://example.com/signin-facebook")); + location.ShouldContain("scope="); + location.ShouldContain("state="); + } + [Fact] public async Task ChallengeWillTriggerRedirection() {