如何使用R连接到哥白尼陆地监测服务API

问题描述 投票:0回答:1

我正在尝试连接到 CLMS API,以便使用 R 下载数据。我已经创建了一个令牌,并将其保存在文件中

land-copernicus.json

这是我的连接尝试:

## Connection
#   1. The information about the connection using the CLMS API can be found here:
#       https://eea.github.io/clms-api-docs/authentication.html
#   2. The use of API authentification flow involves four steps:
#        2.1 Get a service key online
#        2.2 R uses the private key to create and sign a JWT authorisation grant
#        2.3 R exchanges the JWT authorisation grant for a short-lived access token at the @@oauth2-token endpoint
#        2.4 The client then uses this access token to authenticate requests to protected resources
#

library(httr2)

## Read the keys created at step 2.1
service_key = rjson::fromJSON(file = "./land-copernicus.json")
private_key = service_key[["private_key"]]

## Create the authorisation grant from private key
claim = jwt_claim(
    iss = service_key[["client_id"]],
    sub = service_key[["user_id"]],
    aud = service_key[["token_uri"]],
    iat = Sys.time(),
    exp = Sys.time() + 60*60 # Gives an expiration date of one hour (the maximum authorised)
)

grant = jwt_encode_hmac(claim = claim, secret = private_key)
# grant = jwt_encode_hmac(claim = claim, secret = private_key, size = 256, header = list(alg = "RS256"))

## Exchange the JWT authorization grant at the @@oauth2-token endpoint
req = request(base_url = service_key[["token_uri"]]) |>
    req_headers("Accept" = "application/json", "Content-Type" = "application/x-www-form-urlencoded") |>
    req_body_json(data = list(grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer", assertion = grant)) |>
    req_perform(verbosity = 3)

我收到以下错误:

*  Uses proxy env variable no_proxy == 'localhost,HP2011P079.ign.fr'
*  Uses proxy env variable https_proxy == 'http://proxy.ign.fr:3128/'
*    Trying 10.128.81.101:3128...
*  Connected to (nil) (10.128.81.101) port 3128 (#2)
*  allocate connect buffer!
*  Establish HTTP proxy tunnel to land.copernicus.eu:443
-> CONNECT land.copernicus.eu: 443 HTTP/1.1
-> Host: land.copernicus.eu:443
-> User-Agent: httr2/1.0.5 r-curl/5.2.3 libcurl/7.81.0
-> Proxy-Connection: Keep-Alive
-> 
<- HTTP/1.1 200 Connection established
<- 
*  Proxy replied 200 to CONNECT request
*  CONNECT phase completed!
*  ALPN, offering h2
*  ALPN, offering http/1.1
*   CAfile: /etc/ssl/certs/ca-certificates.crt
*   CApath: /etc/ssl/certs
*  TLSv1.0 (OUT), TLS header, Certificate Status (22):
*  TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  TLSv1.2 (IN), TLS header, Certificate Status (22):
*  TLSv1.3 (IN), TLS handshake, Server hello (2):
*  TLSv1.2 (IN), TLS header, Certificate Status (22):
*  TLSv1.2 (IN), TLS handshake, Certificate (11):
*  TLSv1.2 (IN), TLS header, Certificate Status (22):
*  TLSv1.2 (IN), TLS handshake, Server key exchange (12):
*  TLSv1.2 (IN), TLS header, Certificate Status (22):
*  TLSv1.2 (IN), TLS handshake, Server finished (14):
*  TLSv1.2 (OUT), TLS header, Certificate Status (22):
*  TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
*  TLSv1.2 (OUT), TLS header, Finished (20):
*  TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
*  TLSv1.2 (OUT), TLS header, Certificate Status (22):
*  TLSv1.2 (OUT), TLS handshake, Finished (20):
*  TLSv1.2 (IN), TLS header, Finished (20):
*  TLSv1.2 (IN), TLS header, Certificate Status (22):
*  TLSv1.2 (IN), TLS handshake, Finished (20):
*  SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
*  ALPN, server accepted to use h2
*  Server certificate:
*   subject: CN=land.copernicus.eu
*   start date: Oct  4 10:29:01 2024 GMT
*   expire date: Jan  2 10:29:00 2025 GMT
*   subjectAltName: host "land.copernicus.eu" matched cert's "land.copernicus.eu"
*   issuer: C=US; O=Let's Encrypt; CN=R10
*   SSL certificate verify ok.
*  Using HTTP2, server supports multiplexing
*  Connection state changed (HTTP/2 confirmed)
*  Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
*  TLSv1.2 (OUT), TLS header, Supplemental data (23):
*  TLSv1.2 (OUT), TLS header, Supplemental data (23):
*  TLSv1.2 (OUT), TLS header, Supplemental data (23):
*  Using Stream ID: 1 (easy handle 0x5d9ca6076e20)
*  TLSv1.2 (OUT), TLS header, Supplemental data (23):
-> POST /@@oauth2-token HTTP/2
-> Host: land.copernicus.eu
-> user-agent: httr2/1.0.5 r-curl/5.2.3 libcurl/7.81.0
-> accept-encoding: deflate, gzip, br, zstd
-> accept: application/json
-> content-type: application/x-www-form-urlencoded
-> content-length: 461
-> 
*  TLSv1.2 (OUT), TLS header, Supplemental data (23):
>> {"grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer","assertion":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImFsZy4xIjoiUlMyNTYifQ.eyJpc3MiOiIyYjhlMTdmYzBkOTg4YzVkNGY2OTg1ZTMyYjdmY2VkNCIsInN1YiI6Im4wMGg3ZGthIiwiYXVkIjoiaHR0cHM6Ly9sYW5kLmNvcGVybmljdXMuZXUvQEBvYXV0aDItdG9rZW4iLCJleHAiOjE3Mjg0Njk4NDcsIm5iZiI6MTcyODQ2NjI0NywiaWF0IjoxNzI4NDY2MjQ3LCJqdGkiOiJZcEhTSmpOSk0zeE85SFdTbVk1NFpqS0J6QlROdUtWUEtLNGRsZ05oSmtVIn0.1opDHqRqVMuKgojnUvgf1DTOtBAY3fLQB6AGr1C0jYY"}
*  We are completely uploaded and fine
*  TLSv1.2 (IN), TLS header, Supplemental data (23):
*  TLSv1.2 (OUT), TLS header, Supplemental data (23):
*  TLSv1.2 (IN), TLS header, Supplemental data (23):
*  TLSv1.2 (IN), TLS header, Supplemental data (23):
*  TLSv1.2 (IN), TLS header, Supplemental data (23):
<- HTTP/2 400 
<- cache-control: no-store
<- content-length: 73
<- content-type: application/json
<- date: Wed, 09 Oct 2024 09:59:41 GMT
<- pragma: no-cache
<- server: waitress
<- via: waitress
<- x-frame-options: SAMEORIGIN
<- x-powered-by: Zope (www.zope.dev), Python (www.python.org)
<- strict-transport-security: max-age=31536000; includeSubDomains; preload
<- x-content-type-options: nosniff
<- x-xss-protection: 1; mode=block
<- access-control-allow-headers: Authorization, Content-Type, content-type
<- vary: Origin
<- 
<< {"error": "invalid_request", "error_description": "Missing 'grant_type'"}
*  Connection #2 to host (nil) left intact
Error in `req_perform()`:
! HTTP 400 Bad Request.

看来错误在于 {"error": "invalid_request", "error_description": "Missing 'grant_type'"} 我不明白,因为我提供了

grant_type

如果有帮助的话,我的代码的灵感来自于一个jupyter笔记本,可以在这里

找到

我还尝试了以下请求,其中我指定了算法:

grant = jwt_encode_hmac(claim = claim, secret = private_key, size = 256, header = list(alg = "RS256"))

req2 = request(base_url = service_key[["token_uri"]]) |>
    req_headers("Accept" = "application/json", "Content-Type" = "application/x-www-form-urlencoded") |>
    req_body_raw(body = paste0("grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=", grant)) |>
    req_perform(verbosity = 3)

并收到错误{“error”:“invalid_request”,“error_description”:“仅支持RS256签名算法”}。我再次不明白为什么,因为我提供了算法

RS256
(更多信息这里)。

-------------- 编辑 ---------------

当我使用以下代码将 python 生成的授权复制粘贴到 R 中时:

import json
import jwt
import time
import requests

base_url = 'https://land.copernicus.eu'

# Load saved key from filesystem
service_key = json.load(open('./land-copernicus.json', 'rb'))

private_key = service_key['private_key'].encode('utf-8')

claim_set = {
    "iss": service_key['client_id'],
    "sub": service_key['user_id'],
    "aud": service_key['token_uri'],
    "iat": int(time.time()),
    "exp": int(time.time() + (60 * 60)),
}
grant = jwt.encode(claim_set, private_key, algorithm='RS256')

然后就可以了。因此,我推断问题来自

httr2::jwt_encode_hmac
,因为服务和私钥以及声明是相同的

r rest httr rjson
1个回答
0
投票

您需要使用

jwt_encode_sig
而不是
jwt_encode_hmac
,并遵循 他们的文档。这有效:

library(httr2)

## JSON file from website 
## See: https://eea.github.io/clms-api-docs/authentication.html#create-api-tokens
service_key <- jsonlite::read_json("~/Desktop/my_saved_key.json")

claim <-  jwt_claim(
  iss = service_key$client_id,
  sub = service_key$user_id,
  aud = service_key$token_uri,
  nbf = NULL)

## Sign the authorisation grant using private key
grant <- jose::jwt_encode_sig(claim, service_key$private_key)
req <- request(base_url = service_key$token_uri) |>
  req_body_form(grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer", assertion = grant) |>
  req_perform(verbosity = 2)

resp_body_json(req)
© www.soinside.com 2019 - 2024. All rights reserved.