OAuth
必須依存関係: io.ktor:ktor-server-auth
コード例: auth-oauth-google
OAuthは、アクセス委任のためのオープンな標準規格です。OAuthは、Google、Facebook、Twitterなどの外部プロバイダーを使用して、アプリケーションのユーザーを承認するために使用できます。
oauth
プロバイダーは認可コードフローをサポートしています。OAuthパラメーターを一箇所で設定でき、Ktorは必要なパラメーターとともに指定された認可サーバーへ自動的にリクエストを行います。
Ktorにおける認証と認可に関する一般的な情報は、Ktorサーバーでの認証と認可セクションで確認できます。
依存関係を追加する
OAuth
を使用するには、ビルドスクリプトにktor-server-auth
アーティファクトを含める必要があります:
セッションプラグインをインストールする
クライアントが保護されたリソースにアクセスしようとするたびに認可を要求するのを避けるため、認可が成功した際にアクセストークンをセッションに保存することができます。その後、保護されたルートのハンドラー内で現在のセッションからアクセストークンを取得し、それを使用してリソースを要求できます。
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認可フローは以下のようになります:
ユーザーがKtorアプリケーションのログインページを開きます。
Ktorは、特定のプロバイダーの認可ページへ自動的にリダイレクトし、必要なパラメーターを渡します:
- 選択されたプロバイダーのAPIにアクセスするために使用されるクライアントID。
- 認可完了後に開かれるKtorアプリケーションのページを指定するコールバックまたはリダイレクトURL。
- Ktorアプリケーションに必要なサードパーティリソースのスコープ。
- アクセストークン(認可コード)を取得するために使用されるグラントタイプ。
- CSRF攻撃を軽減し、ユーザーをリダイレクトするために使用される
state
パラメーター。 - 特定のプロバイダーに固有のオプションパラメーター。
認可ページには、Ktorアプリケーションに必要な権限レベルを示す同意画面が表示されます。これらの権限は、ステップ2: OAuthプロバイダーの設定で設定された指定スコープに依存します。
ユーザーが要求された権限を承認すると、認可サーバーは指定されたリダイレクトURLにリダイレクトし、認可コードを送信します。
Ktorは、指定されたアクセストークンURLに次のパラメーターを含めて、もう一度自動的にリクエストを行います:
- 認可コード。
- クライアントIDとクライアントシークレット。
認可サーバーはアクセストークンを返却して応答します。
クライアントはこのトークンを使用して、選択されたプロバイダーの必要なサービスへリクエストを行うことができます。ほとんどの場合、トークンは
Bearer
スキーマを使用してAuthorization
ヘッダーで送信されます。サービスはトークンを検証し、そのスコープを認可に利用して、要求されたデータを返します。
OAuthのインストール
oauth
認証プロバイダーをインストールするには、install
ブロック内でoauth関数を呼び出します。オプションで、プロバイダー名を指定できます。例えば、"auth-oauth-google"という名前でoauth
プロバイダーをインストールするには、以下のようになります:
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で認可クレデンシャルを作成する必要があります。
Google Cloud Consoleで認証情報ページを開きます。
認証情報を作成をクリックし、
OAuth クライアント ID
を選択します。ドロップダウンから
ウェブ アプリケーション
を選択します。次の設定を指定します:
- 承認済みのJavaScript生成元:
http://localhost:8080
。 - 承認済みのリダイレクトURI:
http://localhost:8080/callback
。 Ktorでは、urlProviderプロパティを使用して、認可完了時に開かれるリダイレクトルートを指定します。
- 承認済みのJavaScript生成元:
作成をクリックします。
表示されたダイアログで、作成されたクライアントIDとクライアントシークレットをコピーします。これらは
oauth
プロバイダーの設定に使用されます。
ステップ1: HTTPクライアントを作成する
oauth
プロバイダーを設定する前に、サーバーがOAuthサーバーにリクエストを行うために使用するHttpClientを作成する必要があります。ContentNegotiationクライアントプラグインとJSONシリアライザーは、APIへのリクエスト後に受信したJSONデータをデシリアライズするために必要です。
val applicationHttpClient = HttpClient(CIO) {
install(ContentNegotiation) {
json()
}
}
クライアントインスタンスは、サーバーテストで別のクライアントインスタンスを作成できるように、main
モジュール関数に渡されます。
fun Application.main(httpClient: HttpClient = applicationHttpClient) {
}
ステップ2: OAuthプロバイダーを設定する
以下のコードスニペットは、auth-oauth-google
という名前でoauth
プロバイダーを作成および設定する方法を示しています。
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
に自動的にリダイレクトされます。
routing {
authenticate("auth-oauth-google") {
get("/login") {
// Redirects to 'authorizeUrl' automatically
}
}
}
ユーザーは、Ktorアプリケーションに必要な権限レベルを示す認可ページを目にします。これらの権限は、providerLookupで指定されたdefaultScopes
に依存します。
ステップ4: リダイレクトルートを追加する
ログインルートとは別に、ステップ2: OAuthプロバイダーの設定で指定されているように、urlProvider
のリダイレクトルートを作成する必要があります。
このルート内では、call.principal
関数を使用してOAuthAccessTokenResponseオブジェクトを取得できます。OAuthAccessTokenResponse
を使用すると、OAuthサーバーから返されたトークンやその他のパラメーターにアクセスできます。
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
という新しい関数を作成します:
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
ルート内でその関数を呼び出し、ユーザー情報を取得できます:
get("/{path}") {
val userSession: UserSession? = getSession(call)
if (userSession != null) {
val userInfo: UserInfo = getPersonalGreeting(httpClient, userSession)
call.respondText("Hello, ${userInfo.name}!")
}
}
完全に実行可能な例については、auth-oauth-googleを参照してください。