URGENT help needed with Realm sign in

I am getting reports that sign in is not working properly for a large number of users in a live app. The error is reporting as “illegal base64 data at byte…”. The problem seems to be intermittent, sometimes they can log in, and sometimes they can’t. Sometimes they can log in immediately after a failure with no problem.

I am using Google sign in, using ID_TOKEN, in Kotlin.

This was causing a crash before, but I have tracked down the problem with this and handled it, but I still get the error.

Thanks for your help!

I think I may have solved this!

Google seems to sometimes return extra characters at the end of their token, ending with * and some Unicode characters. Stripping these before passing them to Mongo allows it to sign in as expected.

This is in fact not solved.

Still getting “illegal base64 data at byte 342” even when the token looks fine - no Unicode escape characters, only AZaz09_-.

Is no one else having this problem? I thought I could just rerun the sign in, but if I get a token that Mongo fails to recognise, I’m stuck with that token until Google decides to invalidate it. Calling GoogleSignInClient.signOut() does not invalidate the token.

My app is live, and intermittently broken for a good number of users, because of something seemingly out of my control. Any suggestions at all would be very gratefully received.

G’Day @Ada_Lovelace,

Thank you for raising your concern. I have reported this internally.

Please allow me some time to debug this and I will soon be back with any findings. Could you please confirm your forum registration email is also the email for your cloud project?

I appreciate your patience with us and I look forward to your response :slight_smile:

Warm Regards,

1 Like

Hello @Ada_Lovelace,

I noticed multiple auth request errors of the form illegal base64 data at input byte 344 and crypto/rsa: verification error.

It is possible that somewhere in the code the token is getting invalid. I found a post on SO for Google SignIn validation. Please let me know if that is helpful in your case.

I look forward to hearing from you.

Cheers, :performing_arts:

Thanks for your reply. I took a look at the post you mentioned, but it doesn’t really help I’m afraid. I take the token from Google, and I pass it to MongoDb, there’s nothing really in between, so either Google is providing me with bad tokens, or Mongo is not accepting valid tokens.

Could I send you a token that is not working, so you can check this?

Hello @Ada_Lovelace ,

Thank you for your response. Sorry to hear the post was not helpful.

Could you share the full stack trace from the client application with me when this error is thrown? Please also share the valid and invalid tokens that allow and do not allow authentication to Cloud. I will share this with the engineering team internally to verify the reasons for not-accepting the tokens.

I look forward to your response.

Cheers, :performing_arts:

Sure thing, the stack trace is:

AUTH_ERROR(realm::app::ServiceError:47): illegal base64 data at input byte 349
	at io.realm.internal.network.NetworkRequest.onError(NetworkRequest.java:68)
	at io.realm.internal.objectstore.OsJavaNetworkTransport.nativeHandleResponse(Native Method)
	at io.realm.internal.objectstore.OsJavaNetworkTransport.handleResponse(OsJavaNetworkTransport.java:98)
	at io.realm.internal.network.OkHttpNetworkTransport.lambda$sendRequestAsync$0$io-realm-internal-network-OkHttpNetworkTransport(OkHttpNetworkTransport.java:100)
	at io.realm.internal.network.OkHttpNetworkTransport$$ExternalSyntheticLambda0.run(Unknown Source:14)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
	at java.lang.Thread.run(Thread.java:920)

Do you have an email address I can send the tokens to?

I have pasted one of the failing tokens into https://jwt.io/ and it tells me it’s a valid token

Hello Ada,

Thank you for sharing the requested information. Could you please also share the respective lines of code when this error is thrown?

Looking forward to your response.

Cheers, :performing_arts:

This is the code I’m using:

 private fun initGoogleSignIn() {
        val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(RealmConsts.clientIdWeb.value)
            .build()

        mGoogleSignInClient = GoogleSignIn.getClient(this, gso)
        resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            val task: Task<GoogleSignInAccount> =
                GoogleSignIn.getSignedInAccountFromIntent(result.data)
            onSignInResult(task)
        }
    }

private fun onSignInResult(completedTask: Task<GoogleSignInAccount>) {
        try {
            Log.d("SIGN IN", "onSignInResult")
            val account = completedTask.getResult(ApiException::class.java)
            signInUser(account, // callback to UI here)
        } catch (e: ApiException) {
            // error handling removed for brevity
        }
    }
fun signInUser (account: GoogleSignInAccount?, callback: ((Boolean) -> Unit)?) {
        viewModelScope.launch {

            val token = validateToken(account?.idToken) // this checks for non-unicode characters
            if (token == "") {
                // error handling
                return@launch
            }

            signInGoogle(token) // callback that handles the user here
        }

fun signInGoogle (token: String, callback: (User?, String?) -> Unit) {
        signIn(Credentials.google(token, GoogleAuthType.ID_TOKEN), callback)
    }

private fun signIn(credentials: Credentials, callback: (User?, String?) -> Unit){
        try {
            app.loginAsync(credentials) {
                Log.d("HERE", "Async complete ${it.isSuccess}")
                if (it.isSuccess) {
                    callback(it.get(), null)
                } else {
                    // FAILS HERE
                }
            }
        } catch (e: Exception) {
            // error handling
        }
    }
    }

Thank you for sharing the requested information, Ada. I have raised this internally.

I will update you once I have more information to share.

Appreciate your patience with us on this.

Cheers, :performing_arts:

1 Like

Thanks for your help on this.

1 Like

Hello @Ada_Lovelace ,

I wanted to update you that the engineering team is looking into this. There is a possibility that this may be an issue on Google’s side but I will be able to confirm once more information is available.

Appreciate your patience with us on this.

Regards,
Henna

1 Like

Hello @Ada_Lovelace,

Could you share details on how intermittently is this happening? The engineering team reported that the JWT can be parsed but cannot be verified due to a corrupt signing key.

The specific error is:

I look forward to your response.

Cheers, :performing_arts:

With my login, it happens every single time now. I am unable to log in at all.

Thanks for the code example. I’ve made my own test in Kotlin, decoding just the secret from the token:

The secret decoded fine when using URL encoding using both java.util.Base64 and android.util.Base64 libraries, although when I put the same secret into the Go example, it gives an error.

try {
            val urlDecoded = java.util.Base64.getUrlDecoder().decode(secret)
            Log.d("JWT", "URL Decoded: $urlDecoded")
        } catch (e: Exception) {
            Log.d("JWT", "e: $e");
        } // WORKS

        try {
            val decoded = java.util.Base64.getDecoder().decode(secret)
            Log.d("JWT", "Decoded: $decoded")
        } catch (e: Exception) {
            Log.d("JWT", "e: $e");
        } // FAILS

        try {
            val androidDecoded = android.util.Base64.decode(secret, android.util.Base64.DEFAULT)
            Log.d("JWT", "Android Decoded DEFAULT: $androidDecoded")
        } catch (e: Exception) {
            Log.d("JWT", "e: $e");
        } // FAILS

        try {
            val urlAndroidDecoded = android.util.Base64.decode(secret, android.util.Base64.URL_SAFE)
            Log.d("JWT", "Android Decoded URL_SAFE: $urlAndroidDecoded")
        } catch (e: Exception) {
            Log.d("JWT", "e: $e");
        } // WORKS

OK, after a little testing (and learning Go!), I think I’ve found why the error is occuring - the token is presented without padding, meaning that base64.URLEncoding.DecodeString will not work (if the token length is not divisible exactly by the byte length), but base64.RawURLEncoding.DecodeString will.

I worked on this a little more, because currently I cannot use my app.

Adding padding to the token using:

private fun padToken (nonNullToken: String, padDivisor: Int = 4): String {
        val comps = nonNullToken.split(".")
        if (comps.size < 3) return ""
        var token = comps[2]
        val size = token.length
        val padAmount = ((ceil(size.toDouble() / padDivisor) * padDivisor) - size).toInt()
        token = token.padEnd(size + padAmount, '=')
        return "${comps[0]}.${comps[1]}.$token"
    }

generates a string that passes the Go playground above. However, it still does not allow me to sign in, and is also against the JWT spec (jwt.io reports that padded tokens are invalid)