aspnet / Security

[Archived] Middleware for security and authorization of web apps. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
1.27k stars 600 forks source link

OAuth redirect_uri using http instead of https on Azure App Service Linux #1901

Closed jarrettv closed 5 years ago

jarrettv commented 5 years ago

I believe this is a problem with https being handled up to the nginx layer and kestrel getting http traffic. It confuses the BuildRedirectUri and it uses the scheme (http) resulting in the wrong redirect_uri sent to the OAuth provider.

For providers that allow http redirect, it is no problem but some require https like facebook.

It doesn't look like you can easily fix the redirect_uri because it is a protected property.

Any ideas? Perhaps the BuildRedirectUri should check the X-Forwarded-Proto header.

Tratcher commented 5 years ago

There's middleware for dealing with x-forwarded-proto. See here.

jarrettv commented 5 years ago

@Tratcher thanks for the reply. i've already tried the forward headers middleware. Perhaps it is an issue with the nginx side in Azure.

This seems like something that should work out of the box on Azure.

If I try to fix the redirect_uri on the fly, it breaks since it is protected. But i captured the headers from the exception in case that helps.


Accept  text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding gzip, deflate, br
Accept-Language en-US,en;q=0.9
Cache-Control   max-age=0
CLIENT-IP   68.117.149.156:58764
Connection  close
Cookie  .AspNetCore.Correlation.Reddit.iEwx6UcJTSFcJXxEeMH0xzcD08xjf9uDedT8KFaffXA=N; .AspNetCore.Correlation.Reddit.Zy49-FZcLUiMbXr8RKkB5uTEfjj02VzbFe2-RC4PLZg=N; .AspNetCore.Correlation.Reddit.2_6Fp-RNpXMt9qpjEU7C87izWHnfWBpBslgNkqpUaC4=N; ai_user=vE3Ea|2017-12-17T19:09:11.535Z; ARRAffinity=e013ed427e21d614152a7ac45b6d8b195b1dc9dff2e1a24d9690e609b5232aee; .AspNet.LinkId.Google=1; .AspNet.RedirectUri.Google=%2Flink%3Fprovider%3DGoogle; ai_session=n6qGu|1541338881571|1541339537302.4
DISGUISED-HOST  warmatch.us
dnt 1
Host    warmatch.us
IS-SERVICE-TUNNELED 0
Max-Forwards    10
Referer https://www.reddit.com/
upgrade-insecure-requests   1
User-Agent  Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36
WAS-DEFAULT-HOSTNAME    warmatch.azurewebsites.net
X-ARR-LOG-ID    81d87bdf-59e0-4be9-9d3f-53859fc09c7e
X-ARR-SSL   2048|256|C=GB, S=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO RSA Domain Validation Secure Server CA|OU=Domain Control Validated, OU=PositiveSSL, CN=warmatch.us
X-Client-IP 68.117.149.156
X-Client-Port   58764
X-Forwarded-For 68.117.149.156:58764
X-Forwarded-Proto   https
X-Original-URL  /signin-reddit?state=CfDJ8GBPm76rnZJMkDoHQZyANGvky0xrp-UbTGn-gI-UXJj4TIu1XjGau0hcDVfuFna7XH8iVDH6DrkDdt1LPnB-F9wHPIqIqqO3XXDCljdEH81zDKzdwqcKP3YHXZE_mBRwfKZ3cSyGN1CLE2CwMJFnLtz4CJme30vR-T8yhVhGmWoXowLhZJ6jRhMVm4zInFgc9yZgOC6fENEesG6ZQjvm5JfXOx9UuB-F2eKMxTJZqfUXMTR4UXuN9RQT6sJjRLJKhW0IdkOrCgnlExGe1Ji8xh4&code=EisIwm7JMoV1uS-9_25_-BSwuCo
X-SITE-DEPLOYMENT-ID    WarMatch
X-WAWS-Unencoded-URL    /signin-reddit?state=CfDJ8GBPm76rnZJMkDoHQZyANGvky0xrp-UbTGn-gI-UXJj4TIu1XjGau0hcDVfuFna7XH8iVDH6DrkDdt1LPnB-F9wHPIqIqqO3XXDCljdEH81zDKzdwqcKP3YHXZE_mBRwfKZ3cSyGN1CLE2CwMJFnLtz4CJme30vR-T8yhVhGmWoXowLhZJ6jRhMVm4zInFgc9yZgOC6fENEesG6ZQjvm5JfXOx9UuB-F2eKMxTJZqfUXMTR4UXuN9RQT6sJjRLJKhW0IdkOrCgnlExGe1Ji8xh4&code=EisIwm7JMoV1uS-9_25_-BSwuCo```
Tratcher commented 5 years ago

Do you have XForwardedFor turned on? That's usually what trips people up as it requires extra configuration in some setups. If you reverse proxy is not on the same box then you need to set KnownProxies or KnownNetworks.

Tratcher commented 5 years ago

Yeah, we're working on that... https://github.com/aspnet/AzureIntegration/issues/176

jarrettv commented 5 years ago

I do have XForwardedFor. I've included my startup.cs file in full.

I'm looking for a solution asap as both my Facebook and Reddit logins are broken and I'd rather not roll back to windows hosting.


using ClanLogs.Infra;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Security.Claims;
using System.Threading.Tasks;

namespace ClanLogs.Web
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddResponseCaching();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            services.AddSignalR();

            IocModule.ConfigureServices(services);
            services.AddMemoryCache();
            var mapConfig = MapConfig.Get();
            services.AddSingleton(mapConfig);
            services.AddSingleton(x => x.GetService<MapperConfiguration>().CreateMapper(x.GetService));

            services.Configure<ForwardedHeadersOptions>(opts =>
                opts.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto);

            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(opts =>
                {
                    opts.LoginPath = "/login";
                    opts.Events = new CookieAuthenticationEvents
                    {
                        OnSignedIn = ctx =>
                        {
                            var cookieOpts = new CookieOptions { Expires = DateTime.UtcNow.Add(ctx.Options.ExpireTimeSpan) };
                            var user = ctx.Principal.FindFirst(ClaimTypes.Name)?.Value ?? ctx.Principal.Identity.Name;
                            ctx.HttpContext.Response.Cookies.Append("WM_User", user, cookieOpts);
                            return Task.CompletedTask;
                        }
                        ,
                        OnSigningOut = ctx =>
                       {
                           ctx.HttpContext.Response.Cookies.Delete("WM_User");
                           return Task.CompletedTask;
                       }
                    };
                })
                .AddFacebook(opts =>
                {
                    opts.AppId = Configuration["Authentication:Facebook:AppId"];
                    opts.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
                })
                .AddGoogle(opts =>
                {
                    opts.ClientId = Configuration["Authentication:Google:ClientId"];
                    opts.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
                })
                .AddTwitter(opts =>
                {
                    opts.ConsumerKey = Configuration["Authentication:Twitter:ConsumerKey"];
                    opts.ConsumerSecret = Configuration["Authentication:Twitter:ConsumerSecret"];
                    opts.RetrieveUserDetails = true;
                })
                .AddReddit(opts =>
                {
                    opts.ClientId = Configuration["Authentication:Reddit:ClientId"];
                    opts.ClientSecret = Configuration["Authentication:Reddit:ClientSecret"];
                })
                .AddDiscord(opts =>
                {
                    opts.ClientId = Configuration["Authentication:Discord:ClientId"];
                    opts.ClientSecret = Configuration["Authentication:Discord:ClientSecret"];
                })
                .AddPatreon(opts =>
                {
                    opts.ClientId = Configuration["Authentication:Patreon:ClientId"];
                    opts.ClientSecret = Configuration["Authentication:Patreon:ClientSecret"];
                })
                ;
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseForwardedHeaders();
            app.UseStatusCodePages();
            app.UseResponseCaching();
            app.UseDeveloperExceptionPage();
            //app.UseExceptionHandler("/Dashboard/Error");
            app.UseHsts();
            //app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseAuthentication();
            app.UseSignalR(route =>
            {
                route.MapHub<ChangeHub>("/changeHub");
            });
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}
Tratcher commented 5 years ago

it requires extra configuration in some setups. If you reverse proxy is not on the same box then you need to set KnownProxies or KnownNetworks.

jarrettv commented 5 years ago

here is what i landed on that seems to work. I'd need an Azure engineer to tell me what the known proxies or known networks are.


{
    opts.RequireHeaderSymmetry = false;
    opts.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    opts.KnownNetworks.Clear();
    opts.KnownProxies.Clear();
});```
Tratcher commented 5 years ago

Note RequireHeaderSymmetry = false has been the default for several releases.

Tratcher commented 5 years ago

Closing as a duplicate of https://github.com/aspnet/AzureIntegration/issues/176