generated from JetBrains/compose-multiplatform-ios-android-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #52 from schachi5000/dev
Release
- Loading branch information
Showing
26 changed files
with
641 additions
and
322 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
10 changes: 0 additions & 10 deletions
10
shared/src/commonMain/kotlin/net/schacher/mcc/shared/TestClass.kt
This file was deleted.
Oops, something went wrong.
52 changes: 6 additions & 46 deletions
52
shared/src/commonMain/kotlin/net/schacher/mcc/shared/auth/AuthHandler.kt
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,16 @@ | ||
package net.schacher.mcc.shared.auth | ||
|
||
import co.touchlab.kermit.Logger | ||
import io.ktor.http.Url | ||
import net.schacher.mcc.shared.time.Time | ||
import net.schacher.mcc.shared.utils.debug | ||
import kotlin.time.Duration.Companion.seconds | ||
import kotlinx.coroutines.flow.StateFlow | ||
|
||
object AuthHandler { | ||
interface AuthHandler { | ||
|
||
const val APP_SCHEME = "mccapp" | ||
|
||
val loggedIn: Boolean | ||
get() = this.accessToken != null && | ||
(this.accessToken?.expiresAt ?: 0) > Time.currentTimeMillis | ||
val loginState: StateFlow<Boolean> | ||
|
||
val authHeader: String | ||
get() = "Bearer ${this.accessToken?.token ?: throw IllegalStateException("No access token available")}" | ||
|
||
var accessToken: AccessToken? = null | ||
private set(value) { | ||
field = value | ||
Logger.d { "Access token set to $value" } | ||
} | ||
|
||
|
||
fun handleCallbackUrl(callbackUrl: String) { | ||
val fixedCallbackUrl = callbackUrl.replace("#", "?") | ||
Logger.debug { "Handling callback url: $fixedCallbackUrl" } | ||
this.accessToken = try { | ||
TokenUtils.parseData(fixedCallbackUrl) | ||
} catch (e: Exception) { | ||
Logger.e(e) { "Error parsing access token from $fixedCallbackUrl" } | ||
null | ||
} | ||
} | ||
} | ||
|
||
fun isLoggedIn(): Boolean | ||
|
||
object TokenUtils { | ||
fun handleCallbackUrl(callbackUrl: String): Boolean | ||
|
||
fun parseData(callbackUrl: String): AccessToken = Url(callbackUrl).let { | ||
AccessToken( | ||
token = it.parameters["access_token"] | ||
?: throw IllegalArgumentException("No access token found"), | ||
expiresAt = it.parameters["expires_in"]?.toLongOrNull() | ||
?.let { Time.currentTimeMillis + it.seconds.inWholeMilliseconds } | ||
?: throw IllegalArgumentException("No expiration time found") | ||
) | ||
} | ||
fun logout() | ||
} | ||
|
||
data class AccessToken( | ||
val token: String, | ||
val expiresAt: Long | ||
) |
101 changes: 101 additions & 0 deletions
101
shared/src/commonMain/kotlin/net/schacher/mcc/shared/auth/PersistingAuthHandler.kt
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package net.schacher.mcc.shared.auth | ||
|
||
import co.touchlab.kermit.Logger | ||
import io.ktor.http.Url | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.StateFlow | ||
import kotlinx.coroutines.flow.asStateFlow | ||
import net.schacher.mcc.shared.datasource.database.SettingsDao | ||
import net.schacher.mcc.shared.time.Time | ||
import net.schacher.mcc.shared.utils.debug | ||
import kotlin.time.Duration.Companion.seconds | ||
|
||
class PersistingAuthHandler(private val settingsDao: SettingsDao) : AuthHandler { | ||
companion object { | ||
const val APP_SCHEME = "mccapp" | ||
private const val ACCESS_TOKEN = "access_token" | ||
private const val EXPIRES_AT = "access_token_expires_at" | ||
} | ||
|
||
private var accessToken: AccessToken? = null | ||
set(value) { | ||
field = value | ||
Logger.debug { "Access token set to $value" } | ||
|
||
this._loginState.value = this.isLoggedIn() | ||
} | ||
|
||
private val _loginState = MutableStateFlow(this.isLoggedIn()) | ||
|
||
override val loginState: StateFlow<Boolean> = _loginState.asStateFlow() | ||
|
||
init { | ||
this.restoreAccessToken() | ||
} | ||
|
||
override fun isLoggedIn(): Boolean = this.accessToken != null && | ||
(this.accessToken?.expiresAt ?: 0) > Time.currentTimeMillis | ||
|
||
override val authHeader: String | ||
get() = "Bearer ${this.accessToken?.token ?: throw IllegalStateException("No access token available")}" | ||
|
||
private fun restoreAccessToken() { | ||
val token = this.settingsDao.getString(ACCESS_TOKEN) | ||
val expiresAt = this.settingsDao.getString(EXPIRES_AT)?.toLongOrNull() | ||
|
||
if (token != null && expiresAt != null) { | ||
this.accessToken = AccessToken(token, expiresAt) | ||
} | ||
} | ||
|
||
override fun handleCallbackUrl(callbackUrl: String): Boolean { | ||
val fixedCallbackUrl = callbackUrl.replace("#", "?") | ||
Logger.debug { "Handling callback url: $fixedCallbackUrl" } | ||
|
||
this.accessToken = try { | ||
this.parseData(fixedCallbackUrl) | ||
} catch (e: Exception) { | ||
Logger.e(throwable = e) { "Error parsing access token from $fixedCallbackUrl" } | ||
null | ||
}?.also { | ||
this.storeAccessToken(it) | ||
} | ||
|
||
return this.accessToken != null | ||
} | ||
|
||
private fun parseData(callbackUrl: String): AccessToken = Url(callbackUrl).let { | ||
AccessToken( | ||
token = it.parameters["access_token"] | ||
?: throw IllegalArgumentException("No access token found"), | ||
expiresAt = it.parameters["expires_in"]?.toLongOrNull() | ||
?.let { Time.currentTimeMillis + it.seconds.inWholeMilliseconds } | ||
?: throw IllegalArgumentException("No expiration time found") | ||
) | ||
} | ||
|
||
override fun logout() { | ||
this.accessToken = null | ||
|
||
this.settingsDao.remove(ACCESS_TOKEN) | ||
this.settingsDao.remove(EXPIRES_AT) | ||
} | ||
|
||
|
||
private fun storeAccessToken(accessToken: AccessToken) { | ||
this.settingsDao.putString(ACCESS_TOKEN, accessToken.token) | ||
this.settingsDao.putString(EXPIRES_AT, accessToken.expiresAt.toString()) | ||
} | ||
} | ||
|
||
data class AccessToken( | ||
val token: String, | ||
val expiresAt: Long | ||
) { | ||
val remainingTime: Long | ||
get() = this.expiresAt - Time.currentTimeMillis | ||
|
||
override fun toString(): String { | ||
return "AccessToken(token='$token', expiresAt=$expiresAt, remainingTime=$remainingTime)" | ||
} | ||
} |
Oops, something went wrong.