apple-signin-auth node js react native error: Invalid id token public key id

1.3k views Asked by At

I have a React native front end where I use invertase/react-native-apple-authentication to handle Apple Authentication.

Then I have a NodeJS back end, where I use A-Tokyo/apple-signin-auth to handle Apple authenticated users and let them access routes.

I made this authentication based on this article.

I want the users to be able use the app without logging in again without a time limit. Therefore I save the identity token, which I get when the user does the first sign up in Async Storage in the front-end. Every time the user tries to access routes the user will be checked if he/she has a identityToken in the Header in my isAuth middleware in the NodeJS backend for the respective request.

I can see in my logs not sometimes requests get the following error the backend in my isAuth middleware:

JsonWebTokenError: error in secret or public key callback: input error: Invalid id token public key id at /app/node_modules/jsonwebtoken/verify.js:96:19 at _getIdTokenApplePublicKey (/app/node_modules/apple-signin-auth/lib/index.js:1:5730) at runMicrotasks () at processTicksAndRejections (internal/process/task_queues.js:95:5)

The error is thrown in the apple-signin-auth library when executing this code:

const appleSignin = require("apple-signin-auth");

result = await appleSignin.verifyIdToken(token, {
    audience: config.CLIENT_ID_APPLE,
    ignoreExpiration: true, // ignore token expiry (never expires)
});

I am not sure why this is happening. When I check the tokens, they seem fine but expired. The CLIENT_ID is right 100%. Does it has something todo with the expiration of the token?

A list for valid options for the appleSignin.verifyIdToken function is stated here.

Thanks for the help!

What I found out is that the public keys from the apple endpoint: https://appleid.apple.com/auth/keys are changing over time. The library is using those keys for decoding or validating the identityToken. As a result the key ids are not matching with the key id from the identity token I saved in the Async Storage. It cannot find the matching kid in the public keys because they are not there anymore. I am thinking about what solution to implement.

Possible solution: Instead of storing the IdentityToken, I should store the RefreshToken in the AsyncStorage in the front end. Hopefully that's a valid approach. (Token based authentication) And then request a new IdentityToken with every back-end request in the isAuth Middleware with the RefreshToken (which according to Apple is endlessly valid). Then verify the IdentityToken with the appleSignin.verifyIdToken method of apple-signin-auth package and give user access to route or not. The public keys from the Apple endpoint are always up-to-date, since the identity token is always requested again.

1

There are 1 answers

0
rmoestl On BEST ANSWER

As far as I understand the workflow, you verify the identity token in the backend only once when the user has authenticated themselves using "Sign in with Apple" on the device.

If verifying the identity token in the backend was successful, you receive a refresh token in the response. You are then supposed to save this refresh token in your backend and verify the refresh token once a day to check if the user is still logged in with Apple. What does that mean? For example a user could revoke access to your app. Or a different user could log in on the Apple device.

By the way, if you verify the refresh token on every request (read multiple times a day), you risk Apple throttling these requests.

Bear in mind that this doesn't free your system from rolling its own session management meaning that your system sends its own session ids back and forth between the backend and front-end. Once a day, you check the refresh token associated with a session to see if the user is still logged in.

Disclaimer: This is how I understood the docs of Sign in with Apple. In other works, I have no experience implementing it. Hope it helps nonetheless.