mirror of
https://github.com/microg/android_packages_apps_GmsCore
synced 2026-08-06 14:26:05 -04:00
Auth: Improve AuthorizationService (#3421)
This commit is contained in:
parent
a46b32f515
commit
4425514509
6 changed files with 215 additions and 105 deletions
|
|
@ -94,6 +94,8 @@ public class AuthRequest extends HttpFormClient.Request {
|
|||
public String oauth2IncludeProfile;
|
||||
@RequestContent("oauth2_include_email")
|
||||
public String oauth2IncludeEmail;
|
||||
@RequestContent("include_granted_scopes")
|
||||
public String includeGrantedScopes;
|
||||
@HttpFormClient.RequestContentDynamic
|
||||
public Map<Object, Object> dynamicFields;
|
||||
|
||||
|
|
@ -238,6 +240,11 @@ public class AuthRequest extends HttpFormClient.Request {
|
|||
return this;
|
||||
}
|
||||
|
||||
public AuthRequest includeGrantedScopes(String includeGrantedScopes) {
|
||||
this.includeGrantedScopes = includeGrantedScopes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthRequest oauth2Prompt(String oauth2Prompt) {
|
||||
this.oauth2Prompt = oauth2Prompt;
|
||||
return this;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ public class AuthManager {
|
|||
private String tokenRequestOptions;
|
||||
public String includeEmail;
|
||||
public String includeProfile;
|
||||
public String includeGrantedScopes;
|
||||
public boolean isGmsApp;
|
||||
public boolean ignoreStoredPermission = false;
|
||||
public boolean forceRefreshToken = false;
|
||||
|
|
@ -339,6 +340,7 @@ public class AuthManager {
|
|||
.oauth2Prompt(oauth2Prompt)
|
||||
.oauth2IncludeProfile(includeProfile)
|
||||
.oauth2IncludeEmail(includeEmail)
|
||||
.includeGrantedScopes(includeGrantedScopes)
|
||||
.itCaveatTypes(itCaveatTypes)
|
||||
.tokenRequestOptions(tokenRequestOptions)
|
||||
.systemPartition(isSystemApp())
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
package org.microg.gms.auth.credentials.identity
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||
|
|
@ -27,6 +28,7 @@ import com.google.android.gms.auth.api.identity.internal.IVerifyWithGoogleCallba
|
|||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
||||
import com.google.android.gms.auth.api.signin.internal.SignInConfiguration
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.api.CommonStatusCodes
|
||||
import com.google.android.gms.common.api.Scope
|
||||
import com.google.android.gms.common.api.Status
|
||||
import com.google.android.gms.common.api.internal.IStatusCallback
|
||||
|
|
@ -35,11 +37,15 @@ import com.google.android.gms.common.internal.GetServiceRequest
|
|||
import com.google.android.gms.common.internal.IGmsCallbacks
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.microg.gms.BaseService
|
||||
import org.microg.gms.auth.AuthConstants
|
||||
import org.microg.gms.auth.credentials.FEATURES
|
||||
import org.microg.gms.auth.signin.AuthSignInActivity
|
||||
import org.microg.gms.auth.signin.SignInConfigurationService
|
||||
import org.microg.gms.auth.signin.checkAccountAuthStatus
|
||||
import org.microg.gms.auth.signin.getOAuthManager
|
||||
import org.microg.gms.auth.signin.getServerAuthTokenManager
|
||||
import org.microg.gms.auth.signin.performSignIn
|
||||
|
|
@ -48,18 +54,19 @@ import org.microg.gms.common.AccountUtils
|
|||
import org.microg.gms.common.Constants
|
||||
import org.microg.gms.common.GmsService
|
||||
import org.microg.gms.common.PackageUtils
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
private const val TAG = "AuthorizationService"
|
||||
private const val REVOKE_ENDPOINT = "https://oauth2.googleapis.com/revoke"
|
||||
|
||||
class AuthorizationService : BaseService(TAG, GmsService.AUTH_API_IDENTITY_AUTHORIZATION) {
|
||||
|
||||
override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) {
|
||||
Log.d(TAG, "handleServiceRequest start ")
|
||||
val packageName = PackageUtils.getAndCheckCallingPackage(this, request.packageName)
|
||||
?: throw IllegalArgumentException("Missing package name")
|
||||
val connectionInfo = ConnectionInfo()
|
||||
connectionInfo.features = FEATURES
|
||||
val packageName = PackageUtils.getAndCheckCallingPackage(this, request.packageName) ?: throw IllegalArgumentException("Missing package name")
|
||||
val connectionInfo = ConnectionInfo().apply { features = FEATURES }
|
||||
callback.onPostInitCompleteWithConnectionInfo(
|
||||
ConnectionResult.SUCCESS, AuthorizationServiceImpl(this, packageName, this.lifecycle).asBinder(), connectionInfo
|
||||
)
|
||||
|
|
@ -68,115 +75,201 @@ class AuthorizationService : BaseService(TAG, GmsService.AUTH_API_IDENTITY_AUTHO
|
|||
|
||||
class AuthorizationServiceImpl(val context: Context, val packageName: String, override val lifecycle: Lifecycle) : IAuthorizationService.Stub(), LifecycleOwner {
|
||||
|
||||
companion object{
|
||||
companion object {
|
||||
private val nextRequestCode = AtomicInteger(0)
|
||||
private val httpClient: OkHttpClient by lazy {
|
||||
OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS).build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun authorize(callback: IAuthorizationCallback?, request: AuthorizationRequest?) {
|
||||
Log.d(TAG, "Method: authorize called, packageName:$packageName request:$request")
|
||||
Log.d(TAG, "authorize called, packageName=$packageName request=$request")
|
||||
lifecycleScope.launchWhenStarted {
|
||||
val requestAccount = request?.account
|
||||
val account = requestAccount ?: AccountUtils.get(context).getSelectedAccount(packageName)
|
||||
val googleSignInOptions = GoogleSignInOptions.Builder().apply {
|
||||
request?.requestedScopes?.forEach { requestScopes(it) }
|
||||
if (request?.idTokenRequested == true && request.serverClientId != null) {
|
||||
if (account?.name != requestAccount?.name) {
|
||||
requestEmail().requestProfile()
|
||||
}
|
||||
requestIdToken(request.serverClientId)
|
||||
}
|
||||
if (request?.serverAuthCodeRequested == true && request.serverClientId != null) requestServerAuthCode(request.serverClientId, request.forceCodeForRefreshToken)
|
||||
}.build()
|
||||
Log.d(TAG, "authorize: account: ${account?.name}")
|
||||
val result = if (account != null) {
|
||||
val (accessToken, signInAccount) = performSignIn(context, packageName, googleSignInOptions, account, false)
|
||||
if (requestAccount != null) {
|
||||
AccountUtils.get(context).saveSelectedAccount(packageName, requestAccount)
|
||||
}
|
||||
AuthorizationResult(
|
||||
signInAccount?.serverAuthCode,
|
||||
accessToken,
|
||||
signInAccount?.idToken,
|
||||
signInAccount?.grantedScopes?.toList().orEmpty().map { it.scopeUri },
|
||||
signInAccount,
|
||||
null
|
||||
)
|
||||
} else {
|
||||
val options = GoogleSignInOptions.Builder(googleSignInOptions).apply {
|
||||
val defaultAccount = SignInConfigurationService.getDefaultAccount(context, packageName)
|
||||
defaultAccount?.name?.let { setAccountName(it) }
|
||||
}.build()
|
||||
val intent = Intent(context, AuthSignInActivity::class.java).apply {
|
||||
`package` = Constants.GMS_PACKAGE_NAME
|
||||
putExtra("config", SignInConfiguration(packageName, options))
|
||||
}
|
||||
AuthorizationResult(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
request?.requestedScopes.orEmpty().map { it.scopeUri },
|
||||
null,
|
||||
PendingIntent.getActivity(context, nextRequestCode.incrementAndGet(), intent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
|
||||
)
|
||||
}
|
||||
runCatching {
|
||||
callback?.onAuthorized(Status.SUCCESS, result.also { Log.d(TAG, "authorize: result:$it") })
|
||||
try {
|
||||
val result = performAuthorize(request)
|
||||
Log.d(TAG, "authorize resolved: ${if (result.pendingIntent != null) "pendingIntent" else "silent"}, grantedScopes=${result.grantedScopes.size}")
|
||||
runCatching { callback?.onAuthorized(Status.SUCCESS, result) }
|
||||
} catch (e: InvalidAccountException) {
|
||||
Log.w(TAG, "authorize: invalid account", e)
|
||||
runCatching { callback?.onAuthorized(Status(CommonStatusCodes.INVALID_ACCOUNT), null) }
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "authorize failed, falling back to PendingIntent", e)
|
||||
runCatching { callback?.onAuthorized(Status.SUCCESS, buildPendingIntentResult(request)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun performAuthorize(request: AuthorizationRequest?): AuthorizationResult {
|
||||
require(request?.requestedScopes?.isNotEmpty() == true) { "requestedScopes cannot be null or empty" }
|
||||
|
||||
val requestAccount = request!!.account
|
||||
val candidate = requestAccount ?: AccountUtils.get(context).getSelectedAccount(packageName) ?: SignInConfigurationService.getDefaultAccount(context, packageName)
|
||||
if (candidate == null || request.forceCodeForRefreshToken) {
|
||||
return buildPendingIntentResult(request)
|
||||
}
|
||||
|
||||
val account = AccountManager.get(context).getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE).firstOrNull { it == candidate } ?: run {
|
||||
AccountUtils.get(context).removeSelectedAccount(packageName)
|
||||
return buildPendingIntentResult(request)
|
||||
}
|
||||
|
||||
val hostedDomain = request.hostedDomainFilter
|
||||
if (!hostedDomain.isNullOrEmpty() && !account.name.lowercase(Locale.ROOT).endsWith("@${hostedDomain.lowercase(Locale.ROOT)}")) {
|
||||
throw InvalidAccountException("account ${account.name} does not match hostedDomainFilter=$hostedDomain")
|
||||
}
|
||||
|
||||
val crossAccount = requestAccount != null && account.name != requestAccount.name
|
||||
val options = buildSignInOptions(request, crossAccount)
|
||||
val includeGrantedScopes = if (request.offlineAccess) "0" else "1"
|
||||
val (accessToken, signInAccount) = performSignIn(context, packageName, options, account, false, includeGrantedScopes = includeGrantedScopes)
|
||||
if (accessToken == null || signInAccount == null) {
|
||||
return buildPendingIntentResult(request)
|
||||
}
|
||||
|
||||
if (requestAccount != null) {
|
||||
AccountUtils.get(context).saveSelectedAccount(packageName, requestAccount)
|
||||
}
|
||||
|
||||
return AuthorizationResult(
|
||||
signInAccount.serverAuthCode,
|
||||
accessToken,
|
||||
signInAccount.idToken,
|
||||
signInAccount.grantedScopes.toList().map { it.scopeUri },
|
||||
signInAccount,
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildSignInOptions(request: AuthorizationRequest, crossAccount: Boolean): GoogleSignInOptions {
|
||||
return GoogleSignInOptions.Builder().apply {
|
||||
request.requestedScopes?.forEach { requestScopes(it) }
|
||||
val clientId = request.serverClientId
|
||||
if (request.idTokenRequested && clientId != null) {
|
||||
if (crossAccount) requestEmail().requestProfile()
|
||||
requestIdToken(clientId)
|
||||
}
|
||||
if (request.serverAuthCodeRequested && clientId != null) {
|
||||
requestServerAuthCode(clientId, request.forceCodeForRefreshToken)
|
||||
}
|
||||
}.build()
|
||||
}
|
||||
|
||||
private suspend fun buildPendingIntentResult(request: AuthorizationRequest?): AuthorizationResult {
|
||||
val defaultAccountName = SignInConfigurationService.getDefaultAccount(context, packageName)?.name
|
||||
val options = GoogleSignInOptions.Builder().apply {
|
||||
request?.requestedScopes?.forEach { requestScopes(it) }
|
||||
val clientId = request?.serverClientId
|
||||
if (request?.idTokenRequested == true && clientId != null) {
|
||||
requestEmail().requestProfile().requestIdToken(clientId)
|
||||
}
|
||||
if (request?.serverAuthCodeRequested == true && clientId != null) {
|
||||
requestServerAuthCode(clientId, request.forceCodeForRefreshToken)
|
||||
}
|
||||
defaultAccountName?.let { setAccountName(it) }
|
||||
}.build()
|
||||
val intent = Intent(context, AuthSignInActivity::class.java).apply {
|
||||
`package` = Constants.GMS_PACKAGE_NAME
|
||||
putExtra("config", SignInConfiguration(packageName, options))
|
||||
}
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
nextRequestCode.incrementAndGet(),
|
||||
intent,
|
||||
FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE,
|
||||
)
|
||||
return AuthorizationResult(
|
||||
null, null, null,
|
||||
request?.requestedScopes.orEmpty().map { it.scopeUri },
|
||||
null,
|
||||
pendingIntent,
|
||||
)
|
||||
}
|
||||
|
||||
override fun verifyWithGoogle(callback: IVerifyWithGoogleCallback?, request: VerifyWithGoogleRequest?) {
|
||||
Log.d(TAG, "unimplemented Method: verifyWithGoogle: request:$request")
|
||||
Log.d(TAG, "verifyWithGoogle called, request=$request")
|
||||
lifecycleScope.launchWhenStarted {
|
||||
val account = AccountUtils.get(context).getSelectedAccount(packageName) ?: SignInConfigurationService.getDefaultAccount(context, packageName)
|
||||
if (account == null) {
|
||||
Log.d(TAG, "Method: authorize called, but account is null")
|
||||
callback?.onVerifed(Status.CANCELED, null)
|
||||
return@launchWhenStarted
|
||||
}
|
||||
if (request?.offlineAccess == true && request.serverClientId != null) {
|
||||
val googleSignInOptions = GoogleSignInOptions.Builder().apply {
|
||||
request.requestedScopes?.forEach { requestScopes(it) }
|
||||
requestServerAuthCode(request.serverClientId)
|
||||
}.build()
|
||||
val authResponse = getServerAuthTokenManager(context, packageName, googleSignInOptions, account)?.let {
|
||||
withContext(Dispatchers.IO) { it.requestAuth(true) }
|
||||
}
|
||||
callback?.onVerifed(Status.SUCCESS, VerifyWithGoogleResult().apply {
|
||||
serverAuthToken = authResponse?.auth
|
||||
grantedScopes = authResponse?.grantedScopes?.split(" ")?.map { Scope(it) }?.toList() ?: googleSignInOptions.scopeUris.toList()
|
||||
})
|
||||
return@launchWhenStarted
|
||||
}
|
||||
callback?.onVerifed(Status.CANCELED, null)
|
||||
val result = runCatching { performVerify(request) }.onFailure { Log.w(TAG, "verifyWithGoogle failed", it) }.getOrNull()
|
||||
val status = if (result != null) Status.SUCCESS else Status.CANCELED
|
||||
runCatching { callback?.onVerifed(status, result) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun revokeAccess(callback: IStatusCallback?, request: RevokeAccessRequest?) {
|
||||
Log.d(TAG, "Method: revokeAccess called, request:$request")
|
||||
lifecycleScope.launchWhenStarted {
|
||||
val authOptions = SignInConfigurationService.getAuthOptions(context, packageName)
|
||||
val authAccount = request?.account
|
||||
if (authOptions.isNotEmpty() && authAccount != null) {
|
||||
val authManager = getOAuthManager(context, packageName, authOptions.first(), authAccount)
|
||||
val token = authManager.peekAuthToken()
|
||||
if (token != null) {
|
||||
// todo "https://oauth2.googleapis.com/revoke"
|
||||
authManager.invalidateAuthToken(token)
|
||||
authManager.isPermitted = false
|
||||
}
|
||||
private suspend fun performVerify(request: VerifyWithGoogleRequest?): VerifyWithGoogleResult? {
|
||||
val req = request?.takeIf { it.requestedScopes?.isNotEmpty() == true } ?: return null
|
||||
val account = AccountUtils.get(context).getSelectedAccount(packageName) ?: SignInConfigurationService.getDefaultAccount(context, packageName) ?: return null
|
||||
|
||||
val options = GoogleSignInOptions.Builder().apply {
|
||||
req.requestedScopes?.forEach { requestScopes(it) }
|
||||
if (req.offlineAccess && req.serverClientId != null) {
|
||||
requestServerAuthCode(req.serverClientId)
|
||||
}
|
||||
AccountUtils.get(context).removeSelectedAccount(packageName)
|
||||
}.build()
|
||||
|
||||
if (req.offlineAccess && req.serverClientId != null) {
|
||||
val authResponse = getServerAuthTokenManager(context, packageName, options, account)?.let {
|
||||
withContext(Dispatchers.IO) { it.requestAuth(true) }
|
||||
} ?: return null
|
||||
if (authResponse.auth == null) return null
|
||||
return VerifyWithGoogleResult().apply {
|
||||
serverAuthToken = authResponse.auth
|
||||
grantedScopes = authResponse.grantedScopes?.split(" ")?.map { Scope(it) } ?: options.scopeUris.toList()
|
||||
}
|
||||
}
|
||||
|
||||
val granted = checkAccountAuthStatus(context, packageName, options.scopes.toList(), account)
|
||||
if (!granted) return null
|
||||
return VerifyWithGoogleResult().apply { grantedScopes = options.scopeUris.toList() }
|
||||
}
|
||||
|
||||
override fun revokeAccess(callback: IStatusCallback?, request: RevokeAccessRequest?) {
|
||||
Log.d(TAG, "revokeAccess called, request=$request")
|
||||
lifecycleScope.launchWhenStarted {
|
||||
runCatching { performRevoke(request) }.onFailure { Log.w(TAG, "revokeAccess failed", it) }
|
||||
runCatching { callback?.onResult(Status.SUCCESS) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearToken(callback: IStatusCallback?, request: ClearTokenRequest?) {
|
||||
Log.d(TAG, "Method: clearToken called, request:$request")
|
||||
request?.token?.let {
|
||||
AccountManager.get(context).invalidateAuthToken(AuthConstants.DEFAULT_ACCOUNT_TYPE, it)
|
||||
private suspend fun performRevoke(request: RevokeAccessRequest?) {
|
||||
val account: Account? = request?.account
|
||||
?: AccountUtils.get(context).getSelectedAccount(packageName)
|
||||
?: SignInConfigurationService.getDefaultAccount(context, packageName)
|
||||
|
||||
if (account != null) {
|
||||
val authOptions = SignInConfigurationService.getAuthOptions(context, packageName)
|
||||
for (options in authOptions) {
|
||||
val authManager = getOAuthManager(context, packageName, options, account)
|
||||
val token = authManager.peekAuthToken() ?: continue
|
||||
runCatching { revokeTokenRemotely(token) }.onFailure { Log.w(TAG, "remote revoke failed (continuing local invalidate)", it) }
|
||||
authManager.invalidateAuthToken(token)
|
||||
authManager.isPermitted = false
|
||||
}
|
||||
}
|
||||
runCatching { callback?.onResult(Status.SUCCESS) }
|
||||
|
||||
AccountUtils.get(context).removeSelectedAccount(packageName)
|
||||
SignInConfigurationService.setAuthInfo(context, packageName, null, null)
|
||||
}
|
||||
|
||||
private suspend fun revokeTokenRemotely(token: String) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val body = FormBody.Builder().add("token", token).build()
|
||||
val request = Request.Builder().url(REVOKE_ENDPOINT).post(body).build()
|
||||
httpClient.newCall(request).execute().use { response ->
|
||||
Log.d(TAG, "revoke endpoint status=${response.code}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearToken(callback: IStatusCallback?, request: ClearTokenRequest?) {
|
||||
Log.d(TAG, "clearToken called, request=$request")
|
||||
lifecycleScope.launchWhenStarted {
|
||||
runCatching {
|
||||
request?.token?.takeIf { it.isNotEmpty() }?.let {
|
||||
AccountManager.get(context).invalidateAuthToken(AuthConstants.DEFAULT_ACCOUNT_TYPE, it)
|
||||
}
|
||||
}.onFailure { Log.w(TAG, "clearToken failed", it) }
|
||||
runCatching { callback?.onResult(Status.SUCCESS) }
|
||||
}
|
||||
}
|
||||
|
||||
private class InvalidAccountException(message: String) : Exception(message)
|
||||
}
|
||||
|
|
@ -143,8 +143,9 @@ class IdentitySignInServiceImpl(private val context: Context, private val client
|
|||
}
|
||||
}
|
||||
AccountUtils.get(context).removeSelectedAccount(clientPackageName)
|
||||
SignInConfigurationService.setAuthInfo(context, clientPackageName, null, null)
|
||||
callback.onResult(Status.SUCCESS)
|
||||
}
|
||||
callback.onResult(Status.SUCCESS)
|
||||
}
|
||||
|
||||
override fun getSignInIntent(
|
||||
|
|
|
|||
|
|
@ -96,7 +96,9 @@ class SignInConfigurationService : Service() {
|
|||
}
|
||||
|
||||
private fun getAuthOptions(packageName: String): Set<String>? {
|
||||
val data = preferences.getStringSet(DEFAULT_SIGN_IN_OPTIONS_PREFIX + getPackageNameSuffix(packageName), null)
|
||||
val key = DEFAULT_SIGN_IN_OPTIONS_PREFIX + getPackageNameSuffix(packageName)
|
||||
val data = runCatching { preferences.getStringSet(key, null) }.getOrNull()
|
||||
?: runCatching { preferences.getString(key, null) }.getOrNull()?.let { setOf(it) }
|
||||
if (data.isNullOrEmpty()) return null
|
||||
return data
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,34 +70,39 @@ val consentRequestOptions: String?
|
|||
Base64.encodeToString(requestOptions.encode(), Base64.DEFAULT)
|
||||
}.getOrNull()
|
||||
|
||||
fun getOAuthManager(context: Context, packageName: String, options: GoogleSignInOptions?, account: Account): AuthManager {
|
||||
fun getOAuthManager(context: Context, packageName: String, options: GoogleSignInOptions?, account: Account, includeGrantedScopes: String? = null): AuthManager {
|
||||
val scopes = options?.scopes.orEmpty().sortedBy { it.scopeUri }.toMutableList().apply {
|
||||
if (options?.includeGame == true) { add(Scope(Scopes.GAMES_LITE)) }
|
||||
}
|
||||
return AuthManager(context, account.name, packageName, "oauth2:${scopes.joinToString(" ")}")
|
||||
return AuthManager(context, account.name, packageName, "oauth2:${scopes.joinToString(" ")}").also {
|
||||
it.includeGrantedScopes = includeGrantedScopes ?: "1"
|
||||
}
|
||||
}
|
||||
|
||||
fun getCookiesManager(context: Context, packageName: String, account: Account): AuthManager {
|
||||
return AuthManager(context, account.name, packageName, "weblogin:url=https://accounts.google.com")
|
||||
}
|
||||
|
||||
fun getIdTokenManager(context: Context, packageName: String, options: GoogleSignInOptions?, account: Account): AuthManager? {
|
||||
fun getIdTokenManager(context: Context, packageName: String, options: GoogleSignInOptions?, account: Account, includeGrantedScopes: String? = null): AuthManager? {
|
||||
if (options?.isIdTokenRequested != true || options.serverClientId == null) return null
|
||||
|
||||
val idTokenManager = AuthManager(context, account.name, packageName, "audience:server:client_id:${options.serverClientId}")
|
||||
idTokenManager.includeEmail = if (options.includeEmail) "1" else "0"
|
||||
idTokenManager.includeProfile = if (options.includeProfile) "1" else "0"
|
||||
idTokenManager.includeGrantedScopes = includeGrantedScopes ?: "0"
|
||||
return idTokenManager
|
||||
}
|
||||
|
||||
fun getServerAuthTokenManager(context: Context, packageName: String, options: GoogleSignInOptions?, account: Account): AuthManager? {
|
||||
fun getServerAuthTokenManager(context: Context, packageName: String, options: GoogleSignInOptions?, account: Account, includeGrantedScopes: String? = null): AuthManager? {
|
||||
if (options?.isServerAuthCodeRequested != true || options.serverClientId == null) return null
|
||||
|
||||
val serverAuthTokenManager = AuthManager(context, account.name, packageName, "oauth2:server:client_id:${options.serverClientId}:api_scope:${options.scopeUris.joinToString(" ")}")
|
||||
serverAuthTokenManager.includeEmail = if (options.includeEmail) "1" else "0"
|
||||
serverAuthTokenManager.includeProfile = if (options.includeProfile) "1" else "0"
|
||||
serverAuthTokenManager.forceRefreshToken = options.isForceCodeForRefreshToken
|
||||
serverAuthTokenManager.setOauth2Prompt("auto")
|
||||
// authorization codes must be single-use
|
||||
serverAuthTokenManager.forceRefreshToken = true
|
||||
serverAuthTokenManager.includeGrantedScopes = includeGrantedScopes ?: "1"
|
||||
serverAuthTokenManager.setOauth2Prompt(if (options.isForceCodeForRefreshToken) "consent" else "auto")
|
||||
serverAuthTokenManager.setItCaveatTypes("2")
|
||||
return serverAuthTokenManager
|
||||
}
|
||||
|
|
@ -109,8 +114,8 @@ suspend fun checkAccountAuthStatus(context: Context, packageName: String, scopeL
|
|||
return withContext(Dispatchers.IO) { authManager.requestAuth(true) }.auth != null
|
||||
}
|
||||
|
||||
suspend fun performSignIn(context: Context, packageName: String, options: GoogleSignInOptions?, account: Account, permitted: Boolean = false, idNonce: String? = null): Pair<String?, GoogleSignInAccount?> {
|
||||
val authManager = getOAuthManager(context, packageName, options, account)
|
||||
suspend fun performSignIn(context: Context, packageName: String, options: GoogleSignInOptions?, account: Account, permitted: Boolean = false, idNonce: String? = null, includeGrantedScopes: String? = null): Pair<String?, GoogleSignInAccount?> {
|
||||
val authManager = getOAuthManager(context, packageName, options, account, includeGrantedScopes)
|
||||
var authResponse = withContext(Dispatchers.IO) {
|
||||
if (options?.includeUnacceptableScope == true || !permitted) {
|
||||
authManager.setTokenRequestOptions(consentRequestOptions)
|
||||
|
|
@ -129,7 +134,7 @@ suspend fun performSignIn(context: Context, packageName: String, options: Google
|
|||
}
|
||||
if (authResponse.auth == null) return Pair(null, null)
|
||||
Log.d(TAG, "id token requested: ${options?.isIdTokenRequested == true}, serverClientId = ${options?.serverClientId}, permitted = ${authManager.isPermitted}")
|
||||
val idTokenResponse = getIdTokenManager(context, packageName, options, account)?.let {
|
||||
val idTokenResponse = getIdTokenManager(context, packageName, options, account, includeGrantedScopes)?.let {
|
||||
if (idNonce != null) {
|
||||
it.setTokenRequestOptions(Base64.encodeToString(RequestOptions.build {
|
||||
remote = 1
|
||||
|
|
@ -141,7 +146,7 @@ suspend fun performSignIn(context: Context, packageName: String, options: Google
|
|||
consentResult?.let { result -> it.putDynamicFiled(CONSENT_RESULT, result) }
|
||||
withContext(Dispatchers.IO) { it.requestAuth(true) }
|
||||
}
|
||||
val serverAuthTokenResponse = getServerAuthTokenManager(context, packageName, options, account)?.let {
|
||||
val serverAuthTokenResponse = getServerAuthTokenManager(context, packageName, options, account, includeGrantedScopes)?.let {
|
||||
it.isPermitted = authResponse.auth != null
|
||||
consentResult?.let { result -> it.putDynamicFiled(CONSENT_RESULT, result) }
|
||||
withContext(Dispatchers.IO) { it.requestAuth(true) }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue