mirror of https://github.com/tasks/tasks
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
4.5 KiB
Kotlin
138 lines
4.5 KiB
Kotlin
/*
|
|
* Copyright 2017 The AppAuth for Android Authors. All Rights Reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
* in compliance with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software distributed under the
|
|
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
|
* express or implied. See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package org.tasks.auth
|
|
|
|
import android.content.Context
|
|
import androidx.security.crypto.EncryptedSharedPreferences
|
|
import androidx.security.crypto.MasterKey
|
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
import net.openid.appauth.*
|
|
import org.json.JSONException
|
|
import timber.log.Timber
|
|
import java.util.concurrent.atomic.AtomicReference
|
|
import java.util.concurrent.locks.ReentrantLock
|
|
import javax.inject.Inject
|
|
import javax.inject.Singleton
|
|
|
|
@Singleton
|
|
class AuthStateManager @Inject constructor(@ApplicationContext private val context: Context) {
|
|
private val prefs = EncryptedSharedPreferences.create(
|
|
context,
|
|
STORE_NAME,
|
|
MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(),
|
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
)
|
|
private val prefsLock = ReentrantLock()
|
|
private val currentAuthState = AtomicReference<AuthState>()
|
|
|
|
fun signOut() {
|
|
// discard the authorization and token state, but retain the configuration and
|
|
// dynamic client registration (if applicable), to save from retrieving them again.
|
|
val currentState = current
|
|
val clearedState = currentState.authorizationServiceConfiguration
|
|
?.let { AuthState(it) }
|
|
?: return
|
|
if (currentState.lastRegistrationResponse != null) {
|
|
clearedState.update(currentState.lastRegistrationResponse)
|
|
}
|
|
replace(clearedState)
|
|
}
|
|
|
|
val current: AuthState
|
|
get() {
|
|
if (currentAuthState.get() != null) {
|
|
return currentAuthState.get()
|
|
}
|
|
val state = readState()
|
|
return if (currentAuthState.compareAndSet(null, state)) {
|
|
state
|
|
} else {
|
|
currentAuthState.get()
|
|
}
|
|
}
|
|
|
|
fun replace(state: AuthState): AuthState {
|
|
writeState(state)
|
|
currentAuthState.set(state)
|
|
return state
|
|
}
|
|
|
|
fun updateAfterAuthorization(
|
|
response: AuthorizationResponse?,
|
|
ex: AuthorizationException?
|
|
): AuthState {
|
|
val current = current
|
|
current.update(response, ex)
|
|
return replace(current)
|
|
}
|
|
|
|
fun updateAfterTokenResponse(
|
|
response: TokenResponse?,
|
|
ex: AuthorizationException?
|
|
): AuthState {
|
|
val current = current
|
|
current.update(response, ex)
|
|
return replace(current)
|
|
}
|
|
|
|
fun updateAfterRegistration(
|
|
response: RegistrationResponse?,
|
|
ex: AuthorizationException?
|
|
): AuthState {
|
|
val current = current
|
|
if (ex != null) {
|
|
return current
|
|
}
|
|
current.update(response)
|
|
return replace(current)
|
|
}
|
|
|
|
private fun readState(): AuthState {
|
|
prefsLock.lock()
|
|
return try {
|
|
val currentState = prefs.getString(KEY_STATE, null)
|
|
?: return AuthState()
|
|
try {
|
|
AuthState.jsonDeserialize(currentState)
|
|
} catch (ex: JSONException) {
|
|
Timber.w("Failed to deserialize stored auth state - discarding")
|
|
AuthState()
|
|
}
|
|
} finally {
|
|
prefsLock.unlock()
|
|
}
|
|
}
|
|
|
|
private fun writeState(state: AuthState?) {
|
|
prefsLock.lock()
|
|
try {
|
|
val editor = prefs.edit()
|
|
if (state == null) {
|
|
editor.remove(KEY_STATE)
|
|
} else {
|
|
editor.putString(KEY_STATE, state.jsonSerializeString())
|
|
}
|
|
check(editor.commit()) { "Failed to write state to shared prefs" }
|
|
} finally {
|
|
prefsLock.unlock()
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
private const val STORE_NAME = "AuthState"
|
|
private const val KEY_STATE = "state"
|
|
}
|
|
} |