Scala、AkkaHttp、JsonWebToken(Jwt)を使用して認証を実装したい!パート2


受け取ったJwtをdecodeしてみる!

jwtについて参考にした記事
https://webbibouroku.com/Blog/Article/jwt

qiita.scala
jwt = eyJhbGciOiJSUzI1NiFIEJWOFIJSOIDJFIkpXVCJ9.eyJ1c2VybmFtZSDFOIJFOIWJFPOWIJEFOSDJFQvMDEifQ.u8TT4ySJhBiMod1rNqCxiISXwCXqddVSJE5gncSCGD7RPHigHoZiDnLfwcIIGij-ARBrswe4eluMUgaQwS7FUdN8IVwKkFUY1533TQZZYhTUzobO3q_PnFPWEOIJldfjwopiefjoijsdf

みたいな形になっているので、

pauldijou/jwt-scalaのライブラリを使ってデコードできる環境を作ります。
https://github.com/pauldijou/jwt-scala

今回はただ単にdecodeしたいだけなので、jwt-coreを入れるだけで良さそうです。

なのでbuild.sbtに一行追加して使えるようにする。

qiita.scala
  "com.pauldijou" %% "jwt-core" % "0.18.0"

デコードするときは次のような形で使用します。

qiita.scala
val decodedJwt: Try[String] = Jwt.decode(jwt, publicKey, Seq(alg))

jwtはもちろんStringのトークン、
publicKeyは、公開鍵ですね。今回自分が扱うアクセストークンは鍵を利用しているのでこのような実装になってます。

qiita.scala
publicKey: String =   "-----BEGIN PUBLIC KEY-----\nMIPOEFIJWEOFIJSDLFJL+SDJFOWEIJFWEKljdfopijaopeifj;lIJEFOIj;ldjfopIjeftws419Bil8hPpveXfjIFhVnMrpft6QnleEvYSYL\nxpBLPBfFcLQvx0OMpNR4JVlUPYpjNEksbGeXcxanyR+5fal+cOq1JYPvvBM7tMgL\nHqPMfJHDZfpD9UHlNIl3eWLTaGJnjIbgwFW+XNi0cwGsXGxFoT0ASZqx6HAQzDtO\n2/Qrt7EbwjxiVB0neM+l0N4+mOttuh5VGYv+cE/U64z4MhXjSq3woUrl3ax/2CQc\nMs6hPE6twprOfUKJEGIjut45Ng4t++5hcf4wKZl21F/tgIDmatHOeLPvu8iabHwQ\nGqnN1FXDDgnT2iW43BNfm4zghiHszQHIYdhhVpqVV3rhalcuFubz8VdEhzl0UWZr\n57foLG1+M9i9mV8C3D1mjWB8mdRP8oKRhTE2qWcbBhGnYSDhiA3GSE8PWHF5/+l9\nl8UUHtMPmFP3wKCZBBxxul5VqFnqNIeqU0ofPoJgHrbHrzvxvPfPq2qePofuzbjb\nJpMGWi1h8B+99zTRWvV6U0uJ6VrfcbC61TseV1xlV/SEop0ijWT3oRDtO5eCAV9e\nq4i2ZPUwcV9aypBHp+SptuUCAwEAAQ==\n-----END PUBLIC KEY-----"

Jwt.decodeの中を追って行くと、以下のようになっているので

qiita.scala
  private def parseKey(key: String): Array[Byte] = JwtBase64.decodeNonSafe(
    key.replaceAll("-----BEGIN (.*)-----", "")
     .replaceAll("-----END (.*)-----", "")
     .replaceAll("\r\n", "")
     .replaceAll("\n", "")
     .trim
  )

そのまま上記のpublicKey: Stringを渡せる感じになってます(うれしい)。

そして最後にSeq(alg)の部分では、Jwtのアルゴリズムを渡してあげます。

今回私の環境では、RS256を使用しているので、pdi.jwtの

qiita.scala
  case object RS256 extends algorithms.JwtRSAAlgorithm   { def name = "RS256"; def fullName = "SHA256withRSA" }

これを渡してあげる。

つまり

qiita.scala
jwt = eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IuWxseeUsCIsImV4cCI6IjIwMTkvMDQvMDEifQ.u8TT4ySJhBiMod1rNqCxiISXwCXqddVSJE5gncSCGD7RPHigHoZiDnLfwcIIGij-ARBrswe4eluMUgaQwS7FUdN8IVwKkFUY1533TQZZYhTUzobO3q_PnDyPi8cPDmkSTJHzBhT298G7fNZENUUo8fmCjOkYC9FibTMrF6Aij4w

publickey =  "-----BEGIN PUBLIC KEY-----\nMIPOEFIJWEOFIJSDLFJL+SDJFOWEIJFWEKljdfopijaopeifj;lIJEFOIj;ldjfopIjeftws419Bil8hPpveXfjIFhVnMrpft6QnleEvYSYL\nxpBLPBfFcLQvx0OMpNR4JVlUPYpjNEksbGeXcxanyR+5fal+cOq1JYPvvBM7tMgL\nHqPMfJHDZfpD9UHlNIl3eWLTaGJnjIbgwFW+XNi0cwGsXGxFoT0ASZqx6HAQzDtO\n2/Qrt7EbwjxiVB0neM+l0N4+mOttuh5VGYv+cE/U64z4MhXjSq3woUrl3ax/2CQc\nMs6hPE6twprOfUKJEGIjut45Ng4t++5hcf4wKZl21F/tgIDmatHOeLPvu8iabHwQ\nGqnN1FXDDgnT2iW43BNfm4zghiHszQHIYdhhVpqVV3rhalcuFubz8VdEhzl0UWZr\n57foLG1+M9i9mV8C3D1mjWB8mdRP8oKRhTE2qWcbBhGnYSDhiA3GSE8PWHF5/+l9\nl8UUHtMPmFP3wKCZBBxxul5VqFnqNIeqU0ofPoJgHrbHrzvxvPfPq2qePofuzbjb\nJpMGWi1h8B+99zTRWvV6U0uJ6VrfcbC61TseV1xlV/SEop0ijWT3oRDtO5eCAV9e\nq4i2ZPUwcV9aypBHp+SptuUCAwEAAQ==\n-----END PUBLIC KEY-----"

alg = RS256

ですね。

これでちゃんとしたアクセストークンでしたらデコードに成功するはずです。

失敗しがちな要因としては

・アルゴリズムをSeqに入れて渡してない
・Headerの[Authorization] [Bearer odsafijoSEOFJDLFSJDFOEWIFJOIJSDfdf]のBearerを入れてなかったり、半角スペースを入れてない
・アクセストークンの有効期限が切れていた

とかですかね。

これでdecodeされたJwtが取得できました。

あとはきちんとdecodeができたので、このアクセストークンは有効ということで、ユーザー情報を格納したAuthContextを生成して返してあげれば認証終わりですね。

次回は公開鍵をベタ書きから、認証サーバーに問い合わせて動的に取得したり、無効なアクセストークンリストを取得したりしてみます。

パート3 => https://qiita.com/katokonn1020/items/3a60f47ba8be4113ced7