Как аутентифицировать API Календаря Google из Dialogflow Fulfillment с помощью клиента OAuth2? - PullRequest
0 голосов
/ 09 июля 2020

Я пытаюсь создать чат-бот помощника Google Calendar с выполнением Dialogflow, размещенным в GCP, с использованием клиентских библиотек node.js, dialogflow-fillment и googleapis. У меня проблема с созданием метода аутентификации с использованием идентификатора клиента OAuth. Идея состоит в том, что когда пользователь добавляет бота в Google Chat, бот должен приветствовать его / ее и запрашивать у пользователя разрешение на определенные области (в данном случае для создания событий в Google Calendar). Что мне сейчас удалось сделать, так это отправить пользователю ссылку, по которой он увидит области, утвердит их, и будет сгенерирован код, но затем этот код должен быть передан обратно в функцию, чтобы получить токен и установить учетные данные.

ссылка отправлена ​​пользователю

сгенерированный код

код, переданный пользователю

Есть ли способ автоматически получить этот код и аутентифицировать пользователя?

Мой код выглядит так (он немного запутан из-за всех проведенных мною тестов):

const {google} = require('googleapis');
const {WebhookClient} = require('dialogflow-fulfillment');
const credentials = {"installed":{"client_id":"618408396856-vrd3it4s4nk19tlo7qrnbb51a9f8bq6t.apps.googleusercontent.com","project_id":"pg-xc-n-app-577847","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"d_qDDlFVBtllcotgn2xvc00N","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}};

//setting authentication details
const SCOPES = [
    'https://www.googleapis.com/auth/calendar.events',
    'https://www.googleapis.com/auth/spreadsheets'
    ];
const {client_secret, client_id, redirect_uris} = credentials.installed;
const authentication = new google.auth.OAuth2(
    client_id,
    client_secret,
    redirect_uris[0]
);
const url = authentication.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES
});

const calendarId = 'primary';
const calendar = google.calendar('v3');

process.env.DEBUG = 'dialogflow:*'; // enables lib debugging statements

exports.meetingRoomFulfillment = function meetingRoomFulfillment(req, res) {
    const agent = new WebhookClient({ request: req, response: res });
    console.log(`Intent ${((req.body.queryResult || {}).intent || {}).displayName}`);

    console.log(`Dialogflow Request body`, JSON.stringify(req.body));
    if (req.body.queryResult === undefined || req.body.queryResult.intent === undefined || req.body.queryResult.intent.displayName === undefined) {
        console.log(`Missing intent so cancelling fulfillment`);
        res.send({});
        return;
    }

    function authenticate(agent){
        agent.add(`To authenticate this app please visit the following url: ${url}`);
    }

    function authenticationCode(agent){
        const code = agent.parameters.authenticationCode;
        console.log('The code: ' + code);
        authentication.getToken(code, (err, token) => {
            if (err) return console.error('Error retrieving access token', err);
            authentication.setCredentials(token);
            retrievedToken = token;
            console.log(retrievedToken);
    });
        agent.add('Successfully authenticated!');
    }

  function makeAppointment (agent) {
    const dateTimeStart = new Date(agent.parameters.date.split('T')[0] + 'T' + agent.parameters.time.split('T')[1]);
    const dateTimeEnd = new Date(new Date(dateTimeStart).setHours(dateTimeStart.getHours() + 1));
    const appointmentTimeString = dateTimeStart.toLocaleString();
    const eventDescription = agent.parameters.text;

    // Check the availibility of the time, and make an appointment if there is time on the calendar
    return createCalendarEvent(dateTimeStart, dateTimeEnd, eventDescription).then(() => {
      agent.add(`Ok, let me see if we can fit you in. ${appointmentTimeString} is fine!. I am creating an event called: ${eventDescription}`);
    }).catch(() => {
      agent.add(`I'm sorry, there are no slots available for this period.`);
    });
  }
  let intentMap = new Map();
  intentMap.set('authenticate', authenticate);
  intentMap.set('authentication code', authenticationCode);
  intentMap.set('Make Appointment', makeAppointment);
  agent.handleRequest(intentMap);
}

function createCalendarEvent (dateTimeStart, dateTimeEnd, eventDescription) {
  return new Promise((resolve, reject) => {
      calendar.events.list({
        auth: authentication, 
        calendarId: calendarId,
        timeMin: dateTimeStart.toISOString(),
        timeMax: dateTimeEnd.toISOString()
        }, (err, calendarResponse) => {
        // Check if there is a event already in the calendar
        if (err || calendarResponse.data.items.length > 0) {
            reject(err || new Error('Requested time conflicts with another appointment'));
            console.log(err);
        } else {
            // Create event for the requested time period
            calendar.events.insert({ 
                auth: authentication,
                calendarId: calendarId,
                resource: {
                    summary: eventDescription,
                    start: {dateTime: dateTimeStart},
                    end: {dateTime: dateTimeEnd}
                    }
            }, (err, event) => {
                err ? reject(err) : resolve(event);
                console.log(err);
                }
            );
        }
    });
  });
}

1 Ответ

1 голос
/ 09 июля 2020

Вы на правильном пути, но если пользователь go напрямую перейдет по ссылке OAuth, это означает, что он получит код и, следовательно, должен будет отправить его вам.

Вместо этого вы можете отправьте их на страницу своего сайта, которая перенаправит их на ссылку OAuth. Включенный вами URI перенаправления должен снова выполнять перенаправление обратно на ваш сайт. Таким образом вы получите код и сможете обработать его на своем сервере. После того, как вы завершите танец OAuth, ваш веб-сайт сообщит им, что они авторизовали вас, и сможет продолжить чат.

...