簡體   English   中英

接收 Google Calendar API 通知時出現 403 錯誤

[英]403 error when receiving Google Calendar API notification

我為各種 GSuite 用戶成功創建了日歷事件,然后我想設置一個觀看事件。 我能夠成功地做到這一點:

SCOPES = ['https://www.googleapis.com/auth/calendar']
SERVICE_ACCOUNT_FILE = BASE_DIR + 'path_to_json'

credentials = service_account.Credentials.from_service_account_file(
    SERVICE_ACCOUNT_FILE, scopes=SCOPES)

delegated_user = credentials.with_subject(user)
delegated_service = googleapiclient.discovery.build('calendar', 'v3', credentials=delegated_user)


event = {
        'summary': '%s at %s' % (event_name, event_location),
        'description': 'description',
        'location': event_address,
        'extendedProperties': {
            "private": {
                'briefType': event_name,
            }
        },
        'start': {
            'dateTime': event_start_time.astimezone().isoformat(),
        },
        'end': {
            'dateTime': event_end_time.astimezone().isoformat(),
        }

    }


event = delegated_service.events().insert(calendarId='primary', body=event).execute()

watch_body = {
    'id': event['id'],
    'type': 'web_hook',
    'address': 'https://example.com/googleapi'
}

watch = delegated_service.events().watch(calendarId='primary', body=watch_body).execute()

我不太確定如何接收推送通知。 我已經注冊/添加了我的域(根據https://developers.google.com/calendar/v3/push ),當我在 Google 日歷中更改事件時,我收到了 API 推送通知,但我收到了403 錯誤:

Dec 11 20:58:38 ip-xxx gunicorn[3104]:  - - [11/Dec/2019:20:58:38 +0000] "POST /googleapi HTTP/1.0" 403 1889 "-" "APIs-Google; (+https://developers.google.com/webmasters/APIs-Google.html)"

如何在處理/googleapiview中進行身份驗證才能實際接收來自 Google 的 API 通知?

我嘗試了以下操作只是為了調試我從 Google 收到的內容,但除了 403 錯誤之外我什么也沒得到:

def GoogleAPIWatchView(request):

    SCOPES = ['https://www.googleapis.com/auth/calendar']
    SERVICE_ACCOUNT_FILE = BASE_DIR + 'path_to_json'

    credentials = service_account.Credentials.from_service_account_file(
            SERVICE_ACCOUNT_FILE, scopes=SCOPES)

    regex = re.compile('^HTTP_')
    headers = dict((regex.sub('', header), value) for (header, value) 
           in request.META.items() if header.startswith('HTTP_'))

    print(headers)
    print(request)

    return JsonResponse('success', status=200, safe=False)

您的應用程序正在嘗試使用服務帳戶訪問用戶數據,這是正確的,也是這樣做的,但由於您沒有授予此權限,因此您會收到 403 服務器響應。

為了能夠在您嘗試使用服務帳戶時使用服務帳戶運行您的應用程序,必須授予以下權限:

全域委托權限:

如果服務帳戶沒有域的委派權限,則使用服務帳戶的應用程序無法代表域用戶訪問數據。

要啟用它,請轉到此處

  1. 選擇您的項目
  2. 選擇您的服務帳戶或創建一個新帳戶
  3. 單擊“編輯”以編輯服務帳戶的參數
  4. 選擇“啟用 G Suite 域范圍委派”

您的服務帳戶現在具有域范圍的委派。

接下來,您需要設置服務帳戶的范圍。

服務帳號范圍:

G Suite 域的管理員必須按照以下步驟操作:

  1. 轉到您的 G Suite 域管理控制台
  2. 從控件列表中選擇安全
  3. 從選項列表中選擇高級設置
  4. 在身份驗證部分選擇管理 API 客戶端訪問
  5. 在客戶端名稱字段中輸入服務帳戶的客戶端 ID。 您可以在服務帳戶頁面中找到您的服務帳戶的客戶 ID
  6. 在一個或多個 API 范圍字段中,輸入您的應用程序應被授予訪問權限的范圍列表。 在這種情況下: https : //www.googleapis.com/auth/calendar
  7. 授權

關於域范圍授權和創建服務帳戶的 Google 文檔:

https://developers.google.com/identity/protocols/OAuth2ServiceAccount

正如@JPG 正確指出的那樣,您的api 拒絕了未經授權的請求- 來自 Google 的推送通知。 CSRF是最可能的原因 - 不安全的操作(POST、PUT、DELETE)要求請求中存在 csrf 令牌。

當然,其他域的 api 客戶端無法提供,因為它主要是為了保護來自網頁的跨域 ajax 請求,而不是非交互式 api 調用。

由於谷歌 API 在訪問視圖時無法授權為普通用戶,我們需要視圖對所有人開放,並允許來自所有人的POST請求,我們從 csrf 檢查中刪除GoogleAPIWatchView

盡管如此,它仍然需要保護。

為了確保請求確實是由 Google API 發出的,我們可以檢查 Google API 添加到請求中的某些標頭的存在/值:

注意:所有這些標頭都可能被惡意方欺騙。 對於額外的檢查, 您可以在訂閱觀看通知時定義自定義令牌 該令牌包含在您的應用程序為此通道接收的每條通知消息的X-Goog-Channel-Token HTTP 標頭中。

...
CHANNEL_TOKEN = "mysecrettoken"
watch_body = {
    'id': event['id'],
    'type': 'web_hook',
    'address': 'https://example.com/googleapi',
    'token': CHANNEL_TOKEN
}
...

在視圖中,我們可以執行必要的檢查以確保請求來自 Google:

from django.http import HttpResponseForbidden

@csrf_exempt  # This skips csrf validation.
def GoogleAPIWatchView(request):
    # Validate request came from Google
    if not 'APIs-Google' in request.META.get('HTTP_USER_AGENT', ''):
        return HttpResponseForbidden()
    if not request.META.get('HTTP_X_GOOG_CHANNEL_ID'):
        return HttpResponseForbidden()
    if not request.META.get('HTTP_X_GOOG_CHANNEL_TOKEN') == CHANNEL_TOKEN:
        return HttpResponseForbidden()

    ...

Django Rest Framework 的情況下,您還需要無需身份驗證即可查看所有人。 如果AUTHENTICATION_CLASSES不存在SessionAuthentication - 我們可以跳過@csrf_extempt裝飾器,因為在這種情況下不執行csrf 檢查。

@api_view(['POST'])
def google_push_notification(request):
    authentication_classes = []
    permission_classes = [AllowAny]  # or empty []
    # check google headers
    ...

似乎這個HTTP 403是由於CSRF token 驗證失敗而引發的。 所以,使用csrf_exempt --Django doc裝飾器跳過驗證過程

#views.py
from django.views.decorators.csrf import csrf_exempt @csrf_exempt # This skips csrf validation.
def GoogleAPIWatchView(request):
    ...
    # do something useful

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM