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.