Open eeshaan opened 1 year ago
We seem to have the same issue using calcom-docker (with cal.com v2.4.4). Were you able to fix it?
have you tried the latest here? https://hub.docker.com/r/calcom/cal.com/tags
We've used the Dockerfile in the calcom/docker project to build our own image, as otherwise non-localhost domain names don't work as far as I understood.
right that makes sense. hmm that is odd
maybe @zomars knows more
No clue. It seems like the token gets invalidated. Maybe the encryption key resets on restart? Although that would mean password wouldn't work as well. @krumware any ideas?
I'm very curious as to the expiration and date portion of the request. Is this 'expires' value normal?
@calcom/web:start: date: 'Tue, 29 Nov 2022 04:43:04 GMT',
@calcom/web:start: expires: 'Mon, 01 Jan 1990 00:00:00 GMT',
Through some googling, this could be related to a number of configuration items, not necessarily those specific to the cal.com side. I've had problems in the past when the callback URL wasn't added properly, or my environmental variable had invalid characters or extra quotes.
Quick note, non-localhost domains do work via calcom/docker
Thanks for looking into it! If its a configuration issue or an issue with the callback URL then it shouldn't work after reconnecting the google account, right? But that seems to work for now, as long as we don't restart or recreate the container.
@krumware re non-localhost domains, in the Readme it says:
...there are specific requirements for providing environmental variables at build-time in order to specify a non-localhost BASE_URL. ...
and also
For Production, for the time being, please checkout the repository and build/push your own image privately.
So it seems like we cannot use the pre-built image but need to build our own?
That's really interesting around the restart. We'll continue to monitor.
You can use it, it's not a license restriction or anything. It's just general best practice for container immutability to not allow some file writes at runtime, and some more compliance-heavy environments may have controls affecting that. So the true "enterprise production" recommendation would be to build it (until we fix that). But otherwise for most applications it's fine to run the provided container. We'll try to make that more clear!
Ok it seems that the restarting (or recreating) the container has nothing to do with it! Its that the google tokens cannot get refreshed. Had it running w/o restart and after roughly one week the google tokens expire and I see this error:
calcom | @calcom/web:start: 22:16:18.547 ERROR [[lib] google_calendar Error refreshing google token
calcom | @calcom/web:start: Error invalid_grant
calcom | @calcom/web:start: details:
calcom | @calcom/web:start: <ref *1> {
calcom | @calcom/web:start: response: {
calcom | @calcom/web:start: config: {
calcom | @calcom/web:start: method: 'POST',
calcom | @calcom/web:start: url: 'https://oauth2.googleapis.com/token',
calcom | @calcom/web:start: data: 'refresh_token=<redacted>&client_id=<redacted>.apps.googleusercontent.com&client_secret=<redacted>&grant_type=refresh_token',
calcom | @calcom/web:start: headers: {
calcom | @calcom/web:start: 'Content-Type': 'application/x-www-form-urlencoded',
calcom | @calcom/web:start: 'User-Agent': 'google-api-nodejs-client/7.14.1',
calcom | @calcom/web:start: 'x-goog-api-client': 'gl-node/16.19.0 auth/7.14.1',
calcom | @calcom/web:start: Accept: 'application/json'
calcom | @calcom/web:start: },
calcom | @calcom/web:start: paramsSerializer: [Function: paramsSerializer],
calcom | @calcom/web:start: body: 'refresh_token=<redacted>&client_id=<redacted>.apps.googleusercontent.com&client_secret=<redacted>&grant_type=refresh_token',
calcom | @calcom/web:start: validateStatus: [Function: validateStatus],
calcom | @calcom/web:start: responseType: 'json'
calcom | @calcom/web:start: },
calcom | @calcom/web:start: data: {
calcom | @calcom/web:start: error: 'invalid_grant',
calcom | @calcom/web:start: error_description: 'Token has been expired or revoked.'
calcom | @calcom/web:start: },
calcom | @calcom/web:start: headers: {
calcom | @calcom/web:start: 'alt-svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"',
calcom | @calcom/web:start: 'cache-control': 'no-cache, no-store, max-age=0, must-revalidate',
calcom | @calcom/web:start: connection: 'close',
calcom | @calcom/web:start: 'content-encoding': 'gzip',
calcom | @calcom/web:start: 'content-type': 'application/json; charset=utf-8',
calcom | @calcom/web:start: date: 'Sun, 22 Jan 2023 22:16:18 GMT',
calcom | @calcom/web:start: expires: 'Mon, 01 Jan 1990 00:00:00 GMT',
calcom | @calcom/web:start: pragma: 'no-cache',
calcom | @calcom/web:start: server: 'scaffolding on HTTPServer2',
calcom | @calcom/web:start: 'transfer-encoding': 'chunked',
calcom | @calcom/web:start: vary: 'Origin, X-Origin, Referer',
calcom | @calcom/web:start: 'x-content-type-options': 'nosniff',
calcom | @calcom/web:start: 'x-frame-options': 'SAMEORIGIN',
calcom | @calcom/web:start: 'x-xss-protection': '0'
calcom | @calcom/web:start: },
calcom | @calcom/web:start: status: 400,
calcom | @calcom/web:start: statusText: 'Bad Request',
calcom | @calcom/web:start: request: {
calcom | @calcom/web:start: responseURL: 'https://oauth2.googleapis.com/token'
calcom | @calcom/web:start: }
calcom | @calcom/web:start: },
calcom | @calcom/web:start: config: [Circular *1],
calcom | @calcom/web:start: code: '400'
calcom | @calcom/web:start: }
calcom | @calcom/web:start: error stack:
calcom | @calcom/web:start: • gaxios.ts:158 _request
calcom | @calcom/web:start: node_modules/gaxios/src/gaxios.ts:158:15
calcom | @calcom/web:start:
calcom | @calcom/web:start: • task_queues:96 processTicksAndRejections
calcom | @calcom/web:start: node:internal/process/task_queues:96:5
calcom | @calcom/web:start:
calcom | @calcom/web:start: • oauth2client.js:174 refreshTokenNoCache
calcom | @calcom/web:start: node_modules/google-auth-library/build/src/auth/oauth2client.js:174:21
calcom | @calcom/web:start:
calcom | @calcom/web:start: • 7105.js:1116 refreshAccessToken
calcom | @calcom/web:start: .next/server/chunks/7105.js:1116:34
calcom | @calcom/web:start:
calcom | @calcom/web:start: • 7105.js:1344 <anonymous>
calcom | @calcom/web:start: .next/server/chunks/7105.js:1344:3
The google credential environment variable looks like this:
GOOGLE_API_CREDENTIALS={"web":{"client_id":"<reacted>.apps.googleusercontent.com","project_id":"<redacted>","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"<redacted>","redirect_uris":["https://<redacted>/api/integrations/googlecalendar/callback","https://<redacted>/api/auth/callback/google"]}}
And the google console settings are:
The google cloud app is not published but in testing mode could that be a reason for this also?
Ok it seems that the restarting (or recreating) the container has nothing to do with it! Its that the google tokens cannot get refreshed. Had it running w/o restart and after roughly one week the google tokens expire
+1 on this. Even though the issue is most easily replicated with a restart, I've also observed the same behavior after some arbitrary amount of runtime (also around 1 week). Unsure on this, but I think one possibility is that the token expires when server usage spikes and the app is briefly inaccessible.
The google cloud app is not published but in testing mode could that be a reason for this also?
The same is also true in our case.
That's good to know!
Apologies for reviving the topic, but I am facing the same issue, and I can't find any other mention of how to deal with this, so I'm trying to see if anyone ever found a way to tackle this please ? Having to relink my calendars every few days is not really sustainable, so I imagine some people have found a root cause or a workaround ?
This is the error I see on my end fyi:
@calcom/web:start: 14:28:02:982 DEBUGapp-store/_utils/oauth/OAuthManager handleFetchError Error "Error: invalid_grant
at Gaxios._request (/calcom/node_modules/gaxios/build/src/gaxios.js:129:23)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async MyGoogleAuth.refreshTokenNoCache (/calcom/node_modules/google-auth-library/build/src/auth/oauth2client.js:174:21)
at async OAuthManager.fetchNewTokenObject (/calcom/apps/web/.next/server/chunks/72628.js:1:1244)
at async OAuthManager.refreshOAuthToken (/calcom/apps/web/.next/server/chunks/15240.js:1:5848)
at async OAuthManager.getTokenObjectOrFetch (/calcom/apps/web/.next/server/chunks/15240.js:1:2339)
at async Object.getMyGoogleAuthWithRefreshedToken (/calcom/apps/web/.next/server/chunks/72628.js:1:2059)
at async GoogleCalendarService.authedCalendar (/calcom/apps/web/.next/server/chunks/72628.js:1:2242)
at async GoogleCalendarService.listCalendars (/calcom/apps/web/.next/server/chunks/72628.js:1:10030)
at async /calcom/apps/web/.next/server/chunks/44121.js:1:1968"
@calcom/web:start: 14:28:02:985 DEBUGapp-store/_utils/oauth/OAuthManager refreshOAuthToken Response from refreshOAuthToken {"text":"{\"myFetchError\":\"invalid_grant\"}","ok":false,"status":500,"statusText":""}
@calcom/web:start: 14:28:02:990 ERRORapp-store/_utils/oauth/OAuthManager refreshOAuthToken Token parsing error: [{"code":"invalid_type","expected":"string","received":"undefined","path":["access_token"],"message":"Required"}]
Thanks vm in advance !
I had the same issue and in the end I switchted to cal.com directly avoiding hosting it on my server :(
I tried to look into the code, google, etc. It seems that it's because Google Calendar -> CalendarService.ts somehow doesn't detect properly that a token is expired, and tries to refresh it after its expiry date here:
fetchNewTokenObject: async ({ refreshToken }: { refreshToken: string | null }) => {
const myGoogleAuth = await this.getMyGoogleAuthSingleton();
const fetchTokens = await myGoogleAuth.refreshToken(refreshToken);
// Create Response from fetchToken.res
const response = new Response(JSON.stringify(fetchTokens.res?.data ?? null), {
status: fetchTokens.res?.status,
statusText: fetchTokens.res?.statusText,
});
return response;
I'm (really) no expert, but should there maybe be a check before calling myGoogleAuth.refreshToken() that token is actually not expired ? I'm getting out of ideas, not sure how this is not more widespread, I imagine a lot of people self host, and a lot of people use google calendar, so it's really surprising to me that not more people seem to face this ?
Also it happens after 1 week exactly, because the tokens granted by Google oAuth are valid for 1 week (I checked epoch from the logs), which is why this starts happening after a week of usage.
I am also having App Status = Testing, as I haven't published it. Might it be that "Testing" tokens have 1 week duration while "Published" app ones don't have ? That would be a bit surprising, but who knows. In any case, I think handling expired tokens should work. Need to ask for a new one instead of refreshing existing one.
From Using OAuth 2.0 to Access Google APIs (official Google docs):
A Google Cloud Platform project with an OAuth consent screen configured for an external user type and a publishing status of "Testing" is issued a refresh token expiring in 7 days, unless the only OAuth scopes requested are a subset of name, email address, and user profile (through the userinfo.email, userinfo.profile, openid scopes, or their OpenID Connect equivalents).
This essentially means that if your Google OAuth consent screen has the "Testing" status, the user has to re-authenticate weekly for it to keep working.
If you're fine with scary warnings when people link their Google Calendar, a possible solution is to click 'Publish app' so it enters "Production" (but unverified) status.
Thank you so much for taking the time to check. I tried that before, but was put off by the verification process where Google rejected my app (as it's for a single user...). But thanks to you I just realised that I can publish it (and set it to Production), while still staying unverified.
I have now high hopes this should work, so thanks again very much for taking the time to comment. I still have a feeling that it should be feasible to refresh a token before it expires to keep it alive, but if this works, well, this works ! ^^
I have now high hopes this should work, so thanks again very much for taking the time to comment.
Me too, it's just not clear to me yet that this will keep working indefinitely.
You're welcome!
I still have a feeling that it should be feasible to refresh a token before it expires to keep it alive, but if this works, well, this works ! ^^
No: there are two types of tokens: auth tokens and refresh tokens. Auth tokens are used to authenticate a user with the actual API and are always shortlived, but can be refreshed with a refresh token. This refresh token is supposed to work indefinitely (unless the user removes the connection), unless the application is in 'Testing' mode, in which case they are only valid for one week (and can only be removed and recreated by having the user log in again with their Google account).
I think this workaround should be added to the docs 😃
Well, 2 weeks later, I wanted to confirm that this solved the issue.
@lesderid , just wanted to say thank you again for taking the time to answer, and give detailed explanations about types of tokens, etc.
Really appreciated ! 👍
Issue Summary
When stopping the Cal.com web instance and restarting it, the Google Calendar response comes back as with a
400
bad request. Instead of attempted to fetch a new token, the Google Calendar integration breaks until the user removes the calendar connection and signs back into their Google account.Steps to Reproduce
@calcom/web
invalid_grant
on Google CalendarTechnical details
cal.com v2.3.2 — though the issue has been present in previous releases as well.