【问题标题】:Google API OAuth2 : “invalid_grant” error when requesting token for Service Account with PerlGoogle API OAuth2:使用 Perl 为服务帐户请求令牌时出现“invalid_grant”错误
【发布时间】:2018-11-18 12:41:11
【问题描述】:

从开发者控制台获取服务帐户的凭据

首先,我将 p12 私钥转换为 PEM:

openssl pkcs12 -in <private key for Service Account>.p12 -out calendar.key -nocerts -nodes

然后我运行:

use MIME::Base64;
use Crypt::OpenSSL::RSA;
use File::Slurp;

my $header = encode_base64('{"alg":"RS256","typ":"JWT"}','');
my $claim = encode_base64('{
"iss":"<mail for the Service Account>",
"scope":"https://www.googleapis.com/auth/calendar",
"aud":"https://accounts.google.com/o/oauth2/token",
"exp":'.(time()+3600).',
"iat":'.time().'
}','');

my $key = read_file('calendar.key');
my $rsa = Crypt::OpenSSL::RSA->new_private_key($key);
$rsa->use_sha256_hash;
$rsa->use_pkcs1_padding;
my $signature = encode_base64($rsa->sign($header . '.' . $claim), '');

my $token_request = $header . '.' . $claim . '.' . $signature;

print `curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=$token_request' https://accounts.google.com/o/oauth2/token`;

我明白了

{
  "error" : "invalid_grant"
}

我用 NTP 同步了系统时间,没有帮助。

【问题讨论】:

    标签: perl oauth-2.0 google-api google-calendar-api google-oauth


    【解决方案1】:

    我不知道为什么,但问题出在curl

    我把它换成了WWW::Mechanize:

    my $mech = WWW::Mechanize->new( autocheck => 1 );
    $mech->post('https://accounts.google.com/o/oauth2/token',
        'Content-Type' => 'application/x-www-form-urlencoded',
        'Content' => [
            'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
            'assertion' => $token_request,
        ],
    );
    

    它有效。

    【讨论】:

      【解决方案2】:

      以防万一有人来这里寻找从当前提供的 JSON 文件中的 Google 服务帐户凭据获取不记名令牌的方法;在我无法使用 Crypt::JWT 或 Mojo::JWT::Google 之后,这是一个对我有用的 sn-p。

      use LWP::UserAgent;
      use JSON;
      use  Mojo::JWT;
      use Mojo::File;
      
      my $jwt = create_jwt_from_path_and_scopes( 'path/to/credentials.json', 'email https://www.googleapis.com/auth/compute' );
      my $ua  = LWP::UserAgent->new();
      
      my $response = $ua->post('https://www.googleapis.com/oauth2/v4/token', 
                      {   'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 
                          'assertion'  =>  $jwt 
                      }
                  ); 
      
      #######################################################################
      sub create_jwt_from_path_and_scopes
      {
        my ( $path, $scope ) = @_;
        croak("No path provided")        if not defined $path;
        croak("$path no available")      if not -f $path;
        my $json = decode_json( Mojo::File->new($path)->slurp );
        croak("No Private key in $path") if not defined $json->{private_key};
        croak("Not a service account")   if $json->{type} ne 'service_account';
        my $jwt = Mojo::JWT->new();
        $jwt->algorithm('RS256');
        $jwt->secret($json->{private_key});
      
        $jwt->claims( {
            iss   => $json->{client_email},
            scope => $scope,
            aud   => 'https://www.googleapis.com/oauth2/v4/token',   
            iat   => time(),
            exp   => time()+3600   
        } );
        $jwt->set_iat( 1 );
        return $jwt->encode;
      }
      #######################################################################
      

      【讨论】:

      • 我遇到了类似的问题。您不能使用 Crypt::JWT 的确切原因是什么?我不断收到一个奇怪的依赖错误,说找不到可加载的 CryptX。
      • 我可以给你 100 分......即使我没有寻找相同的答案......无论如何只是一个旁注 - 可以在 bash 中生成密钥,如下所示:jwt_private_key_file=~/.ssh/jwtRS256.key; jwt_public_key_file=~/.ssh/jwtRS256.key.pub; ssh-keygen -t rsa -b 4096 -m PEM -f $jwt_private_key_file ; openssl rsa -in $jwt_private_key_file -pubout -outform PEM -out $jwt_public_key_file
      【解决方案3】:

      将您的 curl 命令更改为:

      curl -H "Content-Type: application/x-www-form-urlencoded" \
           -d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
           -d assertion=$token_request \
            https://accounts.google.com/oauth2/token
      

      【讨论】:

        【解决方案4】:

        尝试使用 BASE64URL 代替 BASE64。 JWT/JWS/JWE 规范使用 BASE64URL。

        MIME::Base64 ---> MIME::Base64::URLSafe
        encode_base64 ---> urlsafe_b64encode
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-22
          • 1970-01-01
          • 2013-09-10
          • 2021-09-11
          • 1970-01-01
          相关资源
          最近更新 更多