【问题标题】:Google API - Reply to Reviews (androidpublisher) Error 401Google API - 回复评论 (androidpublisher) 错误 401
【发布时间】:2021-11-22 17:31:23
【问题描述】:

我正在尝试从我构建的 Swift 应用程序连接到 Google 的 Reply to Reviews API,该 API 是 https://www.googleapis.com/auth/androidpublisher 范围的一部分,以获取特定应用程序评论的列表。我创建了一个服务帐户并设法创建了一个 JWT 令牌,然后尝试向 API 发出 GET 请求,但出现错误:

{
  "error": {
    "code": 401,
    "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
    "status": "UNAUTHENTICATED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "CREDENTIALS_MISSING",
        "domain": "googleapis.com",
        "metadata": {
          "service": "androidpublisher.googleapis.com",
          "method": "google.play.publishingapi.v3.ReviewsService.List"
        }
      }
    ]
  }
}

这是请求网址:

https://www.googleapis.com/androidpublisher/v3/applications/<APP_PACKAGE>/reviews?access_token=<JWT-TOKEN>

这是我的请求代码:

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    
    let auth = Authentication()
    let jwt = auth.generateJWT()
    
    self.retriveReviews(packageIdentifier: "<APP_PACKAGE>", auth: jwt!)
}


func retriveReviews(packageIdentifier: String, auth: String) {
    let url = URL(string: "https://www.googleapis.com/androidpublisher/v3/applications/\(packageIdentifier)/reviews?access_token=\(auth)")
    print("Request URL: \(url)")
    var request = URLRequest(url: url!)
    request.httpMethod = "GET"
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data else { return }
        print(String(data: data, encoding: .utf8)!)
    }

    task.resume()
}

JWT 令牌:

import JWTKit
import UIKit

class Authentication: NSObject {

    func generateJWT() -> String? {
            struct Header: JWTPayload {
                enum CodingKeys: String, CodingKey {
                    case alg = "alg"
                    case type = "typ"
                }
                
            var alg = "RS256"
            var type: String = "JWT"
            
            func verify(using signer: JWTSigner) throws {
                //            print(self.expireTime > Date().timeIntervalSince1970)
                fatalError()
            }
        }
        
        struct Payload: JWTPayload {
            enum CodingKeys: String, CodingKey {
                case email = "iss"
                case scope = "scope"
                case aud = "aud"
                case createdAt = "iat"
                case expireTime = "exp"
            }
            
            var email: String = "XXXXX@XXXXXX.iam.gserviceaccount.com"
            var scope: String = "https://www.googleapis.com/auth/androidpublisher"
            var aud: String = "https://oauth2.googleapis.com/token"
            var createdAt: Double = Date().timeIntervalSince1970
            var expireTime: Double = Date().advanced(by: 1000).timeIntervalSince1970
            
            
            func verify(using signer: JWTSigner) throws {
                print(self.expireTime > Date().timeIntervalSince1970)
                fatalError()
            }
        }
        
        do {
            if let certificatePath = Bundle.main.path(forResource: "k_created", ofType: "pem") {
                let certificateUrl = URL(fileURLWithPath: certificatePath)
                let certififcateData = try Data(contentsOf: certificateUrl)
                let signers = JWTSigners()
                let key = try RSAKey.private(pem: certififcateData)
                signers.use(.rs256(key: key))
                
                //                MARK: HEADER NOT USED
                let header = Header()
                let payload = Payload()
                let jwt = try signers.sign(payload)
                //                let jwt = try signers.sign(payload)
                print("JWT: \(jwt)")
                return jwt
            } else {
                return nil
            }
        } catch {
            print(error)
            return nil
        }
    }
}

我做错了什么?或者也许我缺少一个步骤?

【问题讨论】:

  • 我可以看到您用于使用服务帐户凭据请求访问令牌的代码吗? #justcurious
  • 我已经更新了我的问题,看看:)

标签: ios swift google-api jwt androidpublisher


【解决方案1】:

您似乎试图将访问令牌内联作为查询参数发送。您需要将其作为授权标头发送。

curl \
  'https://androidpublisher.googleapis.com/androidpublisher/v3/applications/[PACKAGENAME]/reviews' \
  --header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
  --header 'Accept: application/json' \
  --compressed

问题是你正在做 access_token=\(auth) 你应该做的地方

request.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")

授权需要在如上所示的标头中,并且需要是有效的访问令牌。

您是否针对验证端点运行了您正在创建的 JWT,以确保其有效。如果您的代码有效,那么您是我 10 年来第一个在不使用客户端库的情况下设法从服务帐户凭据创建访问令牌的人。干得好。

【讨论】:

  • 我在哪里可以获得在 url 中发送的YOUR API KEY?我创建了一个服务帐户和一个 API-KEY,我将 Authorization 标头设置为 Bearer YOUR-ACCESS-TOKEN、Accept 和 Content-Type,但仍然得到相同的响应。
  • API 密钥用于访问公共数据。如果您使用的是服务帐户,则不需要 api 密钥。如果您使用的是服务帐户,则需要使用该帐户来请求访问令牌。
  • 好的,我明白了,但是我的请求缺少什么?因为我拿到了一个JWT token,用这个token调用了api,还是报同样的401错误。
  • retriveReviews(packageIdentifier: String, auth: String) 需要在查询参数中添加授权标头
猜你喜欢
  • 1970-01-01
  • 2017-09-12
  • 2014-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-09
  • 2022-11-13
  • 1970-01-01
相关资源
最近更新 更多