google / java-photoslibrary

Java client library for the Google Photos Library API
http://developers.google.com/photos
Apache License 2.0
108 stars 59 forks source link

Credentials expire after 1 hour, how can we refresh? #48

Closed gavingt closed 1 year ago

gavingt commented 1 year ago

Some of the users of my Android app are experiencing an error like this after 1 hour of usage:

Error loading file preview: com.google.api.gax.rpc.UnauthenticatedException: io.grpc.StatusRuntimeException: UNAUTHENTICATED: Request had invalid authentication credentials. Expected OAuth 2 access token login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.

I've determined that I need a refresh token, but it looks like there's no way to get one on Android. I've found one relevant post here, but the solution doesn't work for me. Even with the proposed solution, the UserCredentials returned by my getUserCredentials() method contains a null refreshToken.

Any help would be appreciated.

fun getGoogleSignInClient(activity: Activity, needsPhotosScope: Boolean, needsDriveScope: Boolean): GoogleSignInClient {
    val signInOptions = GoogleSignInOptions
        .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestEmail()
    val scopes = getRequiredGoogleServiceScopes(needsPhotosScope, needsDriveScope)
    when (scopes.size) {
        0 -> throw RuntimeException("Attempting to use Google Sign-In without requesting any Scopes.")
        1 -> signInOptions.requestScopes(scopes[0])
        2 -> signInOptions.requestScopes(scopes[0], scopes[1])
    }
    return GoogleSignIn.getClient(activity, signInOptions.build())
}
fun getGoogleAccountCredential(googleSignInAccount: GoogleSignInAccount, needsPhotosScope: Boolean,
                               needsDriveScope: Boolean): GoogleAccountCredential {

    // Create list of Scopes in String form.
    val scopeStringList = getRequiredGoogleServiceScopes(needsPhotosScope, needsDriveScope).map { it.scopeUri }

    val credential = GoogleAccountCredential.usingOAuth2(App.appContext, scopeStringList)
    credential.selectedAccount = googleSignInAccount.account
    return credential
}
// Fetch a List of Scopes that match the requirements based on the user's current search criteria.
fun getRequiredGoogleServiceScopes(needsPhotosScope: Boolean, needsDriveScope: Boolean): List<Scope> {
    val scopes = mutableListOf<Scope>()
    if (needsPhotosScope) scopes.add(Scope(googlePhotosScope))
    if (needsDriveScope) scopes.add(Scope(DriveScopes.DRIVE))
    return scopes
}
    fun createGooglePhotosClient(token: String): PhotosLibraryClient? {
        return try {
            val settings = PhotosLibrarySettings.newBuilder()
                .setCredentialsProvider(FixedCredentialsProvider.create(getUserCredentials(token))).build()
            PhotosLibraryClient.initialize(settings)
        } catch (throwable: Throwable) {
            logMessage("GooglePhotosTools", throwable.stackTraceToString())
            null
        }
    }
    private fun getUserCredentials(token: String): UserCredentials {
        val accessToken = AccessToken(token, null)
        return UserCredentials.newBuilder()
            .setClientId(App.appContext.getString(R.string.google_client_id))
            .setClientSecret(App.appContext.getString(R.string.google_client_secret))
            .setAccessToken(accessToken)
            .build()
    }
gavingt commented 1 year ago

I was able to solve this. Here's my solution.