Kotlin에서 OAuth 철저한 입문 클라이언트 서버 구현
전제
Kotlin/jvm
Kotiln 1.3.50
ktor 1.2.4
authorization server, protected resource server는 OAuth 철저 입문의 ch-3-1 디렉토리에 있는 것을 사용한다.
node의 사정으로부터 authorization server가 정상적으로 동작하지 않기 때문에 github의 이슈 를 참고로 한다. 2019/11/03 시점에서 수정되지 않았습니다.
TOP 페이지
Application.kt get("/") {
call.respondHtml {
body {
h1 { +"OAuth徹底入門 with Kotlin" }
a(href = "http://localhost:9000/authorize") { +"AUTHORIZE" }
}
}
}
인증하기 전에 html을 준비한다.
Authorization Code를 요구.
Application.kt get("/authorize") {
call.respondRedirect(false) {
host = "localhost"
port = 9001
path("authorize")
parameters["response_type"] = "code"
parameters["client_id"] = ConstValue.CLIENT_ID
parameters["redirect_uri"] = ConstValue.REDIRECT_URI
val state = getRandomString()
DB.states[ConstValue.CLIENT_ID] = state
parameters["state"] = state
}
}
포인트는 위의 state 파라미터입니다. OAuth 철저 입문의 7장에서 해설되고 있습니다만 CSRF 대책을 위해 query 파라미터의 하나로서 부여할 필요가 있다.
또 생성되는 state에 대해서는 2^160 이상의 랜덤성을 가진 것이 추천되고 있다.
Application.ktfun getRandomString(): String {
return BigInteger(160, SecureRandom()).toString(32)
}
getRandomString 함수는 160자리의 2진수로 생성된 수치를 32진수의 문자열로 변환하여 생성하고 있다.
사용자는 Authorization Server에서 제공하는 권한 부여 UI를 사용하여 권한을 부여합니다.
Application.kt get("/callback") {
val code = call.request.queryParameters["code"]
val state = call.request.queryParameters["state"]
if (code == null || state == null || state != DB.states[ConstValue.CLIENT_ID]) {
throw Exception("invalid authorization code")
}
authorization 서버가 두드리는 callbackAPI에서 받은 state가 공격자에 의해 바뀐 것이 아닌지 검증한다.
token 요청
Application.kt val req = Request.Builder().apply {
val utf8 = StandardCharsets.UTF_8.toString()
val clientIdByteArray = URLEncoder.encode(ConstValue.CLIENT_ID, utf8)
val secretByteArray = URLEncoder.encode(ConstValue.CLIENT_SECRET, utf8)
addHeader("Content-Type", "application/x-www-form-urlencoded")
val idAndPass = Base64.encodeBase64String("$clientIdByteArray:$secretByteArray".toByteArray())
addHeader("Authorization", "Basic $idAndPass")
val body = "grant_type=authorization_code&code=$code&redirect_uri${ConstValue.REDIRECT_URI}".toRequestBody()
method("POST", body)
url("http://localhost:9001/token")
}.build()
val res = OkHttpClient().newCall(req).execute()
val responseBody = Gson().fromJson(res.body!!.string(), TokenResp::class.java)!!
ConstValue.TOKEN = responseBody.access_token
Authorization Code Grant에서는 Authorization 헤더를 이용하여 인증하는 것이 추천되고 있다. 이것은 token을 교환할 때도 마찬가지.
인증 방법에서 Basic이 사용되고 있는 점과 body가 application/json이 아닌 점이 익숙하지 않지만 본질에는 관계 없기 때문에 의식하지 않는다.
토큰 획득 성공
OAuth2로 보호되는 리소스 요청
Application.kt val req = Request.Builder().apply {
addHeader("Content-Type", "application/x-www-form-urlencoded")
addHeader("Authorization", "Bearer ${ConstValue.TOKEN}")
url("http://localhost:9002/resource")
method("POST", "".toRequestBody())
}.build()
val res = OkHttpClient().newCall(req).execute()
이번에는 토큰의 종류로서 Bearer 토큰이 지정되어 있으므로 Authorization 헤더에의 value의 prefix로서 "Bearer"를 붙인다.
Bearer 토큰은 base64로 인코딩되어야 합니다. client는 아무것도 확인하지 않고 사용될 수 있다.
요약
OAuth2는 허가 흐름에서 만들어진 프레임 워크입니다. OpenIDConnect와 동시에 사용되는 경우가 많기 때문에 인증, 인가를 할 수 있는 것으로 착각되지만 이번 사용한 「Authorization Code Grant」를 실시하기 위해 최적화된 것. 이 외에도 인증 흐름은 몇 가지 준비되었지만 취약점을 허용한다는 것을 인식합니다.
client측의 구현을 할 예정밖에 지금까지 없기 때문에 client를 kotlin에서 구현했지만 그 이외의 서버를 취급할 필요가 있을 때는 이번과 같이 다른 언어로 다시 구현해 보면 얻을 수 있는 경우가 많다 .
Reference
이 문제에 관하여(Kotlin에서 OAuth 철저한 입문 클라이언트 서버 구현), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/ginzro/items/6f5e9aa64e252d8f3faa
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Application.kt
get("/") {
call.respondHtml {
body {
h1 { +"OAuth徹底入門 with Kotlin" }
a(href = "http://localhost:9000/authorize") { +"AUTHORIZE" }
}
}
}
인증하기 전에 html을 준비한다.
Authorization Code를 요구.
Application.kt get("/authorize") {
call.respondRedirect(false) {
host = "localhost"
port = 9001
path("authorize")
parameters["response_type"] = "code"
parameters["client_id"] = ConstValue.CLIENT_ID
parameters["redirect_uri"] = ConstValue.REDIRECT_URI
val state = getRandomString()
DB.states[ConstValue.CLIENT_ID] = state
parameters["state"] = state
}
}
포인트는 위의 state 파라미터입니다. OAuth 철저 입문의 7장에서 해설되고 있습니다만 CSRF 대책을 위해 query 파라미터의 하나로서 부여할 필요가 있다.
또 생성되는 state에 대해서는 2^160 이상의 랜덤성을 가진 것이 추천되고 있다.
Application.ktfun getRandomString(): String {
return BigInteger(160, SecureRandom()).toString(32)
}
getRandomString 함수는 160자리의 2진수로 생성된 수치를 32진수의 문자열로 변환하여 생성하고 있다.
사용자는 Authorization Server에서 제공하는 권한 부여 UI를 사용하여 권한을 부여합니다.
Application.kt get("/callback") {
val code = call.request.queryParameters["code"]
val state = call.request.queryParameters["state"]
if (code == null || state == null || state != DB.states[ConstValue.CLIENT_ID]) {
throw Exception("invalid authorization code")
}
authorization 서버가 두드리는 callbackAPI에서 받은 state가 공격자에 의해 바뀐 것이 아닌지 검증한다.
token 요청
Application.kt val req = Request.Builder().apply {
val utf8 = StandardCharsets.UTF_8.toString()
val clientIdByteArray = URLEncoder.encode(ConstValue.CLIENT_ID, utf8)
val secretByteArray = URLEncoder.encode(ConstValue.CLIENT_SECRET, utf8)
addHeader("Content-Type", "application/x-www-form-urlencoded")
val idAndPass = Base64.encodeBase64String("$clientIdByteArray:$secretByteArray".toByteArray())
addHeader("Authorization", "Basic $idAndPass")
val body = "grant_type=authorization_code&code=$code&redirect_uri${ConstValue.REDIRECT_URI}".toRequestBody()
method("POST", body)
url("http://localhost:9001/token")
}.build()
val res = OkHttpClient().newCall(req).execute()
val responseBody = Gson().fromJson(res.body!!.string(), TokenResp::class.java)!!
ConstValue.TOKEN = responseBody.access_token
Authorization Code Grant에서는 Authorization 헤더를 이용하여 인증하는 것이 추천되고 있다. 이것은 token을 교환할 때도 마찬가지.
인증 방법에서 Basic이 사용되고 있는 점과 body가 application/json이 아닌 점이 익숙하지 않지만 본질에는 관계 없기 때문에 의식하지 않는다.
토큰 획득 성공
OAuth2로 보호되는 리소스 요청
Application.kt val req = Request.Builder().apply {
addHeader("Content-Type", "application/x-www-form-urlencoded")
addHeader("Authorization", "Bearer ${ConstValue.TOKEN}")
url("http://localhost:9002/resource")
method("POST", "".toRequestBody())
}.build()
val res = OkHttpClient().newCall(req).execute()
이번에는 토큰의 종류로서 Bearer 토큰이 지정되어 있으므로 Authorization 헤더에의 value의 prefix로서 "Bearer"를 붙인다.
Bearer 토큰은 base64로 인코딩되어야 합니다. client는 아무것도 확인하지 않고 사용될 수 있다.
요약
OAuth2는 허가 흐름에서 만들어진 프레임 워크입니다. OpenIDConnect와 동시에 사용되는 경우가 많기 때문에 인증, 인가를 할 수 있는 것으로 착각되지만 이번 사용한 「Authorization Code Grant」를 실시하기 위해 최적화된 것. 이 외에도 인증 흐름은 몇 가지 준비되었지만 취약점을 허용한다는 것을 인식합니다.
client측의 구현을 할 예정밖에 지금까지 없기 때문에 client를 kotlin에서 구현했지만 그 이외의 서버를 취급할 필요가 있을 때는 이번과 같이 다른 언어로 다시 구현해 보면 얻을 수 있는 경우가 많다 .
Reference
이 문제에 관하여(Kotlin에서 OAuth 철저한 입문 클라이언트 서버 구현), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/ginzro/items/6f5e9aa64e252d8f3faa
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
get("/authorize") {
call.respondRedirect(false) {
host = "localhost"
port = 9001
path("authorize")
parameters["response_type"] = "code"
parameters["client_id"] = ConstValue.CLIENT_ID
parameters["redirect_uri"] = ConstValue.REDIRECT_URI
val state = getRandomString()
DB.states[ConstValue.CLIENT_ID] = state
parameters["state"] = state
}
}
fun getRandomString(): String {
return BigInteger(160, SecureRandom()).toString(32)
}
get("/callback") {
val code = call.request.queryParameters["code"]
val state = call.request.queryParameters["state"]
if (code == null || state == null || state != DB.states[ConstValue.CLIENT_ID]) {
throw Exception("invalid authorization code")
}
Application.kt
val req = Request.Builder().apply {
val utf8 = StandardCharsets.UTF_8.toString()
val clientIdByteArray = URLEncoder.encode(ConstValue.CLIENT_ID, utf8)
val secretByteArray = URLEncoder.encode(ConstValue.CLIENT_SECRET, utf8)
addHeader("Content-Type", "application/x-www-form-urlencoded")
val idAndPass = Base64.encodeBase64String("$clientIdByteArray:$secretByteArray".toByteArray())
addHeader("Authorization", "Basic $idAndPass")
val body = "grant_type=authorization_code&code=$code&redirect_uri${ConstValue.REDIRECT_URI}".toRequestBody()
method("POST", body)
url("http://localhost:9001/token")
}.build()
val res = OkHttpClient().newCall(req).execute()
val responseBody = Gson().fromJson(res.body!!.string(), TokenResp::class.java)!!
ConstValue.TOKEN = responseBody.access_token
Authorization Code Grant에서는 Authorization 헤더를 이용하여 인증하는 것이 추천되고 있다. 이것은 token을 교환할 때도 마찬가지.
인증 방법에서 Basic이 사용되고 있는 점과 body가 application/json이 아닌 점이 익숙하지 않지만 본질에는 관계 없기 때문에 의식하지 않는다.
토큰 획득 성공
OAuth2로 보호되는 리소스 요청
Application.kt val req = Request.Builder().apply {
addHeader("Content-Type", "application/x-www-form-urlencoded")
addHeader("Authorization", "Bearer ${ConstValue.TOKEN}")
url("http://localhost:9002/resource")
method("POST", "".toRequestBody())
}.build()
val res = OkHttpClient().newCall(req).execute()
이번에는 토큰의 종류로서 Bearer 토큰이 지정되어 있으므로 Authorization 헤더에의 value의 prefix로서 "Bearer"를 붙인다.
Bearer 토큰은 base64로 인코딩되어야 합니다. client는 아무것도 확인하지 않고 사용될 수 있다.
요약
OAuth2는 허가 흐름에서 만들어진 프레임 워크입니다. OpenIDConnect와 동시에 사용되는 경우가 많기 때문에 인증, 인가를 할 수 있는 것으로 착각되지만 이번 사용한 「Authorization Code Grant」를 실시하기 위해 최적화된 것. 이 외에도 인증 흐름은 몇 가지 준비되었지만 취약점을 허용한다는 것을 인식합니다.
client측의 구현을 할 예정밖에 지금까지 없기 때문에 client를 kotlin에서 구현했지만 그 이외의 서버를 취급할 필요가 있을 때는 이번과 같이 다른 언어로 다시 구현해 보면 얻을 수 있는 경우가 많다 .
Reference
이 문제에 관하여(Kotlin에서 OAuth 철저한 입문 클라이언트 서버 구현), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/ginzro/items/6f5e9aa64e252d8f3faa
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
val req = Request.Builder().apply {
addHeader("Content-Type", "application/x-www-form-urlencoded")
addHeader("Authorization", "Bearer ${ConstValue.TOKEN}")
url("http://localhost:9002/resource")
method("POST", "".toRequestBody())
}.build()
val res = OkHttpClient().newCall(req).execute()
OAuth2는 허가 흐름에서 만들어진 프레임 워크입니다. OpenIDConnect와 동시에 사용되는 경우가 많기 때문에 인증, 인가를 할 수 있는 것으로 착각되지만 이번 사용한 「Authorization Code Grant」를 실시하기 위해 최적화된 것. 이 외에도 인증 흐름은 몇 가지 준비되었지만 취약점을 허용한다는 것을 인식합니다.
client측의 구현을 할 예정밖에 지금까지 없기 때문에 client를 kotlin에서 구현했지만 그 이외의 서버를 취급할 필요가 있을 때는 이번과 같이 다른 언어로 다시 구현해 보면 얻을 수 있는 경우가 많다 .
Reference
이 문제에 관하여(Kotlin에서 OAuth 철저한 입문 클라이언트 서버 구현), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/ginzro/items/6f5e9aa64e252d8f3faa텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)