Google People API:请求的身份验证凭据无效 - 未设置授权承载标头

问题描述

我正在尝试为我正在开发的聊天应用程序实现这个 Google People api。人们 API 文档只有这个例子 - https://github.com/googleworkspace/node-samples/blob/master/people/quickstart/index.js

我进行了一些更改以将其与我的项目集成。

// imports

const app = express();
app.use(cookieParser());

const ScopES = ['https://www.googleapis.com/auth/contacts.readonly'];
const people = google.people('v1');

let credentials,oauth2client;
fs.readFile('./client_secret.json',async (err,content) => {
    if (err) return console.error(err);
    credentials = JSON.parse(content);

    const { client_secret,client_id,redirect_uris } = credentials.web;
    oauth2client = new google.auth.OAuth2(
        client_id,client_secret,redirect_uris[1]);
});


app.get("/auth/google",(req,res) => {
    console.log(req.cookies);
    res.cookie("sample_cookie","sample_value");

    const authUrl = oauth2client.generateAuthUrl({
        access_type: 'offline',scope: ScopES,});

    console.log('Authorize this app by visiting this url:',authUrl);
    res.redirect(authUrl);
});

app.get("/contacts",resp) => {
    if (req.cookies.access_token && req.cookies.refresh_token) {

        const token = {
            access_token: req.cookies.access_token,refresh_token: req.cookies.refresh_token,scope: req.cookies.scope,token_type: "Bearer",expiry_date: req.cookies.expiry_date,}

        oauth2client.setCredentials(token);

        const service = google.people({ version: 'v1',oauth2client });
        service.people.connections.list({
            resourceName: 'people/me',pageSize: 10,personFields: 'names,emailAddresses,phoneNumbers',},(err,res) => {
            if (err) return resp.send(err);
            const connections = res.data.connections;
            
            if (connections) {
                connections.forEach((person) => {
                    if (person.names && person.names.length > 0) {
                        resp.write(person.names);
                    } else {
                        resp.write('No display name found for connection.');
                    }
                });
            } else {
                resp.write('No connections found.');
            }
            resp.end();
        });
    } else {
        res.send("Something's wrong yet.")
    }
})

app.get(["/auth/google/callback","authorized"],async (req,res) => {
    const code = req.query.code;

    oauth2client.getToken(code,token) => {
        if (err) return console.error('Error retrieving access token',err);
        oauth2client.setCredentials(token);

        res.cookie("access_token",token.access_token);
        res.cookie("refresh_token",token.refresh_token);
        res.cookie("scope",token.scope);
        res.cookie("token_type",token.token_type);
        res.cookie("expiry_date",token.expiry_date);

        res.send("Done.")
    })
})

app.listen(3000,() => {
    console.log("running");
})

但我收到 401:未经授权。我对前一个 (Google) 示例所做的所有更改只是将详细信息保存为 cookie,而不是将详细信息保存为 cookie,并添加了从浏览器访问它的路由。 Google 提供的示例按预期工作。我所做的更改在授权点之前也有效,但在尝试访问联系人路由时,它返回以下响应。

这是我得到的答复(仅包括我认为必要的详细信息):

{
    "response": {
        "config": {
            "oauth2client": {
                "credentials": {
                    "access_token": "my_access_token","refresh_token": "my_refresh_token","scope": "https://www.googleapis.com/auth/contacts.readonly","token_type": "Bearer","expiry_date": 1609256514576
                },"redirectUri": "http://localhost:3000/auth/google/callback","url": "https://people.googleapis.com/v1/people/me/connections?pageSize=10&personFields=names%2CemailAddresses%2CphoneNumbers","method": "GET","headers": {
                "Accept-Encoding": "gzip","User-Agent": "google-api-nodejs-client/0.7.2 (gzip)","Accept": "application/json"
            },"data": {
            "error": {
                "code": 401,"message": "Request is missing required authentication credential. Expected OAuth 2 access token,login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.","errors": [{
                    "message": "Login required.","domain": "global","reason": "required","location": "Authorization","locationType": "header"
                }],"status": "UNAUTHENTICATED"
            }
        },...

我试图调试代码。我什么都抓不住。但我注意到的一件事是,在上面的响应中,我没有设置 Authorization 标头。在来自 google docs 示例的成功 API 请求中,我收到

{
    "config": {
        "url": "https://people.googleapis.com/v1/people/me/connections?pageSize=10&personFields=names%2CemailAddresses%2CphoneNumbers","headers": {
            "Accept-Encoding": "gzip","Authorization": "Bearer access-code","Accept": "application/json"
        },"data": {
        "connections": [{...

我不明白为什么我的代码没有设置 Authorization 标头,而且 OAuthClient 和凭据字段也没有出现在这个成功的响应中。如果不是人 api 我在联系人路由中尝试类似下面的内容或在邮递员中使用不记名令牌发出 GET 请求,我会得到正确的响应。

let bearer = `Bearer ${req.cookies.access_token}`;
request({
    url: 'https://people.googleapis.com/v1/people/me/connections?pageSize=10&personFields=names%2CemailAddresses%2CphoneNumbers',headers: {
        'Authorization': bearer
    }},res) => {
        if (err) {
            console.error(err);
         } else {
            resp.send(res);
         }
    }
);

我正确地收到了回复。但我不想这样做。我不知道我的代码有什么问题,或者是否有人可以提供任何其他工作示例...我也尝试使用过 Passport.js,但遇到了相同的 401 未授权错误

// passport-setup.js
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;

passport.serializeUser(function (user,done) {
    done(null,user);
});

passport.deserializeUser(function (user,user);
});

passport.use(new GoogleStrategy({
    clientID: "client-id",clientSecret: "client-secret",callbackURL: "http://localhost:3000/auth/google/callback",passReqToCallback: true
},function (req,accesstoken,refreshToken,otherTokenDetails,profile,done) {
        req.session.accesstoken = accesstoken;
        req.session.refreshToken = refreshToken;
        req.session.scope = otherTokenDetails.scope;
        req.session.token_type = otherTokenDetails.token_type;
        req.session.expiry_date = new Date().getTime() + otherTokenDetails.expires_in;

        return done(null,profile);
    }
));

index.js
// importing express,cors,bodyParser,passport,cookieSession,passport setup and googleapis

const app = express();
const people = google.people('v1');

// app.use(cors,passport init and session)

const isLoggedIn = (req,res,next) => {
    if (req.user) {
        next();
    }
    else {
        res.sendStatus(401);
    }
}

app.get("/success",isLoggedIn,resp) => {
    const oauth2client = new google.auth.OAuth2(id,secret and url...)

    const token = {
        access_token: req.session.accesstoken,refresh_token: req.session.refreshToken,scope: req.session.scope,token_type: req.session.token_type,expiry_date: req.session.expiry_date,}

    oauth2client.setCredentials(token);

    const service = google.people({ version: 'v1',oauth2client });
    service.people.connections.list({
        resourceName: 'people/me',res) => {
        if (err) return resp.send(err);
        const connections = res.data.connections;
        
        if (connections) {
            console.log('Connections:');
            connections.forEach((person) => {
                if (person.names && person.names.length > 0) {
                    resp.write(person.names);
                } else {
                    resp.write('No display name found for connection.');
                }
            });
        } else {
            resp.write("No connections.");
        }
        res.end();
    });
})

app.get('/auth/google',passport.authenticate('google',{
        scope: ['profile','email','https://www.googleapis.com/auth/contacts'],accesstype: 'offline',prompt: 'consent',}));

app.get('/auth/google/callback',{ failureRedirect: '/login' }),res) {
        res.redirect('/success');
    });

app.listen(3000,() => console.log("Server is up and running on port 3000."));

我检查了几乎所有类似的 Stack Overflow 答案和 GitHub 问题。似乎什么都解决不了。

解决方法

传递给 google.people 的第二个参数的名称是 auth。 在 JavaScript 中,您可以将 {auth: auth} 写成简单的 {auth}。在google提供的例子中,变量名与字段名相同,所以直接提供为auth

const service = google.people({ version: 'v1',auth });

但是您的变量名称与字段名称不同。所以更改名称或只是用

替换这一行
const service = google.people({ version: 'v1',auth: oAuth2Client });

它期望 auth 作为第二个属性,但它收到一个具有名称的属性 oAuth2Client 这就是它不起作用的原因。