【问题标题】: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 Core 和 Google external login setup in ASP.NET Core 创建了一个带有 Google 身份验证的 ASP.NET Core Web 应用程序来检查这个问题。

    我还关注.NET console application to access the Google Calendar API 和Calendar.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 应用之前,我将用于构造FileDataStore 的folder 参数更改为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_token、refresh_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
    相关资源
    最近更新 更多