使用Hasura将IBM云数据库(IBM Cloud Databases)的PostgreSQL进行GraphQL化,并使用App ID进行保护

为何

Hasura是一种可以将数据库简单地转换为GraphQL API的软件。

这次我们将尝试将IBM Cloud的Databases for PostgreSQL转化为GraphQL,并使用App ID保护API。

环境

image.png

Hasura将部署在Red Hat OpenShift on IBM Cloud(ROKS)上。Hasura将使用Databases for PostgreSQL。预期有两种角色,管理员可以直接在浏览器中操作Hasura的控制台。用户可以是调用API的人或者应用程序,并使用从App ID获取的令牌进行API授权。

程序

确认PostgreSQL连接信息

请假设实例已经创建完成。请验证凭证并查找下一个值。

      "composed": [
        "postgres://********:********@********.databases.appdomain.cloud:32497/ibmclouddb?sslmode=verify-full"
      ],

如果PostgreSQL启用了私有端点,请在主机名部分的”database”之前加上”private”。由于这个PostgreSQL是云服务,为了能够应对意外的网络错误,我们将设置connect_timeout为30秒。本次使用以下值。

postgres://********:********@********.private.databases.appdomain.cloud:32497/ibmclouddb?sslmode=verify-full&connect_timeout=30

获取PostgreSQL SSL通信所需的CA证书

同样地,我们将从相同的凭证中寻找下一个值。

        "certificate_base64": "********"

这是SSL通信时的CA证书。我们将对这个值进行解密并保存。

$ echo -n "********" | base64 -d >root.crt

请部署Hasura

由於Hasura有官方提供的容器版本,因此我們將使用它。

$ oc project qiita 2>/dev/null || oc new-project qiita
$ oc create deploy graphql-engine --image hasura/graphql-engine:v2.0.9 --replicas 0
$ oc set env deploy/graphql-engine HASURA_GRAPHQL_ADMIN_SECRET=P@ssw0rd 
$ oc set env deploy/graphql-engine HASURA_GRAPHQL_ENABLE_CONSOLE=true
$ oc set env deploy/graphql-engine HASURA_GRAPHQL_DATABASE_URL=postgres://********:********@********.private.databases.appdomain.cloud:32497/ibmclouddb?sslmode=verify-full\&connect_timeout=30
$ oc create secret generic postgresql-ca --from-file root.crt
$ oc set volume deploy/graphql-engine --add --mount-path /.postgresql/ --secret-name postgresql-ca
$ oc scale deploy/graphql-engine --replicas 1
$ oc get pods | grep graphql-engine
$ oc expose deploy/graphql-engine --port 8080
$ oc create route edge --service graphql-engine

我将解释一些要点。

設置環境變數HASURA_GRAPHQL_ADMIN_SECRET,可以在使用控制台時進行身份驗證。換句話說,如果不設置這個環境變數,控制台將無需身份驗證即可使用,請注意。

如果将CA证书作为名为.postgresql/root.crt的文件进行挂载,Hasura的PostgreSQL驱动程序将自动使用它。

对控制台的访问

我会确认路径。

$ oc get route graphql-engine
NAME             HOST/PORT                                                                                                      PATH   SERVICES         PORT    TERMINATION   WILDCARD
graphql-engine   graphql-engine-qiita.roks-public-tok-********************************-0000.jp-tok.containers.appdomain.cloud          graphql-engine   <all>   edge          None

您可以使用浏览器访问上述URL(https://)。您可以使用之前在环境变量HASURA_GRAPHQL_ADMIN_SECRET中指定的值进行登录。

image.png
image.png

教程

有公式课程提供教材,对于初次接触者非常有参考价值。

本次我們將按照此假設進行。在目前的狀態下,您可以從”基本的資料模型”到”審核”進行實施。

image.png

应用程序ID的设置

在本次教程中,我们使用App ID来进行身份验证,而不是Auth0。通过使用由App ID进行认证和生成的JWT,来进行Hasura的授权机制。

请参考Hasura官方指南,了解有关JWT的信息。

应用程序的注册

从应用程序ID的管理界面添加应用程序。

image.png

类型将设置为常规的Web应用程序。

image.png

我会记下clientId、tenantId、secret。

image.png

注册API调用用户

下一步是添加用户。这次我们将使用云目录。

image.png

保存用户名称(电子邮箱)和密码。

image.png

选择先前注册的用户的用户配置文件。

image.png

编辑自定义属性。

image.png

我会按照以下方式进行设置。简单解释一下,https://hasura.io/jwt/claims 是 Hasura 在验证 JWT 时的命名空间。x-hasura-allowed-roles 和 x-hasura-default-role 是必填信息,用于指定这个用户在 Hasura 中的角色。

image.png

自定义令牌

为了将这些自定义属性包含在JWT中,我们将对令牌进行自定义。api_key是具有对App ID进行更新权限的IAM的API KEY。tenant_id是之前记录下来的值。执行此操作后,JWT将包含之前添加的自定义属性。

$ api_key=********
$ tenant_id=********
$ token=$(curl -s https://iam.cloud.ibm.com/identity/token -X POST -H "Content-Type: application/x-www-form-urlencoded" -H "Accept: application/json" -d "grant_type=urn:ibm:params:oauth:grant-type:apikey" -d "apikey=${api_key}")
$ iam_token=$(echo ${token} | jq -r .access_token)
$ curl https://jp-tok.appid.cloud.ibm.com/management/v4/${tenant_id}/config/tokens -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer ${iam_token}" -d '{
  "accessTokenClaims": [
    {
      "source": "attributes",
      "sourceClaim": "https://hasura.io/jwt/claims"
    }
  ]
}'

手动进行身份验证以获取JWT。

$ tenant_id=********
$ client_id=********
$ secret=********
$ oauth_url=https://jp-tok.appid.cloud.ibm.com/oauth/v4/${tenant_id}
$ basic=$(echo -n "${client_id}:${secret}" | base64 -w 0)
$ token=$(curl -s ${oauth_url}/token -X POST -H "Content-Type: application/x-www-form-urlencoded" -H "Authorization: Basic ${basic}" -d 'grant_type=password&username=hasura-user@example.com&password=P@ssw0rd')
$ access_token=$(echo ${token} | jq -r .access_token)
$ echo access_token=${access_token}
access_token=eyJhbGciOiJSUz********

请用下面的网站解码获取到的访问令牌。

您可以确认已添加了自定义属性。

image.png

我将记录下方的公开密钥。

image.png

向Hasura注册App ID公开密钥。

将以下内容添加到Hasura环境变量HASURA_GRAPHQL_JWT_SECRET中。请将换行符替换为\n。

oc set env deploy graphql-engine HASURA_GRAPHQL_JWT_SECRET='{"type": "RS256", "key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBC
gKCAQEAmEwHPzp87mN6MtaYL3AC\nQKl45dq1tYfxxsvjo5R87hRWY2c/wErFCfeImj6ovaVr0BK5uvS9QdR6XIDjLCR7\nvf+PJalTXiJXi/OZBZRH2t7VlljzNObTkXfARozliwFNdjpApNpQtBE3
I2xDOzel\nfSMmB4t3cl1tTJwEpiL6S81Gk1cpPNRO0XRa+RVxzt8gSS+RkkZu8zMBWCRgwSCH\nTRcshCdPFpLpn1pB3hKLhlOcdcbMZLR5o6W+/RlCMze2uIsSeOs/WzW9MVhj2yD9\nAyCtQvq7a
km30fe4q2wfKZFZjA6hJU9yiPKF17lt69L74g7n363evHPNrp3mEs2W\niQIDAQAB\n-----END PUBLIC KEY-----"}'

验证认可的操作

创建用于查询的JSON。

{ "query": "query { users { id name } }" }

首先,尝试不使用认证标头进行调用。 当然会引发错误。

$ curl https://graphql-engine-qiita.roks-public-tok-********-0000.jp-tok.containers.appdomain.cloud/v1/graphql -X POST -d @query.json
{"errors":[{"extensions":{"path":"$","code":"invalid-headers"},"message":"Missing Authorization header in JWT authentication mode"}]}

在认证标头中指定访问令牌,成功获取结果。

$ curl https://graphql-engine-qiita.roks-public-tok-9dfbcfe481bb6d6d7afea007d1a8cafd-0000.jp-tok.containers.appdomain.cloud/v1/graphql -X POST -H "Authorization: Bearer ${access_token}" -d @query.json
{"data":{"users":[{"id":"2","name":"Fuga"}, {"id":"1","name":"Hoge"}]}}

順便提一句,如果公钥注册不成功,将导致令牌验证失败并出现错误。

{"errors":[{"extensions":{"path":"$","code":"invalid-jwt"},"message":"Could not verify JWT: JWSError JWSInvalidSignature"}]}

此外,如果访问令牌已过期,则会出现以下错误。由于HTTP返回200响应,因此在实施应用程序时,需要检查此输出进行重新认证,而不是依赖HTTP状态。

{"errors":[{"extensions":{"path":"$","code":"invalid-jwt"},"message":"Could not verify JWT: JWTExpired"}]}

设置已经完成了。

广告
将在 10 秒后关闭
bannerAds