错误 403:“请求的身份验证范围不足”,即使相关范围已激活

Posted

技术标签:

【中文标题】错误 403:“请求的身份验证范围不足”,即使相关范围已激活【英文标题】:Error 403: 'Request had insufficient authentication scopes' even though the relevant scopes are activated 【发布时间】:2021-10-03 07:45:31 【问题描述】:

我正在尝试构建一个可以从 Gmail 帐户发送电子邮件的基本脚本,我的代码主要来自:https://ui.ads.microsoft.com/campaign/vnext/accounts/overview/expert?cid=19013943&uid=93024518

我已经尝试了我发现的常见修复,例如启用GMAIL API,授权相应的调用,目前授权的是:

.../auth/gmail.send .../auth/docs .../auth/驱动器 .../auth/drive.scripts .../auth/电子表格 .../auth/gmail.modify .../auth/gmail.compose https://mail.google.com/
function main() 
    var MailApp = new Bing();    
    const notificationEmail = '**my-email**';
    var email = [
        `To: $notificationEmail`,
        'Subject: Google Services script',
        '',
        `You script ran successfully ✓`
    ].join('\n');
    MailApp.sendEmail(email);  



class Bing
    constructor() 
        this.credentials = 

            accessToken: '',
            clientId: '**my-client-id**',
            clientSecret: '**my-client-secret**',
            refreshToken: '**my-refresh-token**'

        ;
        this.gmailApi = GoogleApis.createGmailService(this.credentials);
    
    sendEmail(email) 
    



// ^ Basic Copy+Paste from tutorial mentioned above ^
var GoogleApis;
(function (GoogleApis) 
  function createGmailService(credentials) 
    return createService("https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest", credentials);
  
  GoogleApis.createGmailService = createGmailService;
 
  // Creation logic based on https://developers.google.com/discovery/v1/using#usage-simple
  function createService(url, credentials) 
    var content = UrlFetchApp.fetch(url).getContentText();
    var discovery = JSON.parse(content);
    var baseUrl = discovery['rootUrl'] + discovery['servicePath'];
    var accessToken = getAccessToken(credentials);
    var service = build(discovery, , baseUrl, accessToken);
    return service;
  
 
  function createNewMethod(method, baseUrl, accessToken) 
    return (urlParams, body) => 
      var urlPath = method.path;
      var queryArguments = [];
      for (var name in urlParams) 
        var paramConfg = method.parameters[name];
        if (!paramConfg) 
          throw `Unexpected url parameter $name`;
        
        switch (paramConfg.location) 
          case 'path':
            urlPath = urlPath.replace('' + name + '', urlParams[name]);
            break;
          case 'query':
            queryArguments.push(`$name=$urlParams[name]`);
            break;
          default:
            throw `Unknown location $paramConfg.location for url parameter $name`;
        
      
      var url = baseUrl + urlPath;
      if (queryArguments.length > 0) 
        url += '?' + queryArguments.join('&');
      
      var httpResponse = UrlFetchApp.fetch(url,  contentType: 'application/json', method: method.httpMethod, payload: JSON.stringify(body), headers:  Authorization: `Bearer $accessToken` , muteHttpExceptions: true );
      var responseContent = httpResponse.getContentText();
      var responseCode = httpResponse.getResponseCode();
      var parsedResult;
      try 
        parsedResult = JSON.parse(responseContent);
       catch (e) 
        parsedResult = false;
      
      var response = new Response(parsedResult, responseContent, responseCode);
      if (responseCode >= 200 && responseCode <= 299) 
        return response;
      
      throw response;
    
  

    function Response(result, body, status) 
    this.result = result;
    this.body = body;
    this.status = status;
  
  Response.prototype.toString = function () 
    return this.body;
  
 
  function build(discovery, collection, baseUrl, accessToken) 
    for (var name in discovery.resources) 
      var resource = discovery.resources[name];
      collection[name] = build(resource, , baseUrl, accessToken);
    
    for (var name in discovery.methods) 
      var method = discovery.methods[name];
      collection[name] = createNewMethod(method, baseUrl, accessToken);
    
    return collection;
  
 
  function getAccessToken(credentials) 
    if (credentials.accessToken) 
      return credentials.accessToken;
    
    var tokenResponse = UrlFetchApp.fetch('https://www.googleapis.com/oauth2/v4/token',  method: 'post', contentType: 'application/x-www-form-urlencoded', muteHttpExceptions: true, payload:  client_id: credentials.clientId, client_secret: credentials.clientSecret, refresh_token: credentials.refreshToken, grant_type: 'refresh_token'  );    
    var responseCode = tokenResponse.getResponseCode(); 
    var responseText = tokenResponse.getContentText(); 
    if (responseCode >= 200 && responseCode <= 299) 
      var accessToken = JSON.parse(responseText)['access_token'];
      return accessToken;
        
    throw responseText;  
  
)(GoogleApis || (GoogleApis = ));

class Base64 
  static encode(input) 
    const keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    let output = "";
    let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    var i = 0;
    input = this.utf8Encode(input);
    while (i < input.length) 
      chr1 = input.charCodeAt(i++);
      chr2 = input.charCodeAt(i++);
      chr3 = input.charCodeAt(i++);
      enc1 = chr1 >> 2;
      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
      enc4 = chr3 & 63;
      if (isNaN(chr2)) 
        enc3 = enc4 = 64;
      
      else if (isNaN(chr3)) 
        enc4 = 64;
      
      output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
    
    return output.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
  
  static utf8Encode(input) 
    input = input.replace(/\r\n/g, "\n");
    var utftext = "";
    for (var n = 0; n < input.length; n++) 
      var c = input.charCodeAt(n);
      if (c < 128) 
        utftext += String.fromCharCode(c);
      
      else if ((c > 127) && (c < 2048)) 
        utftext += String.fromCharCode((c >> 6) | 192);
        utftext += String.fromCharCode((c & 63) | 128);
      
      else 
        utftext += String.fromCharCode((c >> 12) | 224);
        utftext += String.fromCharCode(((c >> 6) & 63) | 128);
        utftext += String.fromCharCode((c & 63) | 128);
      
    
    return utftext;
  

我使用本教程中的选项 2 从谷歌获得了 OAuth2:https://docs.microsoft.com/en-us/advertising/scripts/examples/authenticating-with-google-services

范围已正确启用,即 .../auth/gmail.send

我仍然收到以下错误:


"error": 
"code": 403,
"message": "Request had insufficient authentication scopes.",
"errors": [

"message": "Insufficient Permission",
"domain": "global",
"reason": "insufficientPermissions"

],
"status": "PERMISSION_DENIED"


【问题讨论】:

你在哪里添加你的代码中的范围我看不到它。 【参考方案1】:

我在您的代码中看不到您提供范围的任何地方,您应该尝试关注JS quickstart

<!DOCTYPE html>
<html>
  <head>
    <title>Gmail API Quickstart</title>
    <meta charset="utf-8" />
  </head>
  <body>
    <p>Gmail API Quickstart</p>

    <!--Add buttons to initiate auth sequence and sign out-->
    <button id="authorize_button" style="display: none;">Authorize</button>
    <button id="signout_button" style="display: none;">Sign Out</button>

    <pre id="content" style="white-space: pre-wrap;"></pre>

    <script type="text/javascript">
      // Client ID and API key from the Developer Console
      var CLIENT_ID = '<YOUR_CLIENT_ID>';
      var API_KEY = '<YOUR_API_KEY>';

      // Array of API discovery doc URLs for APIs used by the quickstart
      var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest"];

      // Authorization scopes required by the API; multiple scopes can be
      // included, separated by spaces.
      var SCOPES = 'https://www.googleapis.com/auth/gmail.readonly';

      var authorizeButton = document.getElementById('authorize_button');
      var signoutButton = document.getElementById('signout_button');

      /**
       *  On load, called to load the auth2 library and API client library.
       */
      function handleClientLoad() 
        gapi.load('client:auth2', initClient);
      

      /**
       *  Initializes the API client library and sets up sign-in state
       *  listeners.
       */
      function initClient() 
        gapi.client.init(
          apiKey: API_KEY,
          clientId: CLIENT_ID,
          discoveryDocs: DISCOVERY_DOCS,
          scope: SCOPES
        ).then(function () 
          // Listen for sign-in state changes.
          gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);

          // Handle the initial sign-in state.
          updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
          authorizeButton.onclick = handleAuthClick;
          signoutButton.onclick = handleSignoutClick;
        , function(error) 
          appendPre(JSON.stringify(error, null, 2));
        );
      

      /**
       *  Called when the signed in status changes, to update the UI
       *  appropriately. After a sign-in, the API is called.
       */
      function updateSigninStatus(isSignedIn) 
        if (isSignedIn) 
          authorizeButton.style.display = 'none';
          signoutButton.style.display = 'block';
          listLabels();
         else 
          authorizeButton.style.display = 'block';
          signoutButton.style.display = 'none';
        
      

      /**
       *  Sign in the user upon button click.
       */
      function handleAuthClick(event) 
        gapi.auth2.getAuthInstance().signIn();
      

      /**
       *  Sign out the user upon button click.
       */
      function handleSignoutClick(event) 
        gapi.auth2.getAuthInstance().signOut();
      

      /**
       * Append a pre element to the body containing the given message
       * as its text node. Used to display the results of the API call.
       *
       * @param string message Text to be placed in pre element.
       */
      function appendPre(message) 
        var pre = document.getElementById('content');
        var textContent = document.createTextNode(message + '\n');
        pre.appendChild(textContent);
      

      /**
       * Print all Labels in the authorized user's inbox. If no labels
       * are found an appropriate message is printed.
       */
      function listLabels() 
        gapi.client.gmail.users.labels.list(
          'userId': 'me'
        ).then(function(response) 
          var labels = response.result.labels;
          appendPre('Labels:');

          if (labels && labels.length > 0) 
            for (i = 0; i < labels.length; i++) 
              var label = labels[i];
              appendPre(label.name)
            
           else 
            appendPre('No Labels found.');
          
        );
      

    </script>

    <script async defer src="https://apis.google.com/js/api.js"
      onload="this.onload=function();handleClientLoad()"
      onreadystatechange="if (this.readyState === 'complete') this.onload()">
    </script>
  </body>
</html>

【讨论】:

以上是关于错误 403:“请求的身份验证范围不足”,即使相关范围已激活的主要内容,如果未能解决你的问题,请参考以下文章

Google Calendar API - 请求的身份验证范围不足

请求的身份验证范围不足 youtube api

Google Clas-s-room Pub/Sub 注册返回 403 身份验证错误

Youtube 评论 API 抛出“权限不足:请求的身份验证范围不足”错误

Google Calendar API 错误请求的身份验证范围不足

如何使用 C# 向谷歌聊天室发布消息? (**错误**:请求的身份验证范围不足)