Skip to content
Server Plugin

OAuth

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

コード例: auth-oauth-google

Nativeサーバー
KtorはKotlin/Nativeをサポートしており、追加のランタイムや仮想マシンなしでサーバーを実行できます。
のサポート: ✅

OAuthは、アクセス委任のためのオープンな標準規格です。OAuthは、Google、Facebook、Twitterなどの外部プロバイダーを使用して、アプリケーションのユーザーを承認するために使用できます。

oauthプロバイダーは認可コードフローをサポートしています。OAuthパラメーターを一箇所で設定でき、Ktorは必要なパラメーターとともに指定された認可サーバーへ自動的にリクエストを行います。

Ktorにおける認証と認可に関する一般的な情報は、Ktorサーバーでの認証と認可セクションで確認できます。

依存関係を追加する

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

Kotlin
Groovy
XML

セッションプラグインをインストールする

クライアントが保護されたリソースにアクセスしようとするたびに認可を要求するのを避けるため、認可が成功した際にアクセストークンをセッションに保存することができます。その後、保護されたルートのハンドラー内で現在のセッションからアクセストークンを取得し、それを使用してリソースを要求できます。

kotlin
import io.ktor.server.sessions.*

fun Application.main(httpClient: HttpClient = applicationHttpClient) {
    install(Sessions) {
        cookie<UserSession>("user_session")
    }
}
@Serializable
data class UserSession(val state: String, val token: String)

OAuth認可フロー

KtorアプリケーションにおけるOAuth認可フローは以下のようになります:

  1. ユーザーがKtorアプリケーションのログインページを開きます。

  2. Ktorは、特定のプロバイダーの認可ページへ自動的にリダイレクトし、必要なパラメーターを渡します:

    • 選択されたプロバイダーのAPIにアクセスするために使用されるクライアントID。
    • 認可完了後に開かれるKtorアプリケーションのページを指定するコールバックまたはリダイレクトURL。
    • Ktorアプリケーションに必要なサードパーティリソースのスコープ。
    • アクセストークン(認可コード)を取得するために使用されるグラントタイプ。
    • CSRF攻撃を軽減し、ユーザーをリダイレクトするために使用されるstateパラメーター。
    • 特定のプロバイダーに固有のオプションパラメーター。
  3. 認可ページには、Ktorアプリケーションに必要な権限レベルを示す同意画面が表示されます。これらの権限は、ステップ2: OAuthプロバイダーの設定で設定された指定スコープに依存します。

  4. ユーザーが要求された権限を承認すると、認可サーバーは指定されたリダイレクトURLにリダイレクトし、認可コードを送信します。

  5. Ktorは、指定されたアクセストークンURLに次のパラメーターを含めて、もう一度自動的にリクエストを行います:

    • 認可コード。
    • クライアントIDとクライアントシークレット。

    認可サーバーはアクセストークンを返却して応答します。

  6. クライアントはこのトークンを使用して、選択されたプロバイダーの必要なサービスへリクエストを行うことができます。ほとんどの場合、トークンはBearerスキーマを使用してAuthorizationヘッダーで送信されます。

  7. サービスはトークンを検証し、そのスコープを認可に利用して、要求されたデータを返します。

OAuthのインストール

oauth認証プロバイダーをインストールするには、installブロック内でoauth関数を呼び出します。オプションで、プロバイダー名を指定できます。例えば、"auth-oauth-google"という名前でoauthプロバイダーをインストールするには、以下のようになります:

kotlin
import io.ktor.server.application.*
import io.ktor.server.auth.*

fun Application.main(httpClient: HttpClient = applicationHttpClient) {
    install(Authentication) {
        oauth("auth-oauth-google") {
            // Configure oauth authentication
        }
    }
}

OAuthの設定

このセクションでは、Googleを使用してアプリケーションのユーザーを認可するためのoauthプロバイダーの設定方法を説明します。完全に実行可能な例については、auth-oauth-googleを参照してください。

前提条件: 認可クレデンシャルを作成する

Google APIにアクセスするには、Google Cloud Consoleで認可クレデンシャルを作成する必要があります。

  1. Google Cloud Consoleで認証情報ページを開きます。

  2. 認証情報を作成をクリックし、OAuth クライアント IDを選択します。

  3. ドロップダウンからウェブ アプリケーションを選択します。

  4. 次の設定を指定します:

    • 承認済みのJavaScript生成元: http://localhost:8080
    • 承認済みのリダイレクトURI: http://localhost:8080/callback。 Ktorでは、urlProviderプロパティを使用して、認可完了時に開かれるリダイレクトルートを指定します。
  5. 作成をクリックします。

  6. 表示されたダイアログで、作成されたクライアントIDとクライアントシークレットをコピーします。これらはoauthプロバイダーの設定に使用されます。

ステップ1: HTTPクライアントを作成する

oauthプロバイダーを設定する前に、サーバーがOAuthサーバーにリクエストを行うために使用するHttpClientを作成する必要があります。ContentNegotiationクライアントプラグインとJSONシリアライザーは、APIへのリクエスト後に受信したJSONデータをデシリアライズするために必要です。

kotlin
val applicationHttpClient = HttpClient(CIO) {
    install(ContentNegotiation) {
        json()
    }
}

クライアントインスタンスは、サーバーテストで別のクライアントインスタンスを作成できるように、mainモジュール関数に渡されます。

kotlin
fun Application.main(httpClient: HttpClient = applicationHttpClient) {
}

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

以下のコードスニペットは、auth-oauth-googleという名前でoauthプロバイダーを作成および設定する方法を示しています。

kotlin
val redirects = mutableMapOf<String, String>()
install(Authentication) {
    oauth("auth-oauth-google") {
        // Configure oauth authentication
        urlProvider = { "http://localhost:8080/callback" }
        providerLookup = {
            OAuthServerSettings.OAuth2ServerSettings(
                name = "google",
                authorizeUrl = "https://accounts.google.com/o/oauth2/auth",
                accessTokenUrl = "https://accounts.google.com/o/oauth2/token",
                requestMethod = HttpMethod.Post,
                clientId = System.getenv("GOOGLE_CLIENT_ID"),
                clientSecret = System.getenv("GOOGLE_CLIENT_SECRET"),
                defaultScopes = listOf("https://www.googleapis.com/auth/userinfo.profile"),
                extraAuthParameters = listOf("access_type" to "offline"),
                onStateCreated = { call, state ->
                    //saves new state with redirect url value
                    call.request.queryParameters["redirectUrl"]?.let {
                        redirects[state] = it
                    }
                }
            )
        }
        client = httpClient
    }
  • urlProviderは、認可が完了したときに呼び出されるリダイレクトルートを指定します。

    このルートが承認済みのリダイレクトURIのリストに追加されていることを確認してください。

  • providerLookupを使用すると、必要なプロバイダーのOAuth設定を指定できます。これらの設定はOAuthServerSettingsクラスによって表現され、KtorがOAuthサーバーへ自動的にリクエストを行うことを可能にします。
  • clientプロパティは、KtorがOAuthサーバーへリクエストを行うために使用するHttpClientを指定します。

ステップ3: ログインルートを追加する

oauthプロバイダーを設定した後、authenticate関数内にoauthプロバイダーの名前を受け入れる保護されたログインルートを作成する必要があります。Ktorがこのルートへのリクエストを受信すると、providerLookupで定義されたauthorizeUrlに自動的にリダイレクトされます。

kotlin
routing {
    authenticate("auth-oauth-google") {
        get("/login") {
            // Redirects to 'authorizeUrl' automatically
        }
    }
}

ユーザーは、Ktorアプリケーションに必要な権限レベルを示す認可ページを目にします。これらの権限は、providerLookupで指定されたdefaultScopesに依存します。

ステップ4: リダイレクトルートを追加する

ログインルートとは別に、ステップ2: OAuthプロバイダーの設定で指定されているように、urlProviderのリダイレクトルートを作成する必要があります。

このルート内では、call.principal関数を使用してOAuthAccessTokenResponseオブジェクトを取得できます。OAuthAccessTokenResponseを使用すると、OAuthサーバーから返されたトークンやその他のパラメーターにアクセスできます。

kotlin
    routing {
        authenticate("auth-oauth-google") {
            get("/login") {
                // Redirects to 'authorizeUrl' automatically
            }

            get("/callback") {
                val currentPrincipal: OAuthAccessTokenResponse.OAuth2? = call.principal()
                // redirects home if the url is not found before authorization
                currentPrincipal?.let { principal ->
                    principal.state?.let { state ->
                        call.sessions.set(UserSession(state, principal.accessToken))
                        redirects[state]?.let { redirect ->
                            call.respondRedirect(redirect)
                            return@get
                        }
                    }
                }
                call.respondRedirect("/home")
            }
        }
    }

この例では、トークンを受信した後に次のアクションが実行されます:

  • トークンはセッションに保存され、その内容は他のルート内からアクセスできます。
  • ユーザーはGoogle APIへのリクエストが行われる次のルートにリダイレクトされます。
  • 要求されたルートが見つからない場合、ユーザーは/homeルートにリダイレクトされます。

ステップ5: APIへリクエストを行う

リダイレクトルート内でトークンを受信し、セッションに保存した後、このトークンを使用して外部APIへリクエストを行うことができます。以下のコードスニペットは、HttpClientを使用してそのようなリクエストを行い、Authorizationヘッダーにこのトークンを送信することでユーザー情報を取得する方法を示しています。

リクエストを行い、レスポンスボディを返すgetPersonalGreetingという新しい関数を作成します:

kotlin
private suspend fun getPersonalGreeting(
    httpClient: HttpClient,
    userSession: UserSession
): UserInfo = httpClient.get("https://www.googleapis.com/oauth2/v2/userinfo") {
    headers {
        append(HttpHeaders.Authorization, "Bearer ${userSession.token}")
    }
}.body()

次に、getルート内でその関数を呼び出し、ユーザー情報を取得できます:

kotlin
get("/{path}") {
    val userSession: UserSession? = getSession(call)
    if (userSession != null) {
        val userInfo: UserInfo = getPersonalGreeting(httpClient, userSession)
        call.respondText("Hello, ${userInfo.name}!")
    }
}

完全に実行可能な例については、auth-oauth-googleを参照してください。