Skip to content

Instantly share code, notes, and snippets.

@ylem
Created March 30, 2023 11:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ylem/5fe3a3720739f3b22c5f546756fcfe0a to your computer and use it in GitHub Desktop.
Save ylem/5fe3a3720739f3b22c5f546756fcfe0a to your computer and use it in GitHub Desktop.
OAuth in Kotlin
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