使用 LWP::Authen::OAuth2 访问受 OAuth2 保护的 Google API 时出现问题

Problem accessing Google API protected with OAuth2 by using LWP::Authen::OAuth2

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

由于 FCM 是 Google API 系列的一部分,因此需要 OAuth2 令牌才能访问 API。在我的研究过程中,我发现 this perl solution. Because my service is running on an non-Google server environment, I can't use Google Application Default Credentials, but have to provide them manually, so I downloaded a json containing a private key following this 描述。

阅读 documentation of 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 "https://www.googleapis.com/auth/firebase.messaging" 的相应身份验证 API 仍然接受 Bearer 令牌...我尝试生成我的 JWT,这似乎是成功的,但是我发送给实际 FCM 的请求 API 然后给了我这个:

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 仍然支持不记名令牌,即使它们在引用 docs page 的示例中使用。有没有人有关于那个的最新信息?非常感谢!

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

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

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

当创建一个新的 $oauth2 对象,并为字段 service_provider 赋值时,模块 LWP::Authen::OAuth2::ServiceProvider 的对象被创建,可能还需要它的参数,比如scope,用于定义您希望获得授权的 API 家庭,并根据该 client_type.

现在 Google 不是无名服务提供者,因此已经有一个预构建的 ServiceProvider 模块,明确地为 Google APIs : LWP::Authen::OAuth2::ServiceProvider::Google. This automatically fills in some parameters of a ServiceProvider object, and holds a hash of available client_types to make sure you use one of them, because depending on the client_type a specific sub-module of ServiceProvider::Google 被创建内部。

所以我尝试这样测试:

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"
);

根据进一步的描述 here and sending a request with the built-in LWP::UserAgent object I still got an 401 error UNAUTHENTICATED which confused me a lot. So when I read the documentation the I-don't-know-what time I stepped across this tiny line in the client_types chapter of ServiceProvider::Google:

Service Account

This client_type is for applications that login to the developer's account using the developer's credentials. See https://developers.google.com/accounts/docs/OAuth2ServiceAccount for Google's documentation.

This is not yet supported, and would require the use of JSON Web Tokens to support.

是的,这有点让整个 LWP:Authen::OAuth 家族无法访问 Firebase API,因为几乎所有人都有 client_type => "service_account"。现在我的项目有一个截止日期,我等不及模块得到扩展,否则我自己扩展它会超出我的 perl 技能。

但在绝望的泪水之间总有一线希望,因为我发现 this Google docs page with a live-saving addendum, that it's possible to use a JSON Web Token as a Bearer token. Researching for that I found more than one Perl solution to generate JWT from JSONs like a service account, and also this 非常有用的 Whosebug 答案为我指明了走出瓶颈的出路。