Ktor + Cognito로 OAuth2 (OpenID Connect) 인증
인증 수단으로 Cognito의 OpenID Connect 인증을 사용했는데 미묘하게 맞지 않아서 공유합니다.
Google의 Auth2 인증 및 Github 예제는 공식에 나와 있습니다. 하지만, 간선 Ktor 초보자에게는 알기 어려운 쓰는 방법이었으므로 보다 상세한 예를 사용해 실장하는 것으로 알기 쉽게 했습니다.
이번 구현은 GitHub에 공개 하고 있습니다.
대상
OAuth2, OpenID Connect에 대해 대부분 이해합니다.
Ktor에서 OAuth2를 사용하면 어떻게됩니까? 한 사람
전제
Kotlin 1.3.50
Ktor 1.2.4
종속성 추가
빠른 시작에서 아무 것도 옵션을 선택하지 않은 상태에서 아래 두 줄을 추가합니다.
build.gradle // https://mvnrepository.com/artifact/io.ktor/ktor-auth
compile "io.ktor:ktor-auth:$ktor_version"
// https://mvnrepository.com/artifact/io.ktor/ktor-client-apache
compile "io.ktor:ktor-client-apache:$ktor_version"
ktor-auth는 OAuth2 인증을 사용하기 때문에 ktor-client-apache는 인증 흐름 중에 Cognito와 통신하는 데 사용됩니다.
Cognito
Cognito 튜토리얼 참조.
기본적으로는 기본 설정대로 문제 없다.
app-client를 만든 후 callback url을 설정합니다.
clientId,client secret를 확인한다.
서버 측 (OAuth2 관점에서 클라이언트)
다음은 전체 코드입니다.
Application.ktfun Application.module(testing: Boolean = false) {
install(Authentication) {
oauth(COGNITO) {
client = HttpClient()
providerLookup = {
val domain = getEnv("cognito.domain")
OAuthServerSettings.OAuth2ServerSettings(
name = "cognito",
authorizeUrl = "$domain/oauth2/authorize",
accessTokenUrl = "$domain/oauth2/token",
requestMethod = HttpMethod.Post,
clientId = getEnv("cognito.clientId"),
clientSecret = getEnv("cognito.clientSecret")
)
}
urlProvider = { "http://localhost:8080/login" }
}
}
routing {
authenticate(COGNITO) {
get("/login") {
val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>()
if (principal == null) {
call.respondRedirect("http://localhost:8080/")
} else {
val domain = getEnv("cognito.domain")
val client = HttpClient()
val result = client.get<String>("$domain/oauth2/userInfo") {
header("Authorization", "Bearer ${principal.accessToken}")
}
call.respondHtml {
body {
h1 { +"you are login." }
a { +result }
}
}
}
}
}
get("/") {
call.respondHtml {
head {
title { +"Login with" }
}
body {
h1 { +"Login with:" }
a(href = "/login") { +"cognito" }
}
}
}
}
}
상세하게 나누어 보겠습니다.
OAuth2 설정
Application.kt install(Authentication) {
// 中略
}
const val COGNITO = "cognito"
먼저 Ktor에게 인증 기능 사용을 선언합니다.
Application.kt oauth(COGNITO) {
client = HttpClient()
providerLookup = {
// 中略
}
urlProvider = { "http://localhost:8080/login" }
}
인증 모듈 중에도 OAuth2 기능을 사용합니다. oauth 이외에도 basic, form을 사용할 수 있습니다.
Kotlin 1.3.50
Ktor 1.2.4
종속성 추가
빠른 시작에서 아무 것도 옵션을 선택하지 않은 상태에서 아래 두 줄을 추가합니다.
build.gradle // https://mvnrepository.com/artifact/io.ktor/ktor-auth
compile "io.ktor:ktor-auth:$ktor_version"
// https://mvnrepository.com/artifact/io.ktor/ktor-client-apache
compile "io.ktor:ktor-client-apache:$ktor_version"
ktor-auth는 OAuth2 인증을 사용하기 때문에 ktor-client-apache는 인증 흐름 중에 Cognito와 통신하는 데 사용됩니다.
Cognito
Cognito 튜토리얼 참조.
기본적으로는 기본 설정대로 문제 없다.
app-client를 만든 후 callback url을 설정합니다.
clientId,client secret를 확인한다.
서버 측 (OAuth2 관점에서 클라이언트)
다음은 전체 코드입니다.
Application.ktfun Application.module(testing: Boolean = false) {
install(Authentication) {
oauth(COGNITO) {
client = HttpClient()
providerLookup = {
val domain = getEnv("cognito.domain")
OAuthServerSettings.OAuth2ServerSettings(
name = "cognito",
authorizeUrl = "$domain/oauth2/authorize",
accessTokenUrl = "$domain/oauth2/token",
requestMethod = HttpMethod.Post,
clientId = getEnv("cognito.clientId"),
clientSecret = getEnv("cognito.clientSecret")
)
}
urlProvider = { "http://localhost:8080/login" }
}
}
routing {
authenticate(COGNITO) {
get("/login") {
val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>()
if (principal == null) {
call.respondRedirect("http://localhost:8080/")
} else {
val domain = getEnv("cognito.domain")
val client = HttpClient()
val result = client.get<String>("$domain/oauth2/userInfo") {
header("Authorization", "Bearer ${principal.accessToken}")
}
call.respondHtml {
body {
h1 { +"you are login." }
a { +result }
}
}
}
}
}
get("/") {
call.respondHtml {
head {
title { +"Login with" }
}
body {
h1 { +"Login with:" }
a(href = "/login") { +"cognito" }
}
}
}
}
}
상세하게 나누어 보겠습니다.
OAuth2 설정
Application.kt install(Authentication) {
// 中略
}
const val COGNITO = "cognito"
먼저 Ktor에게 인증 기능 사용을 선언합니다.
Application.kt oauth(COGNITO) {
client = HttpClient()
providerLookup = {
// 中略
}
urlProvider = { "http://localhost:8080/login" }
}
인증 모듈 중에도 OAuth2 기능을 사용합니다. oauth 이외에도 basic, form을 사용할 수 있습니다.
// https://mvnrepository.com/artifact/io.ktor/ktor-auth
compile "io.ktor:ktor-auth:$ktor_version"
// https://mvnrepository.com/artifact/io.ktor/ktor-client-apache
compile "io.ktor:ktor-client-apache:$ktor_version"
Cognito 튜토리얼 참조.
기본적으로는 기본 설정대로 문제 없다.
app-client를 만든 후 callback url을 설정합니다.
clientId,client secret를 확인한다.
서버 측 (OAuth2 관점에서 클라이언트)
다음은 전체 코드입니다.
Application.ktfun Application.module(testing: Boolean = false) {
install(Authentication) {
oauth(COGNITO) {
client = HttpClient()
providerLookup = {
val domain = getEnv("cognito.domain")
OAuthServerSettings.OAuth2ServerSettings(
name = "cognito",
authorizeUrl = "$domain/oauth2/authorize",
accessTokenUrl = "$domain/oauth2/token",
requestMethod = HttpMethod.Post,
clientId = getEnv("cognito.clientId"),
clientSecret = getEnv("cognito.clientSecret")
)
}
urlProvider = { "http://localhost:8080/login" }
}
}
routing {
authenticate(COGNITO) {
get("/login") {
val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>()
if (principal == null) {
call.respondRedirect("http://localhost:8080/")
} else {
val domain = getEnv("cognito.domain")
val client = HttpClient()
val result = client.get<String>("$domain/oauth2/userInfo") {
header("Authorization", "Bearer ${principal.accessToken}")
}
call.respondHtml {
body {
h1 { +"you are login." }
a { +result }
}
}
}
}
}
get("/") {
call.respondHtml {
head {
title { +"Login with" }
}
body {
h1 { +"Login with:" }
a(href = "/login") { +"cognito" }
}
}
}
}
}
상세하게 나누어 보겠습니다.
OAuth2 설정
Application.kt install(Authentication) {
// 中略
}
const val COGNITO = "cognito"
먼저 Ktor에게 인증 기능 사용을 선언합니다.
Application.kt oauth(COGNITO) {
client = HttpClient()
providerLookup = {
// 中略
}
urlProvider = { "http://localhost:8080/login" }
}
인증 모듈 중에도 OAuth2 기능을 사용합니다. oauth 이외에도 basic, form을 사용할 수 있습니다.
fun Application.module(testing: Boolean = false) {
install(Authentication) {
oauth(COGNITO) {
client = HttpClient()
providerLookup = {
val domain = getEnv("cognito.domain")
OAuthServerSettings.OAuth2ServerSettings(
name = "cognito",
authorizeUrl = "$domain/oauth2/authorize",
accessTokenUrl = "$domain/oauth2/token",
requestMethod = HttpMethod.Post,
clientId = getEnv("cognito.clientId"),
clientSecret = getEnv("cognito.clientSecret")
)
}
urlProvider = { "http://localhost:8080/login" }
}
}
routing {
authenticate(COGNITO) {
get("/login") {
val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>()
if (principal == null) {
call.respondRedirect("http://localhost:8080/")
} else {
val domain = getEnv("cognito.domain")
val client = HttpClient()
val result = client.get<String>("$domain/oauth2/userInfo") {
header("Authorization", "Bearer ${principal.accessToken}")
}
call.respondHtml {
body {
h1 { +"you are login." }
a { +result }
}
}
}
}
}
get("/") {
call.respondHtml {
head {
title { +"Login with" }
}
body {
h1 { +"Login with:" }
a(href = "/login") { +"cognito" }
}
}
}
}
}
install(Authentication) {
// 中略
}
const val COGNITO = "cognito"
oauth(COGNITO) {
client = HttpClient()
providerLookup = {
// 中略
}
urlProvider = { "http://localhost:8080/login" }
}
Authorization Server 설정
callbackURL
Application.kt
providerLookup = {
val domain = getEnv("cognito.domain")
OAuthServerSettings.OAuth2ServerSettings(
name = "cognito",
authorizeUrl = "$domain/oauth2/authorize",
accessTokenUrl = "$domain/oauth2/token",
requestMethod = HttpMethod.Post,
clientId = getEnv("cognito.clientId"),
clientSecret = getEnv("cognito.clientSecret")
)
}
Authorization Server를 설정합니다. 기본적으로 인수명대로입니다만 requestMethod는 default에서는 Get가 설정s라고 하고 있어 이번 사용하는 Cognito는 token 리퀘스트시에 Post method 지정 때문에 명시적으로 requestMethod를 지정합니다.
이것으로 OAuth2 설정이 완료되었습니다.
Routing 설정
Application.kt
routing {
authenticate(COGNITO) {
get("/login") {
// 中略
}
}
get("/") {
// 中略
}
}
인증을 하고 싶은 장소에 방금 설정한 인증명을 이용해 authenticate로 wrap 합니다./login에 액세스했을 때에는 인증되어 있는지 확인하여 OAuth2 인증을 합니다. 뭐든지 dsl 기법이 되어 있는 것이 Ktor같네요.
Applicaiton.kt
val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>()
if (principal == null) {
call.respondRedirect("http://localhost:8080/")
} else {
val domain = getEnv("cognito.domain")
val client = HttpClient()
val result = client.get<String>("$domain/oauth2/userInfo") {
header("Authorization", "Bearer ${principal.accessToken}")
}
call.respondHtml {
body {
h1 { +"you are login." }
a { +result }
}
}
}
나중에 route 내에서 자동으로 얻은 principal을 사용하여 실제로 사용할 수 있는지 userInfo 엔드 포인트에 요청을 던져 보겠습니다.
잘하면 위와 같이 사용자 정보를 얻을 수 있습니다.
요약
이번에는 access token을 취득했을 뿐이므로 실제로 사용자 인증으로 사용하려면 jwt를 사용해야하지만 내용 복잡화한다고 판단했기 때문에 그만두었습니다. Spring Boot의 OAuth2 인증 또한 시도했지만 그보다 복잡성이 적습니다.
후에 HTML DSL이 좋다! . 이번과 같이 간단한 HTML을 필요로 하는 구현을 코드 위에서 바삭바삭하게 한다고 하는 것은 프로토타이핑이 보다 쉬워지고 있는 것을 느낍니다. 실제로 개인 개발에서 사용하고 있습니다만 화면 흐름의 확인에 관해서는 이것을 사용해 어느 정도 할 수 있으므로 마음에 듭니다.
Reference
이 문제에 관하여(Ktor + Cognito로 OAuth2 (OpenID Connect) 인증), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/ginzro/items/ac719a0bf6db946f16c4
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(Ktor + Cognito로 OAuth2 (OpenID Connect) 인증), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/ginzro/items/ac719a0bf6db946f16c4텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)