使用Node JS身份验证Google API

14

到目前为止,我已经完成的是应用程序重定向到同意页面。用户接受后,我会带着有效的授权码重定向回本地主机。据我所知,我需要进行另一个调用并交换此代码以获取访问令牌。getAccessToken() 不起作用。 然而,控制台记录返回了以下内容:

invalid_client
invalid_request

请让我知道需要哪些附加信息。

以下是相关代码:

var { google } = require('googleapis');
var http = require("http");
var request = require('request');

var oauth2Client = new google.auth.OAuth2(
    '<My Client ID>',
    '<My Client Secret>',
    'http://localhost:8080'
);

exports.generateAuthCodeUrl = function () {

    const url = oauth2Client.generateAuthUrl({
        access_type: 'offline',
        scope: 'https://www.googleapis.com/auth/blogger'
    });

    return url;
};


exports.getAccessToken = function (accessCode) {
    var codeOptions = {
        code: accessCode
    }
    oauth2Client.getToken(codeOptions, function (err, tokens) {
        // Now tokens contains an access_token and an optional refresh_token. Save them.
        if (!err) {
            oauth2Client.setCredentials(tokens);
            return tokens;
        }
        console.log(err.message);
    });
};

编辑:摘要和适合我的方法

我两次阅读了pinoyyid回答中的链接文章,并注意了他的回答中列出的步骤。列出简单的步骤帮助我更清晰地理解。另外,如评论所建议的那样,我移除了googleapi库上述错误是在该库的代码内发生的),并仅使用request库对必要的端点进行常规调用。我使用request,因为它更简洁。我最终得到的代码看起来像这样:

exports.generateAuthCodeUrl = function () {

    var authURL = "https://accounts.google.com/o/oauth2/v2/auth?" +
        "client_id=" + client_id +
        "&scope=" + scope +
        "&redirect_uri=" + redirect_uri +
        "&response_type=" + response_type;

    //redirect to consent page
    return authURL;  
};

exports.getAccessToken = function (x) {
    var postDataUrl = 'https://www.googleapis.com/oauth2/v4/token?' +
        'code=' + x +  //auth code received from the previous call
        '&client_id=' + client_id +
        '&client_secret=' + client_secret +
        '&redirect_uri=' + redirect_uri +
        '&grant_type=' + "authorization_code"

    var options = {
        uri: postDataUrl,
        method: 'POST'
    };

    request(options, function (err, res, body) {
        return body; //returns an object with an access token!!!
    });
};

非常高兴我成功了!非常感谢大家


如果你会使用passport.js,请告诉我,我可以提供帮助。 - programoholic
@programoholic 目前正在寻找一个纯的 Node 解决方案。但如果我们选择使用 Express 和 Passport,可能会在将来进行更新。 - KidBilly
1
我的建议是,放弃使用库,直接调用端点。只需一次重定向获取授权代码,然后进行简单的REST调用以将其交换为访问/刷新令牌。如果您遇到任何问题,可以将您的HTTP请求与Google OAuth playground中的请求进行比较,以查看您出了什么问题。 - pinoyyid
@pinoyyid 那听起来是个好主意。我会尝试不使用库。 - KidBilly
如果有帮助的话,我已经发布了OAuth白痴指南作为回答,以列出步骤。 - pinoyyid
2个回答

15

3步轻松学会谷歌三腿OAuth认证

你所需要的一切都在这个页面上:https://developers.google.com/identity/protocols/OAuth2WebServer。仔细阅读两遍,您就可以成为OAuth大师了。简而言之,它说...

  1. 通过4个查询参数构建accounts.google.com URL:
    1. client_id 用于标识您的应用程序
    2. scope 说明您要请求哪些权限
    3. redirect_uri 告诉Google将用户的浏览器重定向到何处以获取结果
    4. response_type=code 表示您要一个授权代码
  2. 将用户的浏览器重定向到该URL
  3. 喝口咖啡,让用户登录,选择他的Google帐户并授权,直到最终...
  4. 用户的浏览器重定向回您的应用程序redirect_uri,其中包含一个一次性的code授权代码作为查询参数
  5. 将授权码提交到Google的令牌端点
  6. 解析JSON响应以获得访问令牌
  7. 在后续的谷歌API请求中使用访问令牌作为“authorization: bearer access_token”HTTP标头
如果您访问https://developers.google.com/oauthplayground/,您可以在线执行步骤,查看各种网址和响应的情况。

接受这个作为正确答案,因为它对我帮助最大。谢谢@pinoyyid! - KidBilly
根据你的回答更新了我的帖子。再次感谢,这很有帮助。 - KidBilly

3

我写了这个库来获取用户信息,希望能对你有所帮助。

'use strict'

const { google } = require('googleapis')
const credentials = require('../configs/config').google

class googleApi {
    constructor(){
        const {client_id, client_secret, redirectUri } = credentials;
        this.oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirectUri)
    }

    generateUrl(scopes){
        const url = this.oAuth2Client.generateAuthUrl({
            access_type: 'offline',
            scope: scopes.join(' ')
        })
        return url;
    }

    async getUserInfo(code){
        const credentials = await this.oAuth2Client.getToken(code)
        this.oAuth2Client.setCredentials(credentials.tokens);
        const plus = google.plus({
            version: 'v1',
            auth: this.oAuth2Client,
        });
        const data = await plus.people.get({userId: 'me'});
        return data;
    }
}

module.exports = new googleApi();

这是实现代码:

'use strict'
const googleApi = require('../libs/google');

exports.requestGmailAuth = function (req, res, next){
    let url = googleApi.generateUrl(scopes)
    res.redirect(url);
}

exports.getGmailUserInfo = async function (req, res, next){
    const qs = new url.URL(req.url, 'http://localhost:3000').searchParams;
    let code = qs.get('code')
    if(!code){
        next(new Error('No code provided'))
    }
    googleApi.getUserInfo(code)
        .then(function(response){
            res.send(response.data)
        }).catch(function(e){
            next(new Error(e.message))
    })
}

这些是路由:

app.get('/request/gmail/auth', user.requestGmailAuth)
app.get('/get/gmail/user', user.getGmailUserInfo)

当 /request/gmail/auth 收到请求后,它会重定向到授权页面,然后授权页面会使用 "code" 参数重定向到 /get/gmail/user。

请尝试此代码片段,如果问题仍然存在,请检查您的客户端 ID 和客户端密钥,并确保您在开发人员仪表板中启用了 Google Plus API。


我认为这是一个干净的实现。我以前使用过非常类似的东西。但是我遇到了一个问题。如何注销用户?因为在浏览器中,特别是Chrome,一旦用户登录Google,ID将持久存在,因此这将自动登录该用户并跳过选择其他ID的选项。 - sakib11

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接