如何在没有手动浏览器身份验证的情况下从 Meteor.js 应用程序对 GMail-api 进行 oauth (2) 身份验证?

Posted

技术标签:

【中文标题】如何在没有手动浏览器身份验证的情况下从 Meteor.js 应用程序对 GMail-api 进行 oauth (2) 身份验证?【英文标题】:How do I oauth (2) authenticate to GMail-api from a Meteor.js app without manual browser authentication? 【发布时间】:2020-05-02 03:56:19 【问题描述】:

我想在没有手动浏览器身份验证的情况下登录我的流星服务器程序。所以认证必须全部自动化;我根本不想要用户干预/交互。 我遇到了here 的答案,但它在 java 中,我不知道它是否有效。有人可以在meteor.js中提供答案吗?如果答案在 node.js 中并且可以在流星中工作,那也很好。

最好我想在后端运行这个流星代码,作为使用 msavin /SteveJobs pacjage 的作业的一部分。

【问题讨论】:

请添加一些您已经实现的代码。如果您完全不知道从哪里开始,您可以查看帐户包的代码,这些包是 GitHub 上 Meteor 存储库的一部分。 @Jankapunkt,请注意我说的是登录和使用 gmail-api。一个例子是 github.com/Slava/meteor-gmail 。我不是在处理包含 accounts-google 包的帐户包。我想使用 gmail api,它提供了许多我无法从 accounts-google 包中获得的功能。如果我误解了你,请告诉我。感谢您的回复。 这是完全相同的 oauth 工作流程,但配置或多或少有所不同。从 oauth 的角度来看,Meteor 服务器也只是一个“客户端”。那么,你到底卡在哪里了?为什么不将您的凭据存储在 settings.json 中并在启动时使用来自 meteor-gmail 链接的 API 加载它们? 所以我有github.com/meteor/meteor/tree/devel/packages;我在上面的链接中看哪里?我上面的链接有错吗? 希望在 Gmail API 中使用哪些操作?你在读/写用户数据吗? 【参考方案1】:

下面是简单的node.js的工作sn-p

功能:

list labels send message get email

您可以使用Gmail API 调整它以满足您的需求

如果您对这个示例感到困惑,请尝试Node.js Quickstart Guide

这里是sn-p:

const fs = require('fs');
const readline = require('readline');
const  google  = require('googleapis');

// If modifying these scopes, delete token.json.
const SCOPES = [
  'https://mail.google.com/',
  'https://www.googleapis.com/auth/gmail.modify',
  'https://www.googleapis.com/auth/gmail.compose',
  'https://www.googleapis.com/auth/gmail.send'
];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = 'token.json';

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 * @param Object credentials The authorization client credentials.
 * @param function callback The callback to call with the authorized client.
 */
function authorize(credentials, callback) 
  const  client_secret, client_id, redirect_uris  = credentials.installed;
  const oAuth2Client = new google.auth.OAuth2(
    client_id, client_secret, redirect_uris[0]);

  // Check if we have previously stored a token.
  fs.readFile(TOKEN_PATH, (err, token) => 
    if (err) return getNewToken(oAuth2Client, callback);
    oAuth2Client.setCredentials(JSON.parse(token));
    callback(oAuth2Client);
  );


/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 * @param google.auth.OAuth2 oAuth2Client The OAuth2 client to get token for.
 * @param getEventsCallback callback The callback for the authorized client.
 */
function getNewToken(oAuth2Client, callback) 
  const authUrl = oAuth2Client.generateAuthUrl(
    access_type: 'offline',
    scope: SCOPES,
  );
  console.log('Authorize this app by visiting this url:', authUrl);
  const rl = readline.createInterface(
    input: process.stdin,
    output: process.stdout,
  );
  rl.question('Enter the code from that page here: ', (code) => 
    rl.close();
    oAuth2Client.getToken(code, (err, token) => 
      if (err) return console.error('Error retrieving access token', err);
      oAuth2Client.setCredentials(token);
      // Store the token to disk for later program executions
      fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => 
        if (err) return console.error(err);
        console.log('Token stored to', TOKEN_PATH);
      );
      callback(oAuth2Client);
    );
  );


/********************************************************************************************
*********************************************************************************************
*********************************************************************************************
 * Custom edits here
 */

let labelList = [];

function listLabels(auth) 

  const gmail = google.gmail( version: 'v1', auth );

  gmail.users.labels.list(
    userId: 'me',
  , (err, res) => 

    if (err) return console.log('The API returned an error: ' + err);

    const labels = res.data.labels;

    if (labels.length) 
      labels.forEach((label) => 
        labelList.push(`$label.name`);
      );
     else 
      console.log('No labels found.');
    

  );

  console.log('===listLabels finished===');



function makeEmailBody(to, from, subject, message) 

  let str = [
    "Content-Type: text/plain; charset=\"UTF-8\"\n",
    "MIME-Version: 1.0\n",
    "Content-Transfer-Encoding: 7bit\n",
    "to: ", to, "\n",
    "from: ", from, "\n",
    "subject: ", subject, "\n\n",
    message
  ].join('');

  let encodedMail = new Buffer(str).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');

  return encodedMail;



function sendMessage(auth) 

  let raw = makeEmailBody(
    /* to */'email@gmail.com',
    /* from */'email@gmail.com',
    /* subject */'Subject',
    /* message */`Labels:
      $labelList
    `);

  const gmail = google.gmail( version: 'v1', auth );

  gmail.users.messages.send(
    auth: auth,
    userId: 'me',
    resource: 
      raw: raw
    
  , (err, res) => 
    return (err || res)
  );

  console.log('===SendMessage finished===');



let userId = 'email@gmail.com';
let messageId = '__id__'; 

function getEmail(auth)

  const gmail = google.gmail( version: 'v1', auth );

  gmail.users.messages.get(
    id: messageId,
    userId: userId,
    format: 'full'
  , (err, res) => 
    if (err) return console.log('The API returned an error: ' + err);
    console.log(res.data);
  );

  console.log('===listLabels finished===');



// RUN script
fs.readFile('credentials.json', (err, content) => 

  if (err) 
    console.log('Error loading client secret file: ' + err);
    return;
  

  authorize(JSON.parse(content), getEmail);

  // authorize(JSON.parse(content), listLabels);
  // authorize(JSON.parse(content), listFiles);
  // authorize(JSON.parse(content), sendMessage);

);

如何创建服务帐号:

    在Admin Console中创建项目 创建service account 转到管理控制台 > 安全 > 高级设置 > Manage API client access 在客户名称中输入您创建的服务帐户的完整电子邮件 在一个或多个 API 范围中输入 https://mail.google.com/ 并点击授权 返回Service accounts,选择您的帐户,启用 G Suite 域范围委派 创建服务帐户密钥(下载为 .json) 为您的项目激活 Gmail API。转到 API 和服务 > Dashboard,点击启用 API 和服务,搜索 Gmail 并启用它。

现在您获得了服务帐号,您可以在项目中使用它。

【讨论】:

在他们授权时您似乎有用户干预。我不要那个。有人告诉我我正在寻找的是使用服务帐户。我还没有这样做。如果你有例子欢迎提供 @yushauzumo 如果您需要的是服务帐户 sn-p 它的另一个故事,请查看我的 answer here 让我知道这是否与您想要实现的目标相似但使用 node.js .

以上是关于如何在没有手动浏览器身份验证的情况下从 Meteor.js 应用程序对 GMail-api 进行 oauth (2) 身份验证?的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有客户端身份验证的情况下从服务器验证 Firebase 用户?

如何在未经身份验证的情况下从 Google Play 商店请求应用程序信息

为 Web API 实施 Azure AD 身份验证,可以在有和没有用户上下文的情况下从多个客户端调用?

是否可以在没有 OAuth2 身份验证对话框的情况下从 Google App-Engine (python) 连接和查询 BigQuery 表?

如何在 identityserver4 中没有用户名和密码的情况下从令牌端点获取令牌?

如何在没有 x5c 的情况下从 jwks 验证 JWT 的签名