【问题标题】:Running asp.net core 2 app with OAuth2 as Azure Appservice results in 502 errors使用 OAuth2 作为 Azure Appservice 运行 asp.net core 2 应用程序会导致 502 错误
【发布时间】:2018-03-30 10:15:53
【问题描述】:

我使用来自 Google 的 OAuth 身份验证创建了一个简单的 ASP.NET Core Web 应用程序。我在本地机器上运行得很好。 然而,在将其作为 AppService 部署到 Azure 之后,OAuth 重定向似乎变得一团糟。

应用程序本身可以在这里找到:
https://gcalworkshiftui20180322114905.azurewebsites.net/

这是一个实际返回结果并显示应用正在运行的 url:
https://gcalworkshiftui20180322114905.azurewebsites.net/Account/Login?ReturnUrl=%2F

有时应用程序响应良好,但一旦我尝试使用 Google 登录,它就会一直加载并最终返回以下消息:

The specified CGI application encountered an error and the server terminated the process.

在幕后,身份验证回调似乎因 502.3 错误而失败:

502.3 Bad Gateway “The operation timed out”

可以在此处找到错误跟踪: https://gcalworkshiftui20180322114905.azurewebsites.net/errorlog.xml

Microsoft 的文档还没有真正提供帮助。
https://docs.microsoft.com/en-us/azure/app-service/app-service-authentication-overview

进一步调查使我相信这与以下代码有关:

public GCalService(string clientId, string secret)
{
    string credPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
    credPath = Path.Combine(credPath, ".credentials/calendar-dotnet-quickstart.json");

    var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
        new ClientSecrets
        {
            ClientId = clientId,
            ClientSecret = secret
        },
        new[] {CalendarService.Scope.Calendar},
        "user",
        CancellationToken.None,
        new FileDataStore(credPath, true)).Result;

    // Create Google Calendar API service.
    _service = new CalendarService(new BaseClientService.Initializer()
    {
        HttpClientInitializer = credential,
        ApplicationName = "gcalworkshift"
    });
}

我可以想象 Azure 不支持个人文件夹?谷歌搜索并没有告诉我太多。

【问题讨论】:

    标签: azure asp.net-core oauth-2.0 azure-web-app-service


    【解决方案1】:

    我跟随 Facebook, Google, and external provider authentication in ASP.NET CoreGoogle external login setup in ASP.NET Core 创建了一个带有 Google 身份验证的 ASP.NET Core Web 应用程序来检查这个问题。

    我还关注.NET console application to access the Google Calendar APICalendar.ASP.NET.MVC5 来构建我的示例项目。核心代码如下,大家可以参考:

    Startup.cs

        public class Startup
        {
            public readonly IDataStore dataStore = new FileDataStore(GoogleWebAuthorizationBroker.Folder); //C:\Users\{username}\AppData\Roaming\Google.Apis.Auth
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddDbContext<ApplicationDbContext>(options =>
                    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    
                services.AddIdentity<ApplicationUser, IdentityRole>()
                    .AddEntityFrameworkStores<ApplicationDbContext>()
                    .AddDefaultTokenProviders();
    
                services.AddAuthentication().AddGoogle(googleOptions =>
                {
                    googleOptions.ClientId = "{ClientId}";
                    googleOptions.ClientSecret = "{ClientSecret}";
                    googleOptions.Scope.Add(CalendarService.Scope.CalendarReadonly); //"https://www.googleapis.com/auth/calendar.readonly"
                    googleOptions.AccessType = "offline"; //request a refresh_token
                    googleOptions.Events = new OAuthEvents()
                    {
                        OnCreatingTicket = async (context) =>
                        {
                            var userEmail = context.Identity.FindFirst(ClaimTypes.Email).Value;
    
                            var tokenResponse = new TokenResponse()
                            {
                                AccessToken = context.AccessToken,
                                RefreshToken = context.RefreshToken,
                                ExpiresInSeconds = (long)context.ExpiresIn.Value.TotalSeconds,
                                IssuedUtc = DateTime.UtcNow
                            };
    
                            await dataStore.StoreAsync(userEmail, tokenResponse);
                        }
                    };
                });
    
                services.AddMvc();
            }
    
        }
    }
    

    CalendarController.cs

        [Authorize]
        public class CalendarController : Controller
        {
    
            private readonly IDataStore dataStore = new FileDataStore(GoogleWebAuthorizationBroker.Folder);
    
            private async Task<UserCredential> GetCredentialForApiAsync()
            {
                var initializer = new GoogleAuthorizationCodeFlow.Initializer
                {
                    ClientSecrets = new ClientSecrets
                    {
                        ClientId = "{ClientId}",
                        ClientSecret = "{ClientSecret}",
                    },
                    Scopes = new[] {
                        "openid",
                        "email",
                        CalendarService.Scope.CalendarReadonly
                    }
                };
                var flow = new GoogleAuthorizationCodeFlow(initializer);
    
                string userEmail = ((ClaimsIdentity)HttpContext.User.Identity).FindFirst(ClaimTypes.Name).Value;
    
                var token = await dataStore.GetAsync<TokenResponse>(userEmail);
                return new UserCredential(flow, userEmail, token);
            }
    
            // GET: /Calendar/ListCalendars
            public async Task<ActionResult> ListCalendars()
            {
                const int MaxEventsPerCalendar = 20;
                const int MaxEventsOverall = 50;
    
                var credential = await GetCredentialForApiAsync();
    
                var initializer = new BaseClientService.Initializer()
                {
                    HttpClientInitializer = credential,
                    ApplicationName = "ASP.NET Core Google Calendar Sample",
                };
                var service = new CalendarService(initializer);
    
                // Fetch the list of calendars.
                var calendars = await service.CalendarList.List().ExecuteAsync();
    
                return Json(calendars.Items);
            }
        }    
    

    在部署到 Azure Web 应用之前,我将用于构造FileDataStorefolder 参数更改为D:\home,但出现以下错误:

    UnauthorizedAccessException:对路径“D:\home\Google.Apis.Auth.OAuth2.Responses.TokenResponse-{user-identifier}”的访问被拒绝。

    然后,我尝试将参数folder 设置为D:\home\site 并重新部署我的Web 应用程序,发现它可以按预期工作,并且记录的用户凭据将保存在您的Azure Web 应用服务器的D:\home\site 下。

    Azure Web Apps 在称为沙盒的安全环境中运行,该环境有一些限制,您可以关注Azure Web App sandbox 的详细信息。

    此外,您提到了App Service Authentication,它提供了内置身份验证,而无需在您的代码中添加任何代码。由于您已在 Web 应用程序中编写代码进行身份验证,因此无需设置应用服务身份验证。

    对于使用应用服务身份验证,您可以按照here 进行配置,然后您的NetCore 后端可以通过/.auth/me 端点上的HTTP GET 获取其他用户详细信息(access_tokenrefresh_token 等),详细信息你可以关注这个类似的issue。获取登录用户的令牌响应后,您可以手动构建UserCredential,然后构建CalendarService

    【讨论】:

    • 哇,感谢布鲁斯的精心调查!我会看看你是否建议实施对我有用并回复!
    • 所以我更改了代码以使用您建议的 FileDataStore,现在一切正常。感谢您的帮助!
    猜你喜欢
    • 2011-04-27
    • 2017-04-11
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    • 2021-12-13
    • 2016-05-20
    • 2016-05-18
    • 2021-06-20
    相关资源
    最近更新 更多