为了账号安全,请及时绑定邮箱和手机立即绑定

添加 Google API 离线访问 .NET Core 应用程序

添加 Google API 离线访问 .NET Core 应用程序

C#
达令说 2023-09-16 17:40:53
我编写了一个 ASP.NET Core Web 应用程序,它使用 Auth0 作为用户的主要授权机制,它中间人是一大堆外部身份验证端点,例如 Google 和 Facebook。这工作得很好,我在那里没有任何问题。该网络应用程序的核心利用 Google Analytics 来执行自己的分析和业务逻辑。我的网络应用程序正在分析的 Google Analytics 帐户可能并且很可能与用户自己的 Google 帐户不同。需要明确的是,我的意思是,用户很可能会使用他们希望的任何登录提供商登录,然后他们将附加一个特定的 Google 企业帐户,可以访问其企业的 Google Analytics 系统。Web 应用程序在用户登录和离线时执行分析。因此,我始终将用户身份验证 (Auth0) 步骤与 Analytics 帐户步骤的身份验证分开。大致流程如下:用户使用任何提供商(Google、Facebook、电子邮件/通行证)通过 Auth0 登录并访问私人仪表板。用户设置一个“公司”并单击一个按钮来授权我们的网络应用程序访问带有 Analytics 的特定 Google 帐户。用户将被重定向回私人仪表板,并且 Google 帐户的刷新令牌将被存储以供将来使用。之前我也通过 Auth0 推送 Analytics 身份验证,并且使用缓存的 Auth0 刷新令牌来离线工作。然而,它会在几天后过期,并且 Auth0 似乎不提供长期离线访问。因此,我认为最简单的方法就是不使用 auth0 进行 Analytics 身份验证步骤,直接使用 Google API 进行身份验证并长期存储 Google 刷新令牌。但是我找不到任何具体的例子来说明如何实现这一目标!
查看完整描述

1 回答

?
冉冉说

TA贡献1877条经验 获得超1个赞

我终于破解了!我最终扔掉了所有库,发现使用普通的旧 REST API 最简单。对于那些好奇的人来说,下面的代码示例:


用户的浏览器获取以下内容并重定向到 Google 以获取身份验证令牌:


public IActionResult OnGet([FromQuery]int id, [FromQuery]string returnAction)

{

    var org = context.Organizations.Include(o => o.UserOrgs).First(o => o.Id == id);

    var user = GetUser();


    if (!IsUserMemberOfOrg(user, org)) return BadRequest("User is not a member of this organization!");


    var redirectUri = Uri.EscapeUriString(GetBaseUri()+"dash/auth/google?handler=ReturnCode");

    var uri = $"https://accounts.google.com/o/oauth2/v2/auth?"+

            $"scope={Uri.EscapeUriString("https://www.googleapis.com/auth/analytics.readonly")}"+

            $"&prompt=consent"+

            $"&access_type=offline"+

            //$"&include_granted_scopes=true"+

            $"&state={Uri.EscapeUriString(JsonConvert.SerializeObject(new AuthState() { OrgId = id, ReturnAction = returnAction }))}"+

            $"&redirect_uri={redirectUri}"+

            $"&response_type=code"+

            $"&client_id={_configuration["Authentication:Google:ClientId"]}";


    return Redirect(uri);

}

Google 重定向回以下内容,此时我从网络服务器到 Google API 执行 POST,以将身份验证令牌交换为刷新令牌并将其存储以供以后使用:


public async Task<IActionResult> OnGetReturnCode([FromQuery]string state, [FromQuery]string code, [FromQuery]string scope)

{

    var authState = JsonConvert.DeserializeObject<AuthState>(state);


    var id = authState.OrgId;

    var returnAction = authState.ReturnAction;


    var org = await context.Organizations.Include(o => o.UserOrgs).SingleOrDefaultAsync(o => o.Id == id);

    if (org == null) return BadRequest("This Org doesn't exist!");

    using (var httpClient = new HttpClient())

    {

        var redirectUri = Uri.EscapeUriString(GetBaseUri()+"dash/auth/google?handler=ReturnCode");


        var dict = new Dictionary<string, string>

        {

            { "code", code },

            { "client_id", _configuration["Authentication:Google:ClientId"] },

            { "client_secret", _configuration["Authentication:Google:ClientSecret"] },

            { "redirect_uri", redirectUri },

            { "grant_type", "authorization_code" }

        };


        var content = new FormUrlEncodedContent(dict);

        var response = await httpClient.PostAsync("https://www.googleapis.com/oauth2/v4/token", content);


        var resultContent = JsonConvert.DeserializeObject<GoogleRefreshTokenPostResponse>(await response.Content.ReadAsStringAsync());


        org.GoogleAuthRefreshToken = resultContent.refresh_token;

        await context.SaveChangesAsync();


        return Redirect($"{authState.ReturnAction}/{authState.OrgId}");

    }

}

最后,我们可以稍后使用刷新令牌获取新的访问令牌,而无需用户干预:


public async Task<string> GetGoogleAccessToken(Organization org)

{

    if(string.IsNullOrEmpty(org.GoogleAuthRefreshToken))

    {

        throw new Exception("No refresh token found. " +

            "Please visit the organization settings page" +

            " to setup your Google account.");

    }


    using (var httpClient = new HttpClient())

    {

        var dict = new Dictionary<string, string>

        {

            { "client_id", _configuration["Authentication:Google:ClientId"] },

            { "client_secret", _configuration["Authentication:Google:ClientSecret"] },

            { "refresh_token", org.GoogleAuthRefreshToken },

            { "grant_type", "refresh_token" }

        };

        var resp = await httpClient.PostAsync("https://www.googleapis.com/oauth2/v4/token", 

            new FormUrlEncodedContent(dict));


        if (resp.IsSuccessStatusCode)

        {

            dynamic returnContent = JObject.Parse(await resp.Content.ReadAsStringAsync());

            return returnContent.access_token;

        } else

        {

            throw new Exception(resp.ReasonPhrase);

        }

    }

}


查看完整回答
反对 回复 2023-09-16
  • 1 回答
  • 0 关注
  • 64 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信