我跟随 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。