153 lines
5.2 KiB
Kotlin
153 lines
5.2 KiB
Kotlin
package jp.juggler.subwaytooter.api.auth
|
|
|
|
import android.net.Uri
|
|
import jp.juggler.subwaytooter.R
|
|
import jp.juggler.subwaytooter.api.TootApiClient
|
|
import jp.juggler.subwaytooter.api.TootParser
|
|
import jp.juggler.subwaytooter.api.auth.AuthMisskey10.Companion.encodeScopeArray
|
|
import jp.juggler.subwaytooter.api.auth.AuthMisskey10.Companion.getScopeArrayMisskey
|
|
import jp.juggler.subwaytooter.api.entity.Host
|
|
import jp.juggler.subwaytooter.api.entity.TootInstance
|
|
import jp.juggler.subwaytooter.pref.PrefB
|
|
import jp.juggler.subwaytooter.pref.prefDevice
|
|
import jp.juggler.subwaytooter.push.FcmFlavor
|
|
import jp.juggler.subwaytooter.table.daoSavedAccount
|
|
import jp.juggler.subwaytooter.util.LinkHelper
|
|
import jp.juggler.util.data.JsonObject
|
|
import jp.juggler.util.data.notEmpty
|
|
import jp.juggler.util.log.LogCategory
|
|
import java.util.UUID
|
|
|
|
/**
|
|
* miauth と呼ばれている認証手順。
|
|
* STではMisskey 13から使用する。
|
|
*/
|
|
class AuthMisskey13(override val client: TootApiClient) : AuthBase() {
|
|
companion object {
|
|
private val log = LogCategory("MisskeyMiAuth")
|
|
private const val appIconUrl = "https://m1j.zzz.ac/subwaytooter-miauth-icon.png"
|
|
private const val callbackUrl = "${FcmFlavor.CUSTOM_SCHEME}://miauth/auth_callback"
|
|
|
|
fun isCallbackUrl(uriStr: String) = uriStr.startsWith(callbackUrl)
|
|
}
|
|
|
|
private val api10 = ApiAuthMisskey10(client)
|
|
private val api13 = ApiAuthMisskey13(client)
|
|
|
|
// 認証されたアカウントのユーザ情報を取得する
|
|
override suspend fun verifyAccount(
|
|
accessToken: String,
|
|
outTokenJson: JsonObject?,
|
|
misskeyVersion: Int,
|
|
): JsonObject = api10.verifyAccount(
|
|
apiHost = apiHost ?: error("missing apiHost"),
|
|
accessToken = accessToken,
|
|
).also {
|
|
// ユーザ情報を読めたならtokenInfoを保存する
|
|
outTokenJson?.apply {
|
|
put(KEY_AUTH_VERSION, AUTH_VERSION)
|
|
put(KEY_API_KEY_MISSKEY, accessToken)
|
|
put(KEY_MISSKEY_VERSION, misskeyVersion)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* クライアントを登録してブラウザで開くURLを生成する
|
|
* 成功したら TootApiResult.data にURL文字列を格納すること
|
|
*/
|
|
override suspend fun authStep1(
|
|
ti: TootInstance?,
|
|
forceUpdateClient: Boolean,
|
|
): Uri {
|
|
if (!PrefB.bpEnableDeprecatedSomething.value) {
|
|
error(context.getString(R.string.misskey_support_end))
|
|
}
|
|
|
|
val apiHost = apiHost ?: error("missing apiHost")
|
|
|
|
val sessionId = UUID.randomUUID().toString()
|
|
|
|
client.context.prefDevice.saveLastAuth(
|
|
host = apiHost.ascii,
|
|
secret = sessionId,
|
|
dbId = account?.db_id,
|
|
)
|
|
|
|
return api13.createAuthUrl(
|
|
apiHost = apiHost,
|
|
clientName = clientName,
|
|
iconUrl = appIconUrl,
|
|
callbackUrl = callbackUrl,
|
|
permission = getScopeArrayMisskey(ti).encodeScopeArray(),
|
|
sessionId = sessionId,
|
|
)
|
|
}
|
|
|
|
override suspend fun authStep2(uri: Uri): Auth2Result {
|
|
|
|
// 認証開始時に保存した情報
|
|
val prefDevice = client.context.prefDevice
|
|
val savedSessionId = prefDevice.lastAuthSecret
|
|
|
|
val apiHost = prefDevice.lastAuthInstance
|
|
?.let { Host.parse(it) }
|
|
?: error("missing apiHost")
|
|
|
|
when (val dbId = prefDevice.lastAuthDbId) {
|
|
// new registration
|
|
null -> client.apiHost = apiHost
|
|
|
|
// update access token
|
|
else -> {
|
|
val sa = daoSavedAccount.loadAccount(dbId)
|
|
?: error("missing account db_id=$dbId")
|
|
client.account = sa
|
|
}
|
|
}
|
|
|
|
// コールバックURLに含まれるセッションID
|
|
val sessionId = uri.getQueryParameter("session").notEmpty()
|
|
?: error("missing sessionId in callback URL")
|
|
|
|
if (sessionId != savedSessionId) {
|
|
error("auth session id not match.")
|
|
}
|
|
|
|
val ti = TootInstance.getOrThrow(client)
|
|
|
|
val misskeyVersion = ti.misskeyVersionMajor
|
|
|
|
val data = api13.checkAuthSession(apiHost, sessionId)
|
|
|
|
val ok = data.boolean("ok")
|
|
if (ok != true) {
|
|
error("Authentication result is not ok. [$ok]")
|
|
}
|
|
|
|
val apiKey = data.string("token")
|
|
?: error("missing token.")
|
|
log.i("apiKey=$apiKey")
|
|
|
|
val accountJson = data.jsonObject("user")
|
|
?: error("missing user.")
|
|
|
|
val user = TootParser(context, linkHelper = LinkHelper.create(ti))
|
|
.account(accountJson)
|
|
?: error("can't parse user json.")
|
|
|
|
prefDevice.removeLastAuth()
|
|
|
|
return Auth2Result(
|
|
tootInstance = ti,
|
|
tokenJson = JsonObject().apply {
|
|
user.id.putTo(this, KEY_USER_ID)
|
|
put(KEY_MISSKEY_VERSION, misskeyVersion)
|
|
put(KEY_AUTH_VERSION, AUTH_VERSION)
|
|
put(KEY_API_KEY_MISSKEY, apiKey)
|
|
},
|
|
accountJson = accountJson,
|
|
tootAccount = user,
|
|
)
|
|
}
|
|
}
|