From 757d391b66ea1f8a8b074dc0d4b36ee889419953 Mon Sep 17 00:00:00 2001 From: Wouter van der Plas <2423856+wvanderp@users.noreply.github.com> Date: Tue, 26 Dec 2023 10:29:47 +0100 Subject: [PATCH] trying some stuff around origin --- src/utils/api/token.ts | 55 +++++++++++++++----- tests/browser/cypress/e2e/buildAndLoad.cy.js | 2 +- tests/browser/cypress/e2e/login.cy.js | 2 +- tests/browser/cypress/e2e/login.ts | 7 ++- 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/utils/api/token.ts b/src/utils/api/token.ts index d26b264..f917721 100644 --- a/src/utils/api/token.ts +++ b/src/utils/api/token.ts @@ -3,21 +3,29 @@ import qs from 'qs'; import { ActionLoginResponse, QueryMetaTokenResponse } from '../../types/apiResponse'; /** - * generates the login url for a given server + * Generates the login url for a given server * * Main-account login via \"action=login\" is deprecated and may stop working without warning. * https://phabricator.wikimedia.org/T137805 * + * According to the api documentation you need to specify a specific origin when relying on cors + * https://en.wikipedia.org/wiki/Special:ApiSandbox#action=query&list=search&srsearch=meaning + * * @private - * @param {string} server the server to generate the url for - * @returns {string} the login url + * @param {string} server The server to generate the url for + * @returns {string} The login url + * @param {string} [origin] The origin to use for the api calls aka the "domain" of the webapp (only needed for cors) * @example * const loginUrl = getLoginUrl('https://test.wikidata.org'); * // loginUrl = 'https://test.wikidata.org/w/api.php?action=login&format=json' */ -export function loginUrl(server: string): string { - const url = new URL(server); - return `${url.origin}/w/api.php?action=login&format=json`; +export function loginUrl(server: string, origin?: string): string { + const serverURL = new URL(server); + const url = `${serverURL.origin}/w/api.php?action=login&format=json`; + if (origin) { + return `${url}&origin=${origin}`; + } + return url; } /** @@ -26,13 +34,18 @@ export function loginUrl(server: string): string { * @private * @param {string} server the server to generate the url for * @returns {string} the token url + * @param {string} [origin] The origin to use for the api calls aka the "domain" of the webapp (only needed for cors) * @example * const tokenUrl = getTokenUrl('https://test.wikidata.org'); * // tokenUrl = 'https://test.wikidata.org/w/api.php?action=query&meta=tokens&type=csrf&format=json' */ -export function tokenUrl(server: string): string { - const url = new URL(server); - return `${url.origin}/w/api.php?action=query&meta=tokens&type=csrf&format=json`; +export function tokenUrl(server: string, origin?: string): string { + const serverURL = new URL(server); + const url = `${serverURL.origin}/w/api.php?action=query&meta=tokens&type=csrf&format=json`; + if (origin) { + return `${url}&origin=${origin}`; + } + return url; } /** @@ -67,12 +80,19 @@ interface LoginResponse { } } +interface GetTokenConfig { + server?: string; // The wikibase server to get the token from + origin?: string; // The origin to use for the api calls aka the "domain" of the webapp (only needed for cors) +} + /** * use this function to retrieve tokens by using a username and password. * * @param {string} username The username of the user * @param {string} password The password of the user - * @param {string} [server] The server to get the token from + * @param {object} [config] The config for the function + * @param {string} [config.server] The server to get the token from + * @param {string} [config.origin] The origin to use for the api calls aka the "domain" of the webapp (only needed for cors) * @throws {Error} If the login was not successful * @returns {Token} A object containing the token and the cookie * @example @@ -82,7 +102,16 @@ interface LoginResponse { * authToken: token * }); */ -export default async function getToken(username: string, password: string, server = 'https://www.wikidata.org'): Promise { +export default async function getToken(username: string, password: string, config?: GetTokenConfig): Promise { + // checking the config + const server = config?.server ?? 'https://www.wikidata.org'; + + // @ts-expect-error - we are checking if it exist and not using it if it doesn't + if (typeof window !== 'undefined' && config?.origin === undefined) { + // eslint-disable-next-line no-console + console.warn('getToken: You are using this function in a browser environment without specifying the origin. This may lead to problems with cors.'); + } + // checking the username and password if (username === undefined || password === undefined || username === '' || password === '' || username === null || password === null) { throw new Error('username and password are required'); @@ -97,7 +126,7 @@ export default async function getToken(username: string, password: string, serve // Getting the login cookie & token const body = qs.stringify({ username, password }); - const cookieResult = await axios.post(loginUrl(server), body); + const cookieResult = await axios.post(loginUrl(server, config?.origin), body); const { token: loginToken } = cookieResult.data.login; const cookies = cookieResult.headers['set-cookie']; @@ -113,7 +142,7 @@ export default async function getToken(username: string, password: string, serve const loginBody = qs.stringify({ lgname: username, lgpassword: password, lgtoken: loginToken }); const loginResult = await axios.post( - loginUrl(server), + loginUrl(server, config?.origin), loginBody, { headers: loginHeaders } ); diff --git a/tests/browser/cypress/e2e/buildAndLoad.cy.js b/tests/browser/cypress/e2e/buildAndLoad.cy.js index 9081f8f..d5a48e1 100644 --- a/tests/browser/cypress/e2e/buildAndLoad.cy.js +++ b/tests/browser/cypress/e2e/buildAndLoad.cy.js @@ -1,6 +1,6 @@ describe('template spec', () => { it('passes', () => { - cy.visit('http://localhost:1234/buildAndLoad.html'); + cy.visit('http://localho.st:1234/buildAndLoad.html'); cy.get('body').should('contain', 'Douglas Adams'); }); }); diff --git a/tests/browser/cypress/e2e/login.cy.js b/tests/browser/cypress/e2e/login.cy.js index 459cd4e..aecd0c1 100644 --- a/tests/browser/cypress/e2e/login.cy.js +++ b/tests/browser/cypress/e2e/login.cy.js @@ -1,6 +1,6 @@ describe('template spec', () => { it('passes', () => { - cy.visit('http://localhost:1234/login.html'); + cy.visit('http://localho.st:1234/login.html'); // the following code is injected into the page // and executed in the browser diff --git a/tests/browser/cypress/e2e/login.ts b/tests/browser/cypress/e2e/login.ts index 415cbfd..bc5cfe7 100644 --- a/tests/browser/cypress/e2e/login.ts +++ b/tests/browser/cypress/e2e/login.ts @@ -2,7 +2,7 @@ /* eslint-disable import/no-useless-path-segments */ import { getToken -} from '../../../../'; +} from '../../../../src/'; declare global { interface Window { @@ -11,7 +11,10 @@ declare global { } async function login(username: string, password: string): Promise { - const token = await getToken(username, password); + const origin = window.location.origin; + const token = await getToken(username, password, { + origin + }); if (token) { document.body.innerHTML = `success: ${token}`;