Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public class GoogleSignInOptions extends AutoSafeParcelable {
private ArrayList<GoogleSignInOptionsExtensionParcelable> extensions;
@Field(10)
private String logSessionId;
// Internal parameters
@Field(11)
private String nonceStr;

private GoogleSignInOptions() {
this.scopes = new ArrayList<>();
Expand Down Expand Up @@ -130,6 +133,11 @@ public String getLogSessionId() {
return logSessionId;
}

@Hide
public String getNonceStr() {
return nonceStr;
}

/**
* Builder for {@link GoogleSignInOptions}.
*/
Expand All @@ -144,6 +152,10 @@ public static final class Builder {
private Account account;
@Nullable
private String hostedDomain;
@Nullable
private String nonceStr;
@Nullable
private String sessionId;
private final Map<Integer, GoogleSignInOptionsExtensionParcelable> extensionMap = new HashMap<>();

public Builder() {
Expand All @@ -158,6 +170,8 @@ public Builder(GoogleSignInOptions options) {
this.serverClientId = options.serverClientId;
this.account = options.account;
this.hostedDomain = options.hostedDomain;
this.nonceStr = options.nonceStr;
this.sessionId = options.logSessionId;
if (options.extensions != null) {
for (GoogleSignInOptionsExtensionParcelable extension : options.extensions) {
extensionMap.put(extension.type, extension);
Expand Down Expand Up @@ -289,6 +303,16 @@ public GoogleSignInOptions.Builder setHostedDomain(String hostedDomain) {
return this;
}

public Builder addNonceStr(String nonce) {
this.nonceStr = nonce;
return this;
}

public Builder addSessionId(String sessionId) {
this.sessionId = sessionId;
return this;
}

/**
* Builds the {@link GoogleSignInOptions} object.
*
Expand All @@ -311,6 +335,8 @@ public GoogleSignInOptions build() {
options.account = account;
options.hostedDomain = hostedDomain;
options.extensions = new ArrayList<>(extensionMap.values());
options.nonceStr = nonceStr;
options.logSessionId = sessionId;
return options;
}
}
Expand All @@ -322,6 +348,8 @@ public GoogleSignInOptions build() {
private static final String JSON_SERVER_AUTH_REQUESTED = "serverAuthRequested";
private static final String JSON_SERVER_CLIENT_ID = "serverClientId";
private static final String JSON_HOSTED_DOMAIN = "hostedDomain";
private static final String JSON_NONCE_STR = "nonce";
private static final String JSON_SESSION_ID = "sessionId";

public static GoogleSignInOptions fromJson(String jsonString) throws JSONException {
if (jsonString == null) return null;
Expand All @@ -337,6 +365,8 @@ public static GoogleSignInOptions fromJson(String jsonString) throws JSONExcepti
options.serverAuthCodeRequested = json.getBoolean(JSON_SERVER_AUTH_REQUESTED);
options.serverClientId = json.has(JSON_SERVER_CLIENT_ID) ? json.optString(JSON_SERVER_CLIENT_ID) : null;
options.hostedDomain = json.has(JSON_HOSTED_DOMAIN) ? json.optString(JSON_HOSTED_DOMAIN) : null;
options.nonceStr = json.has(JSON_NONCE_STR) ? json.optString(JSON_NONCE_STR) : null;
options.logSessionId = json.has(JSON_SESSION_ID) ? json.optString(JSON_SESSION_ID) : null;
return options;
}

Expand All @@ -355,6 +385,8 @@ public String toJson() {
json.put(JSON_SERVER_AUTH_REQUESTED, serverAuthCodeRequested);
if (serverClientId != null) json.put(JSON_SERVER_CLIENT_ID, serverClientId);
if (hostedDomain != null) json.put(JSON_HOSTED_DOMAIN, hostedDomain);
if (nonceStr != null) json.put(JSON_NONCE_STR, nonceStr);
if (logSessionId != null) json.put(JSON_SESSION_ID, logSessionId);
return json.toString();
} catch (JSONException e) {
throw new RuntimeException(e);
Expand All @@ -372,6 +404,8 @@ public String toString() {
.field("serverAuthCodeRequested", serverAuthCodeRequested)
.field("serverClientId", serverClientId)
.field("hostedDomain", hostedDomain)
.field("nonceStr", nonceStr)
.field("sessionId", logSessionId)
.end();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ public class AuthConstants {
public static final String STATUS = "status";
public static final String SCOPE_OAUTH2 = "oauth2:";
public static final String SCOPE_EM_OP_PRO = "oauth2:email openid profile";

public static final String GOOGLE_SIGN_IN_AUTHORIZATION_RESULT = "authorization_result";
}
5 changes: 5 additions & 0 deletions play-services-core-proto/src/main/proto/auth.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,16 @@ message RequestOptions {
// optional int32 field_4 = 4;
// optional int32 field_5 = 5;
optional int32 remote = 7;
optional NonceWrapper nonceWrapper = 8;
optional int32 version = 10;
optional string sessionId = 11;
// optional bool field_12 = 12;
}

message NonceWrapper {
optional string nonce = 1;
}

message ConsentUrlResponse {
optional int32 resultCode = 2;
optional string consentUrl = 3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@

package org.microg.gms.auth.credentials.identity

import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
Expand All @@ -18,6 +22,7 @@ import com.google.android.gms.auth.api.identity.internal.IAuthorizationCallback
import com.google.android.gms.auth.api.identity.internal.IAuthorizationService
import com.google.android.gms.auth.api.identity.internal.IVerifyWithGoogleCallback
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.Scope
import com.google.android.gms.common.api.Status
Expand All @@ -28,10 +33,12 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.microg.gms.BaseService
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.getServerAuthTokenManager
import org.microg.gms.auth.signin.performSignIn
import org.microg.gms.auth.signin.scopeUris
import org.microg.gms.common.Constants
import org.microg.gms.common.GmsService
import org.microg.gms.common.PackageUtils

Expand All @@ -51,36 +58,45 @@ class AuthorizationService : BaseService(TAG, GmsService.AUTHORIZATION) {
}
}

class AuthorizationServiceImpl(val context: Context, val packageName: String, override val lifecycle: Lifecycle) : IAuthorizationService.Stub(), LifecycleOwner {
class AuthorizationServiceImpl(val context: Context, val callingPackageName: String, override val lifecycle: Lifecycle) : IAuthorizationService.Stub(), LifecycleOwner {

override fun authorize(callback: IAuthorizationCallback?, request: AuthorizationRequest?) {
Log.d(TAG, "Method: authorize called, request:$request")
Log.d(TAG, "Method: authorize called, callingPackageName: $callingPackageName request:$request")
lifecycleScope.launchWhenStarted {
val account = request?.account ?: SignInConfigurationService.getDefaultAccount(context, packageName)
if (account == null) {
val defaultAccount = request?.account ?: SignInConfigurationService.getDefaultAccount(context, callingPackageName)
if (defaultAccount == null) {
Log.d(TAG, "Method: authorize called, but account is null")
callback?.onAuthorized(Status.CANCELED, null)
return@launchWhenStarted
}
val googleSignInOptions = GoogleSignInOptions.Builder().apply {
setAccountName(defaultAccount.name)
request?.requestedScopes?.forEach { requestScopes(it) }
if (request?.idTokenRequested == true && request.serverClientId != null) requestIdToken(request.serverClientId)
if (request?.serverAuthCodeRequested == true && request.serverClientId != null) requestServerAuthCode(request.serverClientId)
}.build()
val signInAccount = performSignIn(context, packageName, googleSignInOptions, account, false)
val intent = Intent(context, AuthSignInActivity::class.java).apply {
`package` = Constants.GMS_PACKAGE_NAME
putExtra("config", SignInConfiguration().apply {
packageName = callingPackageName
options = googleSignInOptions
})
}
val signInAccount = performSignIn(context, callingPackageName, googleSignInOptions, defaultAccount, false)
callback?.onAuthorized(Status.SUCCESS, AuthorizationResult().apply {
serverAuthToken = signInAccount?.serverAuthCode
idToken = signInAccount?.idToken
grantedScopes = signInAccount?.grantedScopes?.toList()
googleSignInAccount = signInAccount
pendingIntent = PendingIntent.getActivity(context, defaultAccount.hashCode(), intent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
}.also { Log.d(TAG, "authorize: result:$it") })
}
}

override fun verifyWithGoogle(callback: IVerifyWithGoogleCallback?, request: VerifyWithGoogleRequest?) {
Log.d(TAG, "unimplemented Method: verifyWithGoogle: request:$request")
lifecycleScope.launchWhenStarted {
val account = SignInConfigurationService.getDefaultAccount(context, packageName)
val account = SignInConfigurationService.getDefaultAccount(context, callingPackageName)
if (account == null) {
Log.d(TAG, "Method: authorize called, but account is null")
callback?.onVerifed(Status.CANCELED, null)
Expand All @@ -91,7 +107,7 @@ class AuthorizationServiceImpl(val context: Context, val packageName: String, ov
request.requestedScopes?.forEach { requestScopes(it) }
requestServerAuthCode(request.serverClientId)
}.build()
val authResponse = getServerAuthTokenManager(context, packageName, googleSignInOptions, account)?.let {
val authResponse = getServerAuthTokenManager(context, callingPackageName, googleSignInOptions, account)?.let {
withContext(Dispatchers.IO) { it.requestAuth(true) }
}
callback?.onVerifed(Status.SUCCESS, VerifyWithGoogleResult().apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ class IdentitySignInServiceImpl(private val context: Context, private val client
val bundle = Bundle().apply {
val options = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestIdToken(request.googleIdTokenRequestOptions.serverClientId).build()
.requestIdToken(request.googleIdTokenRequestOptions.serverClientId)
.addNonceStr(request.googleIdTokenRequestOptions.nonce)
.addSessionId(request.sessionId)
.build()
putByteArray(BEGIN_SIGN_IN_REQUEST, SafeParcelableSerializer.serializeToBytes(request))
putByteArray(GOOGLE_SIGN_IN_OPTIONS, SafeParcelableSerializer.serializeToBytes(options))
putString(CLIENT_PACKAGE_NAME, clientPackageName)
Expand Down Expand Up @@ -147,6 +150,8 @@ class IdentitySignInServiceImpl(private val context: Context, private val client
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestIdToken(request.serverClientId)
.addNonceStr(request.nonce)
.addSessionId(request.sessionId)
.build()
putByteArray(GET_SIGN_IN_INTENT_REQUEST, SafeParcelableSerializer.serializeToBytes(request))
putByteArray(GOOGLE_SIGN_IN_OPTIONS, SafeParcelableSerializer.serializeToBytes(options))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.R
import com.google.android.gms.auth.api.identity.AuthorizationResult
import com.google.android.gms.auth.api.identity.SignInCredential
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.SignInAccount
Expand Down Expand Up @@ -185,10 +186,18 @@ class AuthSignInActivity : AppCompatActivity() {
private fun finishResult(statusCode: Int, message: String? = null, account: Account? = null, googleSignInAccount: GoogleSignInAccount? = null) {
val data = Intent()
if (statusCode != CommonStatusCodes.SUCCESS) data.putExtra(AuthConstants.ERROR_CODE, statusCode)
data.putExtra(AuthConstants.STATUS, Status(statusCode, message))
data.putExtra(AuthConstants.GOOGLE_SIGN_IN_STATUS, Status(statusCode, message))
data.putExtra(AuthConstants.GOOGLE_SIGN_IN_ACCOUNT, googleSignInAccount)
val bundle = Bundle()
if (googleSignInAccount != null) {
val authorizationResult = AuthorizationResult().apply {
serverAuthToken = googleSignInAccount.serverAuthCode
idToken = googleSignInAccount.idToken
grantedScopes = googleSignInAccount.grantedScopes.toList()
this.googleSignInAccount = googleSignInAccount
}
data.putExtra(AuthConstants.GOOGLE_SIGN_IN_AUTHORIZATION_RESULT, SafeParcelableSerializer.serializeToBytes(authorizationResult))
val signInAccount = SignInAccount().apply {
email = googleSignInAccount.email ?: account?.name
this.googleSignInAccount = googleSignInAccount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import kotlinx.coroutines.withContext
import org.microg.gms.auth.AuthManager
import org.microg.gms.auth.ConsentCookiesResponse
import org.microg.gms.auth.ConsentUrlResponse
import org.microg.gms.auth.NonceWrapper
import org.microg.gms.auth.RequestOptions
import org.microg.gms.auth.consent.CONSENT_KEY_COOKIE
import org.microg.gms.auth.consent.CONSENT_MESSENGER
Expand Down Expand Up @@ -69,6 +70,15 @@ val consentRequestOptions: String?
Base64.encodeToString(requestOptions.encode(), Base64.DEFAULT)
}.getOrNull()

fun GoogleSignInOptions.nonceRequestOptions() = runCatching {
if (nonceStr?.isEmpty() == true) {
return@runCatching null
}
val nonceWrapper = NonceWrapper.build { nonce = nonceStr }
val requestOptions = RequestOptions().newBuilder().remote(1).version(6).nonceWrapper(nonceWrapper).build()
Base64.encodeToString(requestOptions.encode(), Base64.DEFAULT)
}.getOrNull()

fun getOAuthManager(context: Context, packageName: String, options: GoogleSignInOptions?, account: Account): AuthManager {
val scopes = options?.scopes.orEmpty().sortedBy { it.scopeUri }
return AuthManager(context, account.name, packageName, "oauth2:${scopes.joinToString(" ")}")
Expand All @@ -84,6 +94,7 @@ fun getIdTokenManager(context: Context, packageName: String, options: GoogleSign
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"
options.nonceRequestOptions()?.let { idTokenManager.setTokenRequestOptions(it) }
return idTokenManager
}

Expand Down