【小ネタ】WorkflowsからCloud Functionsの呼び出し時にBearer error="invalid_token"が発生したのでaudienceを明示的に指定するようにして解決した話

【小ネタ】WorkflowsからCloud Functionsの呼び出し時にBearer error="invalid_token"が発生したのでaudienceを明示的に指定するようにして解決した話

WorkflowsからHTTPリクエストを発行する際に `audience` を省略すると `url` と同じ値が設定されます
Clock Icon2024.07.07

リテールアプリ共創部@大阪の岩田です

先日Cloud Functionsを呼び出すWorkflowsを修正している時にBearer error="invalid_token" error_description="The access token could not be verified"というエラーが発生するようになったので、原因調査と修正を行いました。慣れてる人からすると基本的な内容なのかとは思いますが、原因切り分けに少し時間を要してしまったので、原因と対策についてご紹介します。

正常動作していた頃のWorkflowsとCloud Funtionsの実装

当初WorkflowsからCloud Functionsを呼び出す処理は正常に動作していました。Cloud Functionsの改修を機に正常動作しなくなってしまったのですが、改修前の実装はそれぞれ以下のような実装でした。※実際の実装ではなく説明用に簡略化しています

まずCloud Functionsです

const functions = require('@google-cloud/functions-framework');

functions.http('helloGET', (req, res) => {
  res.send('Hello World!');
});

全てのリクエストに対してHello World!を返却するだけの実装です。このCloud Functionsを呼び出すWorkflowの処理は以下の通りです。

main:
  steps:
    - call_function:
        call: http.get
        args:
          url: https://asia-northeast1-<プロジェクトID>.cloudfunctions.net/<Cloud Functionsの関数名>
          auth:
            type: OIDC

http.getを使ってCloud Functionsを呼び出すだけの処理です。Cloud Functionsを呼び出すために認証が必要なので引数のauthtype: OIDCを指定しています。この実装で問題なく動作していました。

動作しなくなったWorkflowsとCloud Funtionsの実装

続いてCloud Functionsのコードに改修が発生しました。改修後のコードは以下の通りです。

const functions = require('@google-cloud/functions-framework');
const express = require('express');

const app = express();
const router = express.Router();

router.get('/hoge', async (req, res) => {
    return res.send('hoge');
  }
);

router.all('*', async (req, res) => {
  res.send('Hello World!');
});
app.use(router);

functions.http('main', app);

従来の処理に加えて、GET /hogeというリクエストに対してはhogeを返却する処理を追加しました。そしてWorkflowsからはこの新しい/hogeというエンドポイントを呼び出すよう改修を加えました。Workflowsの定義は以下の通りです。

main:
  steps:
    - call_function:
        call: http.get
        args:
          url: https://asia-northeast1-<プロジェクトID>.cloudfunctions.net/<Cloud Functionsの関数名>/hoge
          auth:
            type: OIDC

http.getの引数で指定するurlの末尾に/hogeを追加しています。この状態でWorkflowsを起動すると...以下のようなエラーが発生しました。

HTTP server responded with error code 401
in step "call_function", routine "main", line: 4
{
  "body": "\n<html><head>\n<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n<title>401 Unauthorized</title>\n</head>\n<body text=#000000 bgcolor=#ffffff>\n<h1>Error: Unauthorized</h1>\n<h2>Your client does not have permission to the requested URL <code>/<Cloud Functionsの関数名>/hoge</code>.</h2>\n<h2></h2>\n</body></html>\n",
  "code": 401,
  "headers": {
    "Alt-Svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
    "Content-Length": "314",
    "Content-Type": "text/html; charset=UTF-8",
    "Date": "Sun, 07 Jul 2024 10:28:40 GMT",
    "Server": "Google Frontend",
    "Www-Authenticate": "Bearer error=\"invalid_token\" error_description=\"The access token could not be verified\""
  },
  "message": "HTTP server responded with error code 401",
  "tags": [
    "HttpError"
  ]
}

URLを少し変更しただけなのになぜ...??

修正後のWorkflows

しばらく色々と悩んだのですが、改めてWorkflowsのドキュメントを確認すると答えが分かりました。

audience キーは省略できますが、これを使用することで、トークンの OIDC オーディエンスを指定できます。デフォルトでは、OIDC_AUDIENCEurl と同じ値に設定されます。

ワークフローからの認証済みリクエスト | Workflows | Google Cloud

改めてWorkflowsの定義を見直すとaudienceは特に指定していませんでした。ということはurlと同じ値...つまり末尾に/hogeがついたURLがaudienceとして利用されていたということが分かります。ということで明示的にaudienceを指定してあげればエラーは解消しそうです。ということで修正後のWorkflowsの定義です。

main:
  steps:
    - call_function:
        call: http.get
        args:
          url: https://asia-northeast1-<プロジェクトID>.cloudfunctions.net/<Cloud Functionsの関数名>/hoge
          auth:
            type: OIDC
            audience: https://asia-northeast1-<プロジェクトID>.cloudfunctions.net/<Cloud Functionsの関数名>

これで無事元通りWorkflowsからCloud Functionsを呼び出せるようになりました。めでたしめでたし。

まとめ

WorkflowsからCloud Functionsの呼び出し時に発生した認証エラーについてご紹介しました。やはり脳死でコピペせずにちゃんと公式ドキュメントを読むのは大事ですね。エンジニアとしての基本姿勢ではあるものの、時間が無いときなどは未だにやってしまうことがあります...皆さんもお気を付け下さい。

参考

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.