Skip to content

Commit

Permalink
make OAuthProvider more powerful
Browse files Browse the repository at this point in the history
  • Loading branch information
angryziber committed Jan 8, 2024
1 parent c57a1b0 commit b366654
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 31 deletions.
44 changes: 17 additions & 27 deletions oauth/src/OAuthClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,19 @@ import klite.*
import klite.json.JsonHttpClient
import klite.json.JsonMapper
import klite.json.SnakeCase
import klite.oauth.OAuthProvider.GOOGLE
import java.net.URI
import java.net.http.HttpClient

abstract class OAuthClient(
class OAuthClient(
val provider: OAuthProvider,
httpClient: HttpClient
) {
private val http: JsonHttpClient = JsonHttpClient(json = JsonMapper(keys = SnakeCase), http = httpClient)
private val http = JsonHttpClient(json = JsonMapper(keys = SnakeCase), http = httpClient)
private val clientId = Config["${provider}_OAUTH_CLIENT_ID"]
private val clientSecret = Config["${provider}_OAUTH_CLIENT_SECRET"]
private val scope = Config.optional("${provider}_OAUTH_SCOPE", "email profile")

private val scope = Config["${provider}_OAUTH_SCOPE"]
private val authUrl = URI(Config["${provider}_OAUTH_URL"])
private val tokenUrl = Config["${provider}_OAUTH_TOKEN_URL"]
private val profileUrl = Config["${provider}_OAUTH_PROFILE_URL"]

fun startAuthUrl(state: String?, redirectUrl: URI, lang: String) = authUrl + mapOfNotNull(
fun startAuthUrl(state: String?, redirectUrl: URI, lang: String) = URI(provider.authUrl) + mapOfNotNull(
"response_type" to "code",
"response_mode" to "form_post",
"redirect_uri" to redirectUrl,
Expand All @@ -37,7 +32,7 @@ abstract class OAuthClient(
suspend fun refresh(refreshToken: String) = fetchTokenResponse("refresh_token", refreshToken)

private suspend fun fetchTokenResponse(grantType: String, code: String, redirectUrl: URI? = null): OAuthTokenResponse =
http.post(tokenUrl, urlEncodeParams(mapOf(
http.post(provider.tokenUrl, urlEncodeParams(mapOf(
"grant_type" to grantType,
(if (grantType == "authorization_code") "code" else grantType) to code,
"client_id" to clientId,
Expand All @@ -47,25 +42,20 @@ abstract class OAuthClient(
setHeader("Content-Type", "${MimeTypes.wwwForm}; charset=UTF-8")
}

protected suspend fun profileRequest(accessToken: String): Map<String, String> =
http.get(profileUrl) {
setHeader("Authorization", "Bearer $accessToken")
}

abstract suspend fun profile(accessToken: String): UserProfile
}

enum class OAuthProvider {
GOOGLE
suspend fun profile(token: OAuthTokenResponse) = provider.fetchProfile(http, token)
}

class GoogleOAuthClient(httpClient: HttpClient): OAuthClient(GOOGLE, httpClient) {
override suspend fun profile(accessToken: String): UserProfile {
val res = profileRequest(accessToken)
val firstName = res["givenName"] ?: ""
val lastName = res["familyName"] ?: ""
return UserProfile(provider, res["id"]!!, firstName, lastName, res["email"]?.let { Email(it) }, res["picture"]?.let { URI(it) })
}
enum class OAuthProvider(val authUrl: String, val tokenUrl: String, val profileUrl: String, val fetchProfile: suspend (http: JsonHttpClient, token: OAuthTokenResponse) -> UserProfile) {
GOOGLE(
Config.optional("GOOGLE_OAUTH_URL", "https://accounts.google.com/o/oauth2/v2/auth"),
Config.optional("GOOGLE_OAUTH_TOKEN_URL", "https://oauth2.googleapis.com/token"),
Config.optional("GOOGLE_OAUTH_PROFILE_URL", "https://www.googleapis.com/oauth2/v1/userinfo?alt=json"),
{ http, token ->
val res = http.get<Map<String, String>>(GOOGLE.profileUrl) { setHeader("Authorization", "Bearer " + token.accessToken) }
val firstName = res["givenName"] ?: ""
val lastName = res["familyName"] ?: ""
UserProfile(GOOGLE, res["id"]!!, firstName, lastName, res["email"]?.let { Email(it) }, res["picture"]?.let { URI(it) })
})
}

data class OAuthTokenResponse(val accessToken: String, val expiresIn: Int, val scope: String? = null, val tokenType: String? = null, val idToken: String? = null, val refreshToken: String? = null)
Expand Down
8 changes: 4 additions & 4 deletions oauth/src/OAuthRoutes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import klite.plus
import java.net.URI

open class OAuthRoutes(
private val oauthClient: GoogleOAuthClient, // TODO: support for other oauth clients
private val oauthClient: OAuthClient,
private val userRepository: UserRepository
) {
@GET open suspend fun start(e: HttpExchange) =
Expand All @@ -23,11 +23,11 @@ open class OAuthRoutes(
val originalUrl = state?.let { URI(it) }
if (code == null) e.redirectToLogin(originalUrl, "userCancelled")

val tokenResponse = oauthClient.authenticate(code, e.fullUrl)
val profile = oauthClient.profile(tokenResponse.accessToken)
val token = oauthClient.authenticate(code, e.fullUrl)
val profile = oauthClient.profile(token)
if (profile.email == null) e.redirectToLogin(originalUrl, "emailNotProvided")

val user = userRepository.by(profile.email) ?: userRepository.create(profile, tokenResponse, e.lang)
val user = userRepository.by(profile.email) ?: userRepository.create(profile, token, e.lang)
e.initSession(user)
e.redirect(originalUrl ?: URI("/"))
}
Expand Down

0 comments on commit b366654

Please sign in to comment.