首页 > 解决方案 > 使用 LWP::Authen::OAuth2 访问受 OAuth2 保护的 Google API 时出现问题

问题描述

我目前在 perl 服务器上编写服务,该服务将向 Firebase Cloud Messaging API 发送请求,然后将推送通知发送到应用程序实例。

由于 FCM 是 Google API 系列的一部分,因此需要 OAuth2 令牌才能访问 API。在我的研究中,我发现了这个perl 解决方案。因为我的服务是在非谷歌服务器环境上运行的,所以我不能使用谷歌应用程序默认凭据,而是必须手动提供它们,所以我下载了一个包含私钥的 json,遵循这个描述。

阅读LWP::Authen::OAuth2 的文档时,我有点困惑,将 json 中的哪个参数放入$oauth2对象中,因为通常使用不同的名称来引用相同的值,就像我怀疑的那样。

与我的firebase项目相关的json:

{
    "type": "service_account",
    "project_id": "my_project_id",
    "private_key_id": "some_key_id",
    "private_key": "-----BEGIN PRIVATE KEY-----very_long_key-----END PRIVATE KEY-----\n",
    "client_email": "firebase-adminsdk-o8sf4@<my_project_id>.iam.gserviceaccount.com",
    "client_id": "some_client_id",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-o8sf4%40<my_project_id>.iam.gserviceaccount.com"
}

对象的实现$oauth如下所示:

my $oauth2 = LWP::Authen::OAuth2->new(
             client_id => "Public from service provider",
             #probably that will be "some_client_id" from above

             client_secret => "s3cr3t fr0m svc prov",
             #the "very_long_key"?

             service_provider => "Google",
             #the "auth_uri"? That's what I would suggest here
             #I've read some about the LWP::Authen::OAuth2::ServiceProvider module 
             #do I have to create an instance of that here?
             #if so, which params do I need for that from the json?

             redirect_uri => "https://your.url.com/",
             #the FCM api I want to call?

             # Optional hook, but recommended.
             save_tokens => \&save_tokens,
             save_tokens_args => [ $dbh ],

             # This is for when you have tokens from last time.
             token_string => $token_string.

             #yes, i copy-pasted that from the docs
         );

现在,作为 Perl 的初学者和不喜欢模棱两可的键值名称的人,我有点困惑,将哪个值放在哪里,如果有人可以在这里为我提供指南,我会很高兴,即使这个应该放在哪里似乎是一个非常菜鸟的问题,这对我来说很重要:D。所以我感谢每一个有用的答案!

编辑

当尝试使用Crypt::JWT在我的 perl 服务中手动生成JSON Web 令牌时,我遇到了另一个绊脚石,这让我怀疑来自 Google 的相应身份验证 API 是否仍然接受 Bearer 令牌......我尝试生成我的 JWT ,这似乎是成功的,但是我发送到实际 FCM API 的请求然后给了我这个:"https://www.googleapis.com/auth/firebase.messaging"

Request had invalid authentication credentials. 
Expected OAuth 2 access token, login cookie 
or other valid authentication credential

在打印为 String 的响应中,我找到了这个小家伙,这让我很困惑:

Client-Warning: Unsupported authentication scheme 'bearer'

现在我非常不确定,FCM API 仍然支持承载令牌,即使它们在引用文档页面的示例中使用。有没有人有这方面的最新信息?非常感谢你!

标签: perloauth-2.0google-apifirebase-cloud-messaginglwp

解决方案


深入研究各种文档并测试我意识到的一些事情,LWP::Authen::OAuth2 不仅有点开销,对于创建对 OAuth2 保护的 API 的非常小的 HTTPS 请求,目前也是不可能的。

警告隐藏在service_provider托管身份验证 API 的 中,我必须调用它来验证和授权我的服务以访问实际的 Firecase 云消息传递 API。就我而言,这是谷歌,更准确地说,在代码中https://www.googleapis.com/auth/firebase.messaging也称为。scope

现在,一个服务提供商可以为不同的客户端提供不同的身份验证 API。这就是为什么一些服务提供商需要一个额外的参数 - client_typeor type(为了明确我将使用client_type) - 这也会影响通过 OAuth2 的身份验证和授权过程。

当创建一个新的$oauth2-object 并为该字段分配一个值时service_provider,模块LWP::Authen::OAuth2::ServiceProvider的对象被创建,这可能还需要它的参数,如scope, 来定义你想要的 API 系列获得授权并取决于该 a client_type.

现在谷歌不是一个无名的服务提供商,所以已经有一个预构建的服务提供商模块,明确地用于谷歌 API:LWP::Authen::OAuth2::ServiceProvider::Google。这会自动填充ServiceProvider对象的一些参数,并保存可用的哈希值client_types以确保您使用其中一个,因为取决于ServiceProvider::Googleclient_type的特定子模块在内部创建。

所以我试着像这样测试:

my $oauth2 = LWP::Authen::OAuth2->new(
    service_provider => "Google",
    scope => "https://www.googleapis.com/auth/firebase.messaging", 
    client_type => "service_account", #referring to 'type' in the json 
    client_id => "Public from service provider",
    client_secret => "s3cr3t fr0m svc prov",
    redirect_uri => "this-server.mydomain.com"
);

按照此处的进一步描述并使用内置 LWP::UserAgent 对象发送请求,我仍然收到 401 错误UNAUTHENTICATED,这让我很困惑。因此,当我阅读文档时,我不知道什么时候在ServiceProvider::Googleclient_types的章节中跨过这条小线:

服务帐号

此 client_type 适用于使用开发人员凭据登录到开发人员帐户的应用程序。有关 Google 的文档,请参阅https://developers.google.com/accounts/docs/OAuth2ServiceAccount

这尚不支持,需要使用 JSON Web Tokens 来支持。

是的,这有点破坏了整个LWP:Authen::OAuth系列访问 Firebase API 的使用,因为几乎所有这些 API 都具有client_type => "service_account". 现在我的项目有一个截止日期,我迫不及待地想要扩展模块,或者我自己扩展它会超出我的 perl 技能。

但在绝望的泪水之间总有一丝希望,因为我发现这个带有拯救生命的附录的 Google 文档页面,可以使用JSON Web 令牌作为不记名令牌。为此,我发现不止一个 Perl 解决方案可以像服务帐户一样从 JSON 生成 JWT,还有这个非常有用的 Stackoverflow 答案,它向我展示了摆脱瓶颈的方法。


推荐阅读