Closed12

Rails7.1 の generates_token_for のメモ

corocncorocn

Defines the behavior of tokens generated for a specific purpose. A token can be generated by calling TokenFor#generate_token_for on a record. Later, that record can be fetched by calling find_by_token_for (or find_by_token_for!) with the same purpose and token.

特定の目的のためのトークンを生成できる。
トークンはレコードに対して TokenFor#generate_token_for を呼び出すことで生成できる。
find_by_token_for (または find_by_token_for!) を呼び出すと、そのレコードをフェッチできる。

Tokens are signed so that they are tamper-proof. Thus they can be exposed to outside world as, for example, password reset tokens.

トークンは改ざんされないように署名されているので、パスワード・リセット・トークンとして外部に公開することができる。

By default, tokens do not expire. They can be configured to expire by specifying a duration via the expires_in option. The duration becomes part of the token’s signature, so changing the value of expires_in will automatically invalidate previously generated tokens.

デフォルトでは、トークンの有効期限はない。
expires_in オプションで期間を指定することで、有効期限を設定することができる。

有効期間はトークンの署名の一部となるため、expires_inの値を変更すると、以前に生成されたトークンは自動的に無効になる。

A block may also be specified. When generating a token with TokenFor#generate_token_for, the block will be evaluated in the context of the record, and its return value will be embedded in the token as JSON.

ブロックを指定することもできる。
TokenFor#generate_token_forでトークンを生成するとき、ブロックはレコードのコンテキストで評価され、その戻り値はJSONとしてトークンに埋め込まれる。

Later, when fetching the record with find_by_token_for, the block will be evaluated again in the context of the fetched record. If the two JSON values do not match, the token will be treated as invalid. Note that the value returned by the block should not contain sensitive information because it will be embedded in the token as human-readable plaintext JSON.

find_by_token_forでレコードをフェッチするときに、フェッチされたレコードのコンテキストでブロックが再度評価される。2つのJSON値が一致しない場合、トークンは無効として扱われる。

ブロックによって返される値は、人間が読めるプレーンテキストのJSONとしてトークンに埋め込まれるため、機密情報を含んでいないことに注意してね。

corocncorocn

サンプルにあるように定義

class User < ApplicationRecord
  has_secure_password

  # User.authenticate("password")
  # User.authenticate_by(uid: "abcdef", password: "password")

  generates_token_for :password_reset, expires_in: 15.minutes do
    # Last 10 characters of password salt, which changes when password is updated:
    password_salt&.last(10)
  end
end
corocncorocn
user = User.first

token = user.generate_token_for(:password_reset)
=> "eyJfcmFpbHMiOnsiZGF0YSI6WzEsIkRZTEVDSHRxamUiXSwiZXhwIjoiMjAyMy0xMC0xNFQwNDo0MDoyNS40MjlaIiwicHVyIjoiVXNlclxucGFzc3dvcmRfcmVzZXRcbjkwMCJ9fQ==--abc5b95ff0404afa60a3db1b694e59e855213fe9"

payload = JSON.parse(Base64.decode64(token))
=> {"_rails"=>{"data"=>[1, "DYLECHtqje"], "exp"=>"2023-10-14T04:42:09.591Z", "pur"=>"User\npassword_reset\n900"}}

puts JSON.pretty_generate(payload)
{
  "_rails": {
    "data": [
      1,
      "DYLECHtqje"
    ],
    "exp": "2023-10-14T04:42:09.591Z",
    "pur": "User\npassword_reset\n900"
  }
}
=> nil

corocncorocn

JWTではない。
ただのbase64encodeされたtoken。

corocncorocn

tokenの中身が丸見えなので、使用済みであれサンプルコードのまま実装すると若干危険かも?

corocncorocn

"User\npassword_reset\n900"

これの 900 = 15min x 60 っぽい

このスクラップは2023/10/14にクローズされました