Created
March 30, 2023 11:30
-
-
Save ylem/5fe3a3720739f3b22c5f546756fcfe0a to your computer and use it in GitHub Desktop.
OAuth in Kotlin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.util.Base64 | |
import com.google.gson.GsonBuilder | |
import kotlinx.coroutines.Dispatchers | |
import kotlinx.coroutines.flow.Flow | |
import kotlinx.coroutines.flow.catch | |
import kotlinx.coroutines.flow.flow | |
import kotlinx.coroutines.flow.map | |
import kotlinx.coroutines.flow.onEach | |
import okhttp3.* | |
import java.io.IOException | |
class OAuthLogin private constructor() { | |
private val baseUrl = "https://your-auth-server.com" | |
private val clientId = "your-client-id" | |
private val clientSecret = "your-client-secret" | |
private val scope = "your-scope" | |
private var accessToken: String? = null | |
private var refreshToken: String? = null | |
companion object { | |
val instance = OAuthLogin() | |
} | |
fun login(username: String, password: String): Flow<Unit> { | |
val parameters = mapOf( | |
"grant_type" to "password", | |
"username" to username, | |
"password" to password, | |
"scope" to scope | |
) | |
val headers = mapOf( | |
"Authorization" to "Basic ${base64EncodedCredentials()}" | |
) | |
val url = "$baseUrl/oauth/token" | |
val requestBody = FormBody.Builder().apply { | |
parameters.forEach { (key, value) -> | |
add(key, value) | |
} | |
}.build() | |
val request = Request.Builder().apply { | |
url(url) | |
post(requestBody) | |
headers(headers) | |
}.build() | |
return OkHttpClient().newCall(request).executeAsFlow() | |
.map { response -> | |
val gson = GsonBuilder().create() | |
val tokenResponse = gson.fromJson(response.body?.string(), TokenResponse::class.java) | |
accessToken = tokenResponse.accessToken | |
refreshToken = tokenResponse.refreshToken | |
} | |
.onEach { | |
// Save the access token and refresh token to local storage | |
} | |
.catch { e -> | |
// Handle error | |
} | |
} | |
fun refreshAccessToken(): Flow<Unit> { | |
val refreshToken = this.refreshToken ?: return flow { | |
throw IllegalStateException("Refresh token not available") | |
} | |
val parameters = mapOf( | |
"grant_type" to "refresh_token", | |
"refresh_token" to refreshToken | |
) | |
val headers = mapOf( | |
"Authorization" to "Basic ${base64EncodedCredentials()}" | |
) | |
val url = "$baseUrl/oauth/token" | |
val requestBody = FormBody.Builder().apply { | |
parameters.forEach { (key, value) -> | |
add(key, value) | |
} | |
}.build() | |
val request = Request.Builder().apply { | |
url(url) | |
post(requestBody) | |
headers(headers) | |
}.build() | |
return OkHttpClient().newCall(request).executeAsFlow() | |
.map { response -> | |
val gson = GsonBuilder().create() | |
val tokenResponse = gson.fromJson(response.body?.string(), TokenResponse::class.java) | |
accessToken = tokenResponse.accessToken | |
refreshToken = tokenResponse.refreshToken | |
} | |
.onEach { | |
// Save the access token and refresh token to local storage | |
} | |
.catch { e -> | |
// Handle error | |
} | |
} | |
private fun base64EncodedCredentials(): String { | |
val credentials = "$clientId:$clientSecret" | |
val credentialsData = credentials.toByteArray(Charsets.UTF_8) | |
val base64Credentials = Base64.encodeToString(credentialsData, Base64.DEFAULT) | |
return base64Credentials | |
} | |
data class TokenResponse( | |
val accessToken: String, | |
val refreshToken: String, | |
val expiresIn: Int | |
) | |
} | |
fun Call.executeAsFlow(): Flow<Response> = flow { | |
val response = execute() | |
if (!response.isSuccessful) { | |
throw IOException("Unexpected HTTP response |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment