使用服务账号调用Google Calendar Events Watch遇401 Unauthorized错误
Alright, let's tackle that 401 error you're hitting when setting up calendar event watches—especially since you can already list calendars successfully. Here's what's going wrong and how to fix it:
1. Your Axios Request Structure Is Incorrect
The biggest issue here is how you're passing the watch configuration to Axios. Right now, you're mixing the request body data (the type and address for the webhook) with the request headers/config. Axios expects the post body as the second parameter, and the headers/config as the third.
Your current code sends the headers and webhook data together as the request body, which Google doesn't recognize—leading to authentication failure. Here's the corrected version:
let { google } = require("googleapis"); let privatekey = require("../config.json"); let axios = require("axios"); async function main() { const client = new google.auth.JWT( privatekey.client_email, null, privatekey.private_key, [ "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/calendar.readonly" ] ); const token = await client.authorize(); // Separate the watch body from the request headers const watchRequestBody = { type: "web_hook", address: "https://rguc-calendars.firebaseapp.com/notifications" }; const requestConfig = { headers: { "Content-Type": "application/json;charset=UTF-8", Authorization: `${token.token_type} ${token.access_token}` } }; try { const response = await axios.post( "https://www.googleapis.com/calendar/v3/calendars/rguc.co.uk_5hbugn8f7a4j3g7p6im1r1s1co@group.calendar.google.com/events/watch", watchRequestBody, requestConfig ); console.log("Watch setup successful:", response.data); } catch (error) { console.error("Error setting up watch:", error.response?.data || error.message); } } main();
2. Double-Check Critical Setup Details
Even with fixed code, a few other things could cause 401s:
- Webhook Domain Verification: Ensure the domain of your webhook address (
firebaseapp.com) is properly verified in your Google Cloud Console. Google requires this for push notifications to prevent spam. - Service Account Calendar Permissions: Confirm the service account email (
googlecalendarservice@eventapi-219011.iam.gserviceaccount.com) is added as a member to the target calendar with at least "See all event details" permissions (or higher if you need to act on changes). - Token Validity: While
client.authorize()fetches a fresh token, you can logtoken.access_tokento confirm it's a valid, non-expiring (for the moment) token.
3. Correct Credential Type to Use
From your list of credentials, you should stick with Service account keys (the one labeled 5ac83bf728fc9f3f635cec8096170573620dd388 for GoogleCalendarService). Here's why:
- API keys only work for public, unauthenticated endpoints—they can't access user-specific calendar data or set up watches.
- OAuth 2.0 client IDs are for user-facing apps where a human logs in to grant permissions. They don't fit your server-to-server use case.
- Service account keys are designed exactly for this scenario: server-side, no-user-interaction access to Google APIs with pre-granted permissions.
Give the corrected code a shot, and verify those setup details—you should be able to get the watch endpoint working without the 401.
内容的提问来源于stack exchange,提问作者Bomber

