Skip to content

Instantly share code, notes, and snippets.

@tanishiking
Created April 14, 2020 02:38
Show Gist options
  • Save tanishiking/25a380cdf9db6241a0c4d3770247b68d to your computer and use it in GitHub Desktop.
Save tanishiking/25a380cdf9db6241a0c4d3770247b68d to your computer and use it in GitHub Desktop.

Pub/Sub push endpoint の JWT 検証流れ

#GCP #GAE #Pub/Sub

メモ: image 2020/4/14

参考

image

push エンドポイントの指定

gcloud pubsub subscriptions create で endpoint を指定するだけ https://cloud.google.com/pubsub/docs/push#authentication_and_authorization_by_the_push_endpoint

Pub/Sub サブスクリプションでは、すべてのメッセージを Webhook(push エンドポイント)の URL に対する HTTP POST リクエストとして送信するように構成できます。通常、push エンドポイントは、一般公開された HTTPS サーバーにします。これは、認証局が署名した有効な SSL 証明書を提示し、DNS によるルーティングが可能なサーバーである必要があります。

image

Pub/Sub subscription が JWTを生成して Authorization header に付与してくれるようにする

https://cloud.google.com/pubsub/docs/push#setting_up_for_push_authentication

  • push を行うサービスアカウントに roles/iam.serviceAccountTokenCreator を付与する
    • これはどうやら、自らの IDTokenを作成、JWTに署名をするための権限
    • サービス アカウント権限を使用できます(OAuth2 アクセス トークンを作成し、blob または JWT などに署名します)。

  • gcloud pubsub subscriptions create で subsription を作成するときに、先程 roles/iam.serviceAccountTokenCreator を付与したサービスアカウントを --push-auth-service-account で指定。 --push-auth-token-audience どちらでも良い(後述)。

これで、Pub/Sub push subscription が 指定した endpoint に post リクエストを投げるときに Authorization ヘッダに署名済み Id Token を投げてくれる。 こういうの

"Authorization" : "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjdkNjgwZDhjNzBkNDRlOTQ3MTMzY2JkNDk5ZWJjMWE2MWMzZDVh YmMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2V4YW1wbGUuY29tIiwiYXpwIjoiMTEzNzc0M jY0NDYzMDM4MzIxOTY0IiwiZW1haWwiOiJnYWUtZ2NwQGFwcHNwb3QuZ3NlcnZpY2VhY2NvdW50LmNvb SIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjE1NTAxODU5MzUsImlhdCI6MTU1MDE4MjMzNSwia XNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTEzNzc0MjY0NDYzMDM4MzIxO TY0In0.QVjyqpmadTyDZmlX2u3jWd1kJ68YkdwsRZDo-QxSPbxjug4ucLBwAs2QePrcgZ6hhkvdc4UHY 4YF3fz9g7XHULNVIzX5xh02qXEH8dK6PgGndIWcZQzjSYfgO-q-R2oo2hNM5HBBsQN4ARtGK_acG-NGG WM3CQfahbEjZPAJe_B8M7HfIu_G5jOLZCw2EUcGo8BvEwGcLWB2WqEgRM0-xt5-UPzoa3-FpSPG7DHk7 z9zRUeq6eB__ldb-2o4RciJmjVwHgnYqn3VvlX9oVKEgXpNFhKuYA-mWh5o7BCwhujSMmFoBOh6mbIXF cyf5UiVqKjpqEbqPGo_AvKvIQ9VTQ" (from https://cloud.google.com/pubsub/docs/push#using_json_web_tokens_jwts )

image

JWT の検証

nodejs の場合 google-auth-library を使うと楽ちん

code:javascript

  • import { OAuth2Client } from 'google-auth-library'
  • const authClient = new OAuth2Client()
  • // ...
  • // Authorization header から bearer token (JWT) を取得
  • const bearer = req.header('Authorization')
  • if (!bearer) {
    • res.status(400).send('Missing mandatory header "Authorization".')
    • return
  • }
  • const found = bearer?.match(/Bearer (.*)/)
  • const token = found?.[1]
  • if (!token) {
    • res.status(400).send('Invalid Authorization header.')
    • return
  • }
  • // google-auth-library の verifyIdToken を使って JWT を検証
  • const ticket: LoginTicket = await authClient.verifyIdToken({
    • idToken: token,
    • // A single, case-insensitive string that can be used by the webhook to validate the intended audience of this particular token.
    • audience: 'example.com', // TDOO: audience の設定
  • })
  • // 上の過程で検証に失敗した場合は Promise abort が返される、処理を try / catch で囲って、catch の中で 400エラーを返す。

サンプルコードでは loginTicket.getPayload してるけど、getPayload はJWTのpayload部分を返すだけなので認証処理には関係ない。 LoginTicket が返ってきたら検証成功してる。

audience には何を指定するのか

  • audience には Pub/Sub push subscriber から渡された JWT の payload の中の aud claim と同じものを渡す必要がある。

image

verifyIdToken が何をしているのか

![image](OpenID Connect  |  Google Identity Platform  |  Google Developers https://developers.google.com/identity/protocols/oauth2/openid-connect#validatinganidtoken)

verifyIdToken は受け取った id token を検証するわけだが、具体的に何をしているのか https://github.com/googleapis/google-auth-library-nodejs/blob/cc10eb75b9387b782c6ea2d2a8f1798f60a52707/src/auth/oauth2client.ts#L976-L1022

やってることは主に2つ

getFederatedSignonCertsAsync

verifySignedJwtWithCertsAsync

  • 取得した公開鍵を使って、JWTの署名を検証
  • 詳しくは見てない。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment