Skip to content
Server Plugin

Ktor Serverにおける認証と認可

必要な依存関係: io.ktor:ktor-server-auth

Ktorは、認証と認可を処理するためにAuthenticationプラグインを提供します。典型的な使用シナリオには、ユーザーのログイン、特定のリソースへのアクセス許可、関係者間での情報の安全な送信などがあります。Authenticationセッションと併用して、リクエスト間でユーザー情報を保持することもできます。

クライアント側では、Ktorは認証と認可を処理するためのAuthenticationプラグインを提供します。

サポートされている認証タイプ

Ktorは以下の認証および認可スキームをサポートしています。

HTTP認証

HTTPは、アクセス制御と認証のための一般的なフレームワークを提供します。Ktorでは、以下のHTTP認証スキームを使用できます。

  • Basic - Base64エンコーディングを使用してユーザー名とパスワードを提供します。HTTPSと組み合わせて使用しない限り、一般的に推奨されません。
  • Digest - ユーザー名とパスワードにハッシュ関数を適用することで、ユーザー資格情報を暗号化された形式で通信する認証方法です。
  • Bearer - Bearerトークンと呼ばれるセキュリティトークンを使用する認証スキームです。 Bearer認証スキームはOAuthまたはJWTの一部として使用されますが、Bearerトークンを認可するためのカスタムロジックを提供することもできます。

フォームベース認証

フォームベース認証は、ウェブフォームを使用して資格情報を収集し、ユーザーを認証します。

JSON Webトークン (JWT)

JSON Webトークンは、JSONオブジェクトとして関係者間で情報を安全に送信するためのオープンスタンダードです。JSON Webトークンを認可に使用できます。ユーザーがログインすると、各リクエストにはトークンが含まれ、そのトークンで許可されたリソースにユーザーがアクセスできるようになります。Ktorでは、jwt認証を使用してトークンを検証し、その中に含まれるクレームを検証できます。

LDAP

LDAPは、ディレクトリサービス認証に使用されるオープンなクロスプラットフォームプロトコルです。Ktorは、指定されたLDAPサーバーに対してユーザー資格情報を認証するために、ldapAuthenticate関数を提供します。

OAuth

OAuthは、APIへのアクセスを保護するためのオープンスタンダードです。Ktorのoauthプロバイダーを使用すると、Google、Facebook、Twitterなどの外部プロバイダーを使用して認証を実装できます。

セッション

セッションは、異なるHTTPリクエスト間でデータを永続化するメカニズムを提供します。典型的な使用例としては、ログインしたユーザーのID、ショッピングバスケットの内容の保存、クライアント上でのユーザー設定の保持などがあります。Ktorでは、関連付けられたセッションを持つユーザーは、sessionプロバイダーを使用して認証できます。その方法はKtor Serverでのセッション認証で学ぶことができます。

カスタム

Ktorはカスタムプラグインを作成するためのAPIも提供しており、認証と認可を処理するための独自のプラグインを実装するために使用できます。 例えば、AuthenticationChecked フックは認証資格情報がチェックされた後に実行され、認可を実装することができます: custom-plugin-authorization

依存関係の追加

Authenticationを使用するには、ビルドスクリプトにktor-server-authアーティファクトを含める必要があります:

Kotlin
Groovy
XML

JWTLDAPなど、一部の認証プロバイダーは追加のアーティファクトを必要とすることに注意してください。

Authenticationのインストール

アプリケーションにAuthenticationプラグインをインストールするには、指定された

モジュール
モジュールはルートをグループ化することでアプリケーションを構造化できます。
内のinstall関数に渡します。以下のコードスニペットは、Authenticationをインストールする方法を示しています...

  • ... embeddedServer関数呼び出し内で。
  • ... Applicationクラスの拡張関数である明示的に定義されたmodule内で。
kotlin
kotlin

Authenticationの設定

Authenticationをインストールした後、次のようにAuthenticationを設定して使用できます。

ステップ1: 認証プロバイダーを選択する

basicdigestformなどの特定の認証プロバイダーを使用するには、installブロック内で対応する関数を呼び出す必要があります。例えば、basic認証を使用するには、.basic()関数を呼び出します。

kotlin
import io.ktor.server.application.*
import io.ktor.server.auth.*
// ...
install(Authentication) {
    basic {
        // Configure basic authentication
    }
}

この関数内で、このプロバイダーに固有の設定を構成できます。

ステップ2: プロバイダー名を指定する

特定のプロバイダーを使用するための関数は、オプションでプロバイダー名を指定できます。以下のコードサンプルは、basicおよびformプロバイダーをそれぞれ"auth-basic"および"auth-form"という名前でインストールしています。

kotlin
install(Authentication) {
    basic("auth-basic") {
        // Configure basic authentication
    }
    form("auth-form") {
        // Configure form authentication
    }
    // ...
}

これらの名前は後で異なるプロバイダーを使用して異なるルートを認証するために使用できます。

プロバイダー名は一意である必要があり、名前なしのプロバイダーは1つしか定義できないことに注意してください。

ステップ3: プロバイダーを設定する

プロバイダータイプには独自の構成があります。例えば、BasicAuthenticationProvider.Configクラスは.basic()関数にオプションを提供します。このクラスの主要な関数はvalidate()で、ユーザー名とパスワードを検証する役割を担っています。以下のコード例はその使用法を示しています。

kotlin
install(Authentication) {
    basic("auth-basic") {
        realm = "Access to the '/' path"
        validate { credentials ->
            if (credentials.name == "jetbrains" && credentials.password == "foobar") {
                UserIdPrincipal(credentials.name)
            } else {
                null
            }
        }
    }
}

validate()関数の動作を理解するには、2つの用語を導入する必要があります。

  • プリンシパル(principal)とは、認証できるエンティティです。ユーザー、コンピューター、サービスなどです。Ktorでは、さまざまな認証プロバイダーが異なるプリンシパルを使用する場合があります。例えば、basicdigestformプロバイダーはUserIdPrincipalを認証し、jwtプロバイダーはJWTPrincipalを検証します。

    カスタムプリンシパルを作成することもできます。これは次の場合に役立つ可能性があります。

    • 資格情報をカスタムプリンシパルにマッピングすることで、ルートハンドラー内で認証されたプリンシパルに関する追加情報を持つことができます。
    • セッション認証を使用する場合、プリンシパルはセッションデータを格納するデータクラスである可能性があります。
  • クレデンシャル(credential)とは、サーバーがプリンシパルを認証するための一連のプロパティです。ユーザー名とパスワードのペア、APIキーなどです。例えば、basicおよびformプロバイダーはUserPasswordCredentialを使用してユーザー名とパスワードを検証し、jwtJWTCredentialを検証します。

したがって、validate()関数は指定されたクレデンシャルをチェックし、認証が成功した場合はプリンシパルAnyを返し、認証が失敗した場合はnullを返します。

特定の基準に基づいて認証をスキップするには、skipWhen()を使用します。例えば、セッションが既に存在する場合、basic認証をスキップできます。

kotlin
basic {
    skipWhen { call -> call.sessions.get<UserSession>() != null }
}

ステップ4: 特定のリソースを保護する

最後のステップは、アプリケーション内の特定のリソースを保護することです。これはauthenticate()関数を使用することで実現できます。この関数は2つのオプションパラメーターを受け入れます。

  • ネストされたルートを認証するために使用されるプロバイダーの名前。以下のコードスニペットは、_auth-basic_という名前のプロバイダーを使用して/loginおよび/ordersルートを保護しています。

    kotlin
    routing {
        authenticate("auth-basic") {
            get("/login") {
                // ...
            }    
            get("/orders") {
                // ...
            }    
        }
        get("/") {
            // ...
        }
    }
  • ネストされた認証プロバイダーを解決するために使用される戦略。この戦略はAuthenticationStrategy列挙値で表されます。

    例えば、クライアントはAuthenticationStrategy.Required戦略で登録されたすべてのプロバイダーに対して認証データを提供する必要があります。 以下のコードスニペットでは、セッション認証を通過したユーザーのみがbasic認証を使用して/adminルートにアクセスできます。

    kotlin
    routing {
        authenticate("auth-session", strategy = AuthenticationStrategy.Required) {
            get("/hello") {
                // ...
            }    
            authenticate("auth-basic", strategy = AuthenticationStrategy.Required) {
                get("/admin") {
                    // ...
                }
            }  
        }
    }

完全な例については、auth-form-session-nestedを参照してください。

ステップ5: ルートハンドラー内でプリンシパルを取得する

認証に成功すると、ルートハンドラー内でcall.principal()関数を使用して認証されたプリンシパルを取得できます。この関数は、設定された認証プロバイダーによって返される特定のプリンシパルタイプを受け入れます。以下の例では、call.principal()を使用してUserIdPrincipalを取得し、認証されたユーザーの名前を取得しています。

kotlin
routing {
    authenticate("auth-basic") {
        get("/") {
            call.respondText("Hello, ${call.principal<UserIdPrincipal>()?.name}!")
        }
    }
}

セッション認証を使用する場合、プリンシパルはセッションデータを格納するデータクラスである可能性があります。 そのため、このデータクラスをcall.principal()に渡す必要があります。

kotlin
authenticate("auth-session") {
    get("/hello") {
        val userSession = call.principal<UserSession>()
    }
}

ネストされた認証プロバイダーの場合、目的のプロバイダーのプリンシパルを取得するために、プロバイダー名call.principal()に渡すことができます。

以下の例では、最上位のセッションプロバイダーのプリンシパルを取得するために"auth-session"値が渡されています。

kotlin
authenticate("auth-session", strategy = AuthenticationStrategy.Required) {
    authenticate("auth-basic", strategy = AuthenticationStrategy.Required) {
        get("/admin") {
            val userSession = call.principal<UserSession>("auth-session")
        }
    }
}