Closed nicholaschiang closed 3 years ago
Reopening this because the id_token
(returned by Google's OAuth2 API) that is stored as a cookie client-side and used to authenticate users server-side seems to expire after a couple of hours (I'm guessing that it also expires after 60 mins):
Your JWT is invalid: Token used too late, 1620237357.595 > 1620234917:
{
"iss":"https://accounts.google.com",
"azp":"1852201844-hqm8c38munh63mo9gb90r02qh8953hms.apps.googleusercontent.com",
"aud":"1852201844-hqm8c38munh63mo9gb90r02qh8953hms.apps.googleusercontent.com",
"sub":"110270419839003533489",
"hd":"tutorbook.org",
"email":"nicholas@tutorbook.org",
"email_verified":true,
"at_hash":"j0s-vmhsNAGaQC80gRVwOA",
"name":"Nicholas Chiang",
"picture":"https://lh3.googleusercontent.com/a/AATXAJw5F58517n9XKGW6t3YicMuZDNN6eKE7NCDQU5h=s96-c",
"given_name":"Nicholas",
"family_name":"Chiang",
"locale":"en",
"iat":1620231017,
"exp":1620234617
}.
That ☝️ also looks like I don't have to call the userinfo
API to get the user's name
, picture
, and locale
. Changing that might be a useful performance optimization (e.g. only fetch userinfo
when it doesn't exist on the decoded id_token
).
Also, we shouldn't require the user to grant permissions every time if they've already done so and thus we already have their refresh_token
stored in our database. Instead, they should be able to just select their account and immediately login.
@nicholaschiang Can we also keep them "logged in"? So they don't have to choose their Google account every time they open the reader?
My current fix stores the OAuth2 refresh token client-side in an authorization cookie. This cookie is marked as both httpOnly
(inaccessible to JavaScript) and secure
(can only be sent over encrypted HTTPS connection). This setup could be a good advertising point: we don't have to store the OAuth2 token in our database and thus we can only access your email when you're using the app. To revoke permissions, simply get rid of the cookie (by clearing site data) and we can't access anything!
Essentially we have three tokens:
Currently, @kunalmodi had been using the
credential.accessToken
returned by the Firebase Authentication SDK like so:However, that
accessToken
expires after 60mins and the Firebase Authentication SDK does not give you access to the Google OAuth2 refresh tokens so there's no way to refresh access to Google's API after 60mins:Instead, I should work directly with Google's OAuth2 libraries (or REST API endpoints) and then use the returned
googleIdToken
to login to the Firebase SDK client-side (which I'll keep using for server-side authentication):Some links to documentation (it seems the low-level OAuth2 documentation is spread everywhere haha):
googleapis
librarygapi
libraryNote that the
gapi
libraries cannot be bundled with the rest of the app which is really annoying IMO. I want to have complete control over tree-shaking, code-splitting, and just how the browser loads my code in general. Thus, I'll probably opt to use a Node.js server-side implementation or just call Google's OAuth2 REST API endpoints directly.