Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

この記事は認証認可あたりを聞いたことはあるけど、実装はしたことないって人向けです。

具体的にどのようにJwtを発行したり、デコードするかは「Jwt 仕組み」とかでググってもらえるとわかるとおもいます。

正しいリクエストなのかどうやって認証するか?

認証の仕組みとしては、
スクリーンショット 2018-10-25 15.49.32.png
こんな風にRequestのHeaderに格納されているJsonWebTokenをAppサーバー側で、正しくデコードできればそのユーザーは正規のユーザーであると認証する感じです。

RequestHeaderからアクセストークン(String)を取得!

最初はRequestHeaderに入っている

スクリーンショット 2018-10-25 15.55.12.png
の部分を

qiita.scala
val validateCustomHeader: Directive1[String] = {
    val value = headerValueByName("Authorization")
    value.filter(_ == "Bearer [Jwtトークン部分]" ,MalformedHeaderRejection("Authorization", "value was wrong"))
}

と、AkkaのRouteを使ってリクエストの認証を行おうと思ったんですが、
valueのfilterの_ == "Bearer ~~~~~~~~~~"部分をどうもうまく拡張できない。

value.filter(_ => (jwtの有効性をチェックするBoolean))とかで書けると思ったんですが、型的にoutでした。(かなしい)

なので、別のアプローチから実装することに。

SecurityDirectivesで取得してみる

SecurityDirectivesの中身を見てたら便利そうなのを発見

qiita.scala
  def authenticateOAuth2Async[T](realm: String, authenticator: AsyncAuthenticator[T]): AuthenticationDirective[T] =
    extractExecutionContext.flatMap { implicit ec 
      def extractAccessTokenParameterAsBearerToken = {
        import akka.http.scaladsl.server.Directives._
        parameter('access_token.?).map(_.map(OAuth2BearerToken))
      }
      val extractCreds: Directive1[Option[OAuth2BearerToken]] =
        extractCredentials.flatMap {
          case Some(c: OAuth2BearerToken)  provide(Some(c))
          case _                           extractAccessTokenParameterAsBearerToken
        }
      extractCredentialsAndAuthenticateOrRejectWithChallenge[OAuth2BearerToken, T](extractCreds, { cred 
        authenticator(Credentials(cred)).fast.map {
          case Some(t)  AuthenticationResult.success(t)
          case None     AuthenticationResult.failWithChallenge(HttpChallenges.oAuth2(realm))
        }
      })
    }

なんかOAuth2BearerTokenをうまく扱えそう^^

qiita.scala
val extractCreds: Directive1[Option[OAuth2BearerToken]] =
  extractCredentials.flatMap {
    case Some(c: OAuth2BearerToken)  provide(Some(c))
    case _                           extractAccessTokenParameterAsBearerToken
  }

ここの部分でHeaderのAuthorizationの部分をStringからOAuth2BearerTokenにしてSome(c)に格納してるみたいですね。

さらに下の

qiita.scala
        authenticator(Credentials(cred)).fast.map {
          case Some(t)  AuthenticationResult.success(t)
          case None     AuthenticationResult.failWithChallenge(HttpChallenges.oAuth2(realm))
        }

の部分で引数のauthenticatorにCredentials(cred)の形で渡しているので、ここを追ってみると
最終的に

qiita.scala
  override def authenticate(credentials: Credentials): Future[Option[AuthContext]] = {
       ~~~~ここでJwt認証の実装をする ~~~~~
  }

って形で、authenticatorを実装すれば認証機能が作れそうです。

つまり

qiita.scala
a: Future[Option[AuthContext]] match {
  Some(t) => 認証成功
  None => 認証失敗
}

となるわけですね

Authenticatorを実装してみる

authenticator(Credentials(cred))とあるので、HeaderのAuthorization部分の取得を1から書く必要はなさそう。

qiita.scala
  override def authenticate(credentials: Credentials): Future[Option[AuthContext]] = {
    credentials match {
      case Credentials.Provided(jwt) => validateJwt(jwt)
      case _ => Future { None }
    }
  }

と簡単に書いちゃいましょう。これでjwtがString形式で受け取れるようになりました。

というわけで次回は、Jwtのデコードをしていこうと思います。

パート2 -> https://qiita.com/katokonn1020/items/44ff9a6bec7967358b1d

katokonn1020
Scalaエンジニアやってます
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした