Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth: Improve AssistedSignIn function #2304

Merged
merged 13 commits into from
May 2, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialRequestOptions;
import org.microg.gms.common.Hide;
import org.microg.gms.utils.ToStringHelper;

import java.util.Arrays;
import java.util.List;
Expand All @@ -43,6 +44,20 @@ public class BeginSignInRequest extends AbstractSafeParcelable {
@Field(value = 7, getterName = "getPasskeyJsonRequestOptions")
private final PasskeyJsonRequestOptions passkeyJsonRequestOptions;

@NonNull
@Override
public String toString() {
return ToStringHelper.name("BeginSignInRequest")
.field("PasswordRequestOptions", passwordRequestOptions)
.field("GoogleIdTokenRequestOptions", googleIdTokenRequestOptions)
.field("sessionId", sessionId)
.field("autoSelectEnabled", autoSelectEnabled)
.field("theme", theme)
.field("PasskeysRequestOptions", passkeysRequestOptions)
.field("PasskeyJsonRequestOptions", passkeyJsonRequestOptions)
.end();
}

@Constructor
BeginSignInRequest(@Param(1) PasswordRequestOptions passwordRequestOptions, @Param(2) GoogleIdTokenRequestOptions googleIdTokenRequestOptions, @Param(3) String sessionId, @Param(4) boolean autoSelectEnabled, @Param(5) int theme, @Param(6) PasskeysRequestOptions passkeysRequestOptions, @Param(7) PasskeyJsonRequestOptions passkeyJsonRequestOptions) {
this.passwordRequestOptions = passwordRequestOptions;
Expand Down Expand Up @@ -116,6 +131,20 @@ public static class GoogleIdTokenRequestOptions extends AbstractSafeParcelable {
@Field(value = 7, getterName = "requestVerifiedPhoneNumber")
private final boolean requestVerifiedPhoneNumber;

@NonNull
@Override
public String toString() {
return ToStringHelper.name("GoogleIdTokenRequestOptions")
.field("supported", supported)
.field("serverClientId", serverClientId)
.field("nonce", nonce)
.field("filterByAuthorizedAccounts", filterByAuthorizedAccounts)
.field("linkedServiceId", linkedServiceId)
.field("idTokenDepositionScopes", idTokenDepositionScopes)
.field("requestVerifiedPhoneNumber", requestVerifiedPhoneNumber)
.end();
}

@Hide
@Constructor
public GoogleIdTokenRequestOptions(@Param(1) boolean supported, @Param(2) String serverClientId, @Param(3) String nonce, @Param(4) boolean filterByAuthorizedAccounts, @Param(5) String linkedServiceId, @Param(6) List<String> idTokenDepositionScopes, @Param(7) boolean requestVerifiedPhoneNumber) {
Expand Down Expand Up @@ -316,6 +345,15 @@ public static class PasskeyJsonRequestOptions extends AbstractSafeParcelable {
@Field(value = 2, getterName = "getRequestJson")
private final String requestJson;

@NonNull
@Override
public String toString() {
return ToStringHelper.name("PasskeyJsonRequestOptions")
.field("supported", supported)
.field("requestJson", requestJson)
.end();
}

@Constructor
@Hide
public PasskeyJsonRequestOptions(@Param(1) boolean supported, @Param(2) String requestJson) {
Expand Down Expand Up @@ -409,6 +447,16 @@ public static class PasskeysRequestOptions extends AbstractSafeParcelable {
@Field(value = 3, getterName = "getRpId")
private final String rpId;

@NonNull
@Override
public String toString() {
return ToStringHelper.name("PasskeysRequestOptions")
.field("supported", supported)
.field("challenge", challenge)
.field("rpId", rpId)
.end();
}

@Constructor
@Hide
public PasskeysRequestOptions(@Param(1) boolean supported, @Param(2) byte[] challenge, @Param(3) String rpId) {
Expand Down Expand Up @@ -516,6 +564,14 @@ public static class PasswordRequestOptions extends AbstractSafeParcelable {
@Field(value = 1, getterName = "isSupported")
public final boolean supported;

@NonNull
@Override
public String toString() {
return ToStringHelper.name("PasswordRequestOptions")
.field("supported", supported)
.end();
}

@Constructor
@Hide
public PasswordRequestOptions(@Param(1) boolean supported) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import org.microg.gms.common.Hide;
import org.microg.gms.utils.ToStringHelper;

import java.util.Arrays;
import java.util.Objects;
Expand All @@ -39,6 +40,19 @@ public class GetSignInIntentRequest extends AbstractSafeParcelable {
@Field(value = 6, getterName = "getTheme")
private final int theme;

@NonNull
@Override
public String toString() {
return ToStringHelper.name("GetSignInIntentRequest")
.field("serverClientId", serverClientId)
.field("hostedDomainFilter", hostedDomainFilter)
.field("sessionId", sessionId)
.field("nonce", nonce)
.field("requestVerifiedPhoneNumber", requestVerifiedPhoneNumber)
.field("theme", theme)
.end();
}

@Constructor
GetSignInIntentRequest(@Param(1) String serverClientId, @Param(2) String hostedDomainFilter, @Param(3) String sessionId, @Param(4) String nonce, @Param(5) boolean requestVerifiedPhoneNumber, @Param(6) int theme) {
this.serverClientId = serverClientId;
Expand Down
15 changes: 15 additions & 0 deletions play-services-core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,21 @@
</intent-filter>
</service>

<activity
android:theme="@style/Theme.AppCompat.Dialog.Alert"
android:name="org.microg.gms.auth.signin.AssistedSignInActivity"
android:enabled="true"
android:exported="true"
android:process=":ui"
android:configChanges="keyboardHidden|keyboard|orientation|screenSize"
android:launchMode="singleTask"
android:excludeFromRecents="false">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.credentials.ASSISTED_SIGNIN"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>

<activity
android:name="org.microg.gms.auth.consent.ConsentSignInActivity"
android:process=":ui"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.microg.gms.people;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
Expand All @@ -26,10 +25,9 @@
import android.util.Log;

import com.google.android.gms.common.Scopes;
import org.json.JSONException;

import org.json.JSONObject;
import org.microg.gms.auth.AuthManager;
import org.microg.gms.auth.AuthRequest;
import org.microg.gms.auth.AuthResponse;
import org.microg.gms.common.Constants;
import org.microg.gms.common.Utils;
Expand All @@ -46,6 +44,38 @@ public class PeopleManager {
public static final String USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo";
public static final String REGEX_SEARCH_USER_PHOTO = "https?\\:\\/\\/lh([0-9]*)\\.googleusercontent\\.com/";

public static String getDisplayName(Context context, String accountName) {
DatabaseHelper databaseHelper = new DatabaseHelper(context);
Cursor cursor = databaseHelper.getOwner(accountName);
String displayName = null;
try {
if (cursor.moveToNext()) {
int idx = cursor.getColumnIndex("display_name");
if (idx >= 0 && !cursor.isNull(idx)) displayName = cursor.getString(idx);
}
} finally {
cursor.close();
databaseHelper.close();
}
return displayName;
}

public static String getGivenName(Context context, String accountName) {
DatabaseHelper databaseHelper = new DatabaseHelper(context);
Cursor cursor = databaseHelper.getOwner(accountName);
String displayName = null;
try {
if (cursor.moveToNext()) {
int idx = cursor.getColumnIndex("given_name");
if (idx >= 0 && !cursor.isNull(idx)) displayName = cursor.getString(idx);
}
} finally {
cursor.close();
databaseHelper.close();
}
return displayName;
}

public static File getOwnerAvatarFile(Context context, String accountName, boolean network) {
DatabaseHelper databaseHelper = new DatabaseHelper(context);
Cursor cursor = databaseHelper.getOwner(accountName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@

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

import android.accounts.AccountManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import com.google.android.gms.auth.api.identity.BeginSignInRequest
import com.google.android.gms.auth.api.identity.BeginSignInResult
Expand All @@ -18,75 +20,107 @@ import com.google.android.gms.auth.api.identity.internal.IGetPhoneNumberHintInte
import com.google.android.gms.auth.api.identity.internal.IGetSignInIntentCallback
import com.google.android.gms.auth.api.identity.internal.ISignInService
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.Status
import com.google.android.gms.common.api.internal.IStatusCallback
import com.google.android.gms.common.internal.ConnectionInfo
import com.google.android.gms.common.internal.GetServiceRequest
import com.google.android.gms.common.internal.IGmsCallbacks
import com.google.android.gms.common.internal.safeparcel.SafeParcelableSerializer
import org.microg.gms.BaseService
import org.microg.gms.auth.AuthConstants
import org.microg.gms.auth.signin.ACTION_ASSISTED_SIGN_IN
import org.microg.gms.auth.signin.BEGIN_SIGN_IN_REQUEST
import org.microg.gms.auth.signin.GET_SIGN_IN_INTENT_REQUEST
import org.microg.gms.auth.credentials.FEATURES
import org.microg.gms.auth.signin.AuthSignInActivity
import org.microg.gms.auth.signin.CLIENT_PACKAGE_NAME
import org.microg.gms.auth.signin.GOOGLE_SIGN_IN_OPTIONS
import org.microg.gms.auth.signin.performSignOut
import org.microg.gms.common.Constants
import org.microg.gms.common.GmsService

const val TAG = "IdentitySignInService"
private const val TAG = "IdentitySignInService"

class IdentitySignInService : BaseService(TAG, GmsService.IDENTITY_SIGN_IN) {

override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) {
Log.d(TAG, "handleServiceRequest start ")
val connectionInfo = ConnectionInfo()
connectionInfo.features = FEATURES
callback.onPostInitCompleteWithConnectionInfo(
ConnectionResult.SUCCESS,
IdentitySignInServiceImpl(this, request.packageName).asBinder(),
connectionInfo
ConnectionResult.SUCCESS, IdentitySignInServiceImpl(this, request.packageName).asBinder(), connectionInfo
)
}
}

class IdentitySignInServiceImpl(private val mContext: Context, private val clientPackageName: String) :
ISignInService.Stub() {

private val requestMap = mutableMapOf<String, GoogleSignInOptions>()

override fun beginSignIn(callback: IBeginSignInCallback, request: BeginSignInRequest) {
Log.d(TAG, "method 'beginSignIn' return status is SUCCESS")
callback.onResult(Status.SUCCESS, BeginSignInResult(performSignIn(request.googleIdTokenRequestOptions.serverClientId)))
Log.d(TAG, "method 'beginSignIn' called")
Log.d(TAG, "request-> $request")
if (request.googleIdTokenRequestOptions.serverClientId.isNullOrEmpty()) {
Log.d(TAG, "serverClientId is empty, return CANCELED ")
callback.onResult(Status.CANCELED, null)
return
}
val bundle = Bundle().apply {
val options = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(request.googleIdTokenRequestOptions.serverClientId).build()
putByteArray(BEGIN_SIGN_IN_REQUEST, SafeParcelableSerializer.serializeToBytes(request))
putByteArray(GOOGLE_SIGN_IN_OPTIONS, SafeParcelableSerializer.serializeToBytes(options))
putString(CLIENT_PACKAGE_NAME, clientPackageName)
requestMap[request.sessionId] = options
}
callback.onResult(Status.SUCCESS, BeginSignInResult(performSignIn(bundle)))
}

override fun signOut(callback: IStatusCallback, requestTag: String) {
Log.d(TAG, "method signOut called, requestTag=$requestTag")
if (requestMap.containsKey(requestTag)) {
val accounts = AccountManager.get(mContext).getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE)
if (accounts.isNotEmpty()) {
accounts.forEach { performSignOut(mContext, clientPackageName, requestMap[requestTag], it) }
}
}
callback.onResult(Status.SUCCESS)
}

override fun getSignInIntent(
callback: IGetSignInIntentCallback,
request: GetSignInIntentRequest
callback: IGetSignInIntentCallback, request: GetSignInIntentRequest
) {
Log.d(TAG, "method 'getSignInIntent' return status is SUCCESS")
callback.onResult(Status.SUCCESS, performSignIn(request.serverClientId))
Log.d(TAG, "method 'getSignInIntent' called")
Log.d(TAG, "request-> $request")
if (request.serverClientId.isNullOrEmpty()) {
Log.d(TAG, "serverClientId is empty, return CANCELED ")
callback.onResult(Status.CANCELED, null)
return
}
val bundle = Bundle().apply {
val options =
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).requestIdToken(request.serverClientId)
.build()
putByteArray(GET_SIGN_IN_INTENT_REQUEST, SafeParcelableSerializer.serializeToBytes(request))
putByteArray(GOOGLE_SIGN_IN_OPTIONS, SafeParcelableSerializer.serializeToBytes(options))
putString(CLIENT_PACKAGE_NAME, clientPackageName)
requestMap[request.sessionId] = options
}
callback.onResult(Status.SUCCESS, performSignIn(bundle))
}

override fun getPhoneNumberHintIntent(
callback: IGetPhoneNumberHintIntentCallback,
request: GetPhoneNumberHintIntentRequest
callback: IGetPhoneNumberHintIntentCallback, request: GetPhoneNumberHintIntentRequest
) {
Log.w(TAG, "method 'getPhoneNumberHintIntent' not fully implemented, return status is CANCELED.")
callback.onResult(Status.CANCELED, null)
}

private fun performSignIn(serverClientId: String): PendingIntent {
val signInConfiguration = SignInConfiguration().apply {
options = GoogleSignInOptions.Builder()
.requestIdToken(serverClientId)
.requestId()
.requestEmail()
.requestProfile()
.build()
packageName = clientPackageName
}
val intent = Intent(mContext, AuthSignInActivity::class.java).apply {
private fun performSignIn(bundle: Bundle): PendingIntent {
val intent = Intent(ACTION_ASSISTED_SIGN_IN).apply {
`package` = Constants.GMS_PACKAGE_NAME
putExtra("config", signInConfiguration)
putExtras(bundle)
}
val flags = PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_UPDATE_CURRENT
return PendingIntent.getActivity(mContext, 0, intent, flags)
Expand Down