Пакет узла oauth-1.0a, возвращающий 401 для API Zotero (NodeJS) - PullRequest
0 голосов
/ 20 декабря 2018

Я использую пакет узла oauth-1.0a для реализации OAuth с помощью API Zotero.

Я использую код, найденный в репозитории zotero-oauth-example .

Запуск кода в репозитории, упомянутом выше, работает.Я думаю, что моя реализация не работает, потому что я разделил одну функцию в репо на две функции -> пример репо не обрабатывает перенаправление клиента и обработку обратного вызова.

ЧтоЯ думаю, что проблема заключается в следующем: Я думаю, что создание двух разных объектов OAuth (один шаг 1, другой шаг 3) что-то ломает.Обратите внимание, что я временно сохраняю хеш, созданный на шаге 1 hash_function, который используется на шаге 3.

Я не знаком с OAuth.Есть ли у вас какие-либо предложения по поводу того, что мне следует попробовать?

1) Запрос токена (сервер)

// omitting includes packages
const tokenRequestConfig = {
        url: 'https://www.zotero.org/oauth/request',
        method: 'POST',
        data: {
           

 oauth_callback: baseURL, // redirect to baseURL
            },
        },

        initZoteroIntegration = async () => {
            let oAuthHash;

            const oauth = OAuth({
                    consumer: {
                        key: process.env.ZOTERO_APP_CLIENT_KEY,
                        secret: process.env.ZOTERO_APP_CLIENT_SECRET,
                    },
                    signature_method: 'HMAC-SHA1',
                    hash_function(base_string, key) {
                        oAuthHash = crypto.createHmac('sha1', key).update(base_string).digest('base64');
                        return oAuthHash;
                    },
                }),

                tokenRequestResponse = await fetch('https://www.zotero.org/oauth/request', {
                    headers: oauth.toHeader(oauth.authorize(tokenRequestConfig)),
                    method: 'post',
                }),

                tokenRequestData = await tokenRequestResponse.text(),
                obj = {};

            tokenRequestData.replace(/([^=&]+)=([^&]*)/g, (m, key, value) => {
                obj[decodeURIComponent(key)] = decodeURIComponent(value);
            });

            const oAuthToken = obj.oauth_token,
                oAuthTokenSecret = obj.oauth_token_secret,
                url = `https://www.zotero.org/oauth/authorize?oauth_token=${oAuthToken}&library_access=1&notes_access=1&write_access=1&all_groups=write`;

            /* the url should be returned to the client */
            return ({
                url,
                oAuthToken,
                oAuthTokenSecret,
                oAuthHash,
            });
        };

2) Клиент направляется в приложение Zotero для входа в систему и авторизации разрешений.

3) Token Exchange (сервер)

// omitting includes packages
const confirmIntegration = async ({oAuthToken, oAuthTokenSecret, oAuthVerifier, oAuthHash}) => {
            const oauth = OAuth({
                    consumer: {
                        key: process.env.ZOTERO_APP_CLIENT_KEY,
                        secret: process.env.ZOTERO_APP_CLIENT_SECRET,
                    },
                    signature_method: 'HMAC-SHA1',
                    hash_function() {
                        return oAuthHash;
                    },
                }),

                tokenExchangeConfig = {
                    url: `https://www.zotero.org/oauth/access?oauth_token=${oAuthToken}`,
                    method: 'POST',
                    data: {
                        oauth_verifier: oAuthVerifier,
                        oauth_callback: baseURL,
                    },
                },

                tokenExchangeResponse = await fetch(`https://www.zotero.org/oauth/access?oauth_token=${oAuthToken}`, {
                    headers: oauth.toHeader(oauth.authorize(tokenExchangeConfig, {
                        public: oAuthToken,
                        secret: oAuthTokenSecret,
                    })),
                    method: 'post',
                }),

                tokenExchangeData = await tokenExchangeResponse.text();

            try {
                const username = tokenExchangeData.match(/username=(\w+)/)[1],
                    userID = tokenExchangeData.match(/userID=([0-9]+)/)[1],
                    userAPIKey = tokenExchangeData.match(/oauth_token_secret=([a-zA-Z0-9]+)/)[1];


            return {
                    username,
                    userID,
                    userAPIKey,
                };
            } catch (e) {
                // TODO throw some error
                return null;
            }
        };

На шаге «Обмен токенами» я получаю ответ с кодом состояния 401 («Не авторизован»).

tokenExchangeResponse.text() возвращаетсяoauth_problem=signature_invalid

Вот необработанный вывод:

"https_sig_error=1&z_debug_sbs=POST&https%3A%2F%2Fwww.zotero.org%2Foauth%2Faccess&oauth_consumer_key%3D9a016199db19772cb220%26oauth_nonce%3DDs3iXBVWF4izl1qfX0mk0JXIvZkl7N5o%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1545238813%26oauth_token%3D6f6ade01f30625feeb36%26oauth_verifier%3D02469ed77305b02befd8%26oauth_version%3D1.0&oauth_problem=signature_invalid&debug_sbs=POST&https%3A%2F%2Fwww.zotero.org%2Foauth%2Faccess&oauth_consumer_key%3D9a016199db19772cb220%26oauth_nonce%3DDs3iXBVWF4izl1qfX0mk0JXIvZkl7N5o%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1545238813%26oauth_token%3D6f6ade01f30625feeb36%26oauth_verifier%3D02469ed77305b02befd8%26oauth_version%3D1.0"

* Исправление Хорошо, я нашел решение.Я использую node-cache для кэширования объекта OAuth в течение ограниченного времени.

Спасибо @tnajdek за разъяснение, что вышеупомянутое использование OAuth было проблемой.

1 Ответ

0 голосов
/ 20 декабря 2018

Обратите внимание, что я временно сохраняю хеш, созданный в хэш-функции шага 1, который используется в шаге 3.

Это не будет работать.

hash_function аргумент OAuth конструктор принимает base_string (который является сериализованной сводкой вашего запроса) и key для получения его результата.И base_string, и key будут отличаться между шагами 1 и 3, однако ваша функция hash_function возвращает кэшированные результаты с шага 1 в результате для совершенно разных аргументов base_string и key на шаге 3.

Я не уверен, как выглядит остальная часть вашего приложения, но я бы создал экземпляр OAuth только один раз и повторно использовал бы его в начальном запросе и в запросе обратного вызова внутри подпрограмм обработки запросов вашего сервера.Если это не вариант, вы можете воссоздать OAuth, используя те же аргументы, и он должен работать нормально.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...