Skip to content

Custom provider getToken() creates infinite loop when device time is tempered #3935

Open
@im12345dev

Description

@im12345dev

[READ] Step 1: Are you in the right place?

Issues filed here should be about bugs in the code in this repository.
If you have a general question, need help debugging, or fall into some
other category use one of these other channels:

  • For general technical questions, post a question on StackOverflow
    with the firebase tag.
  • For general Firebase discussion, use the firebase-talk
    google group.
  • For help troubleshooting your application that does not fall under one
    of the above categories, reach out to the personalized
    Firebase support channel.

[REQUIRED] Step 2: Describe your environment

  • Android Studio version: Chipmunk 2021.2.1
  • Firebase Component: App Check
  • Component version: bom 30.2.0

[REQUIRED] Step 3: Describe the problem

I have implemented a custom provider using Firebase Functions and the firebase-admin sdk which return a token and expiration time using some custom logic, the implementation works perfectly when the time on the device is correct and not tempered with, but as soon as the time on the device is being tempered, for example a user set the clock to +2 hours using the device settings, Android App Check SDK gets into infinite loop as the token expiration time from the server is current time + 60 minutes but the device time is current time + 2 hours (due to settings change), which leads to the SDK to think the token is expired and start an infinite refresh loop

Steps to reproduce:

Configuring a token TTL to 60 minutes and changing the device clock to +1 hour should reproduce it

Relevant Code:

`public class YourCustomAppCheckProvider implements AppCheckProvider {

private final Context applicationContext;

public YourCustomAppCheckProvider(Context applicationContext) {
    this.applicationContext = applicationContext;
}

@Override
public Task<AppCheckToken> getToken() {

    TaskCompletionSource<AppCheckToken> taskCompletionSource = new TaskCompletionSource<>();

    try {
        String token = customLogicToGetToken()
        int expirationFromServer = expiration received from the server endpoint...

        long expMillis = expirationFromServer * 1000L - 60000;

        taskCompletionSource.setResult(new CustomAppCheckToken(token,expMillis));
    } catch (IOException | JSONException e) {
        taskCompletionSource.setException(e);
    }

    return taskCompletionSource.getTask();
}

}
`

The Firebase function to generate the token + expiration time:

`

exports.fetchAppCheckToken = functions.https.onCall((authenticityData, context) => {

//Custom logic to decide if the request is valid or not, in case not return "error"...


    return admin.appCheck().createToken(APP ID)
        .then(function (appCheckToken) {
            const expiresAt = Math.floor(Date.now() / 1000) + 60 * 60;

            return {token: appCheckToken.token, expiresAt: expiresAt, ttlMillis: appCheckToken.ttlMillis}
        })
        .catch(function (err) {
            console.error("Unable to create App Check token.");
            console.error(err);

            return "error"
        });
});

Now of course I can just check if the "expiresAt" received from the function is smaller than System.currentTimeInMills() and then return exception in the getToken() method, but that means any user who changed his device time in settings wont be able to user my app.
Is it a bug or just something I am doing wrong?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions