Appleでサインインのトークン検証
セットアップ
# Gemfile
gem "jwt"
# クライアントから送られてきたパラメータ
token = "identityToken"
code = "authorizationCode"
署名検証
url = "https://appleid.apple.com/auth/keys"
jwks = JSON.parse(open(url).read, symbolize_names: true)
algorithms = jwks[:keys].map { |key| key[:alg] } # or tokenのHeaderの:alg
decoded_token = JWT.decode(token, nil, true, algorithms: algorithms, jwks: jwks).first.with_indifferent_access
# 1つのJWKだけ検証したいとき
jwk = jwks[:keys].first
public_key = JWT::JWK.import(jwk).keypair.public_key # or `JWT::JWK::RSA.import(jwk).public_key`
JWT.decode(token, public_key, true, algorithm: "RS256")
# 期限は10分なので注意
JWT.decode(...) #=> JWT::ExpiredSignature: Signature has expired
authorizationCodeの検証
# https://openid.net/specs/openid-connect-core-1_0.html#CodeValidation
digest = Digest::SHA256.digest(code)
c_hash = Base64.urlsafe_encode64(digest[0, digest.size/2], padding: false)
decoded_token[:c_hash] == c_hash
nonceの検証
クライアントの実装はfirebase/quickstart-iosが参考になるかも。 iOS 13からCryptoKitが導入されたので、quickstart-ios的なnonceの生成は以下みたいな感じでもOKかな 🤔
# クライアント
import CryptoKit
let uuid = CFUUIDCreateString(nil, CFUUIDCreate(nil))!
let rawNonce = NSString(string: uuid).replacingOccurrences(of: "-", with: "")
let data = rawNonce.data(using: .utf8)!
request.nonce = SHA256.hash(data: data).compactMap { String(format: "%02x", $0) }.joined()
# サーバー
decoded_token[:nonce] == Digest::SHA256.hexdigest(rawNonce)
iOS 12以下をサポートしている場合、weak linkする必要があるので忘れずに。
Targets > APP > Build Settings > Other Linker Flags に
-weak_framework CryptoKit
を追加
リンク
- JSON Web Token (JWT)
- JSON Web Signature (JWS)
- JSON Web Key (JWK)
- jwt/ruby-jwt
- Sign in with Apple REST API | Apple Developer Documentation