0

I am using MediaWiki as my backend and I have it running on 'localhost/name'.

My login.js file:

import React, {useState} from 'react';
import axios from 'axios';

function Login()  {

    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');

    async function login() {
        console.log(username, password);

        let url = 'http://localhost/wikimedia/api.php';

        let params = {
            action: "query",
            meta: "tokens",
            format: "json",
            type: "login"
        };

        // fetch login
        const response = await axios.get(url + '?' + params + '&origin=*');

        console.log(response);

        let loginToken = response.data.query.tokens.logintoken
        let cookie = response.headers['set-cookie'].join(';');

        let body = {
            action: 'login',
            lgname: username,
            lgpassword: password,
            lgtoken: loginToken,
            format: 'json'
        }

        let bodyData = new URLSearchParams(body).toString();

        axios.post(url, bodyData, {
            headers: {
                Cookie: cookie,
            }
        }).then(response => {

            let cookie = response.headers['set-cookie'].join(';')
            console.log(response.data);
        })
    }

    return (
      <div>
        <h1>Sign In</h1>
        <input 
            type="text"
            onChange={(e) => setUsername(e.target.value)}
            placeholder="username"
        />
        <br/>
        <input
            type="password"
            onChange={(e) => setPassword(e.target.value)}
            placeholder="password"
        />
        <br/>
        <button onClick={login} type="submit">Login</button>
      </div>
    )
  };
  
  export default Login;

However, I'm getting the below response which is not what is expected according to the documentation:

{
    "data": "a-bunch-of-irrelevant-stuff",
    "status": 200,
    "statusText": "OK",
    "headers": {
        "cache-control": "private, must-revalidate, max-age=0",
        "content-language": "en",
        "content-type": "text/html; charset=utf-8",
        "expires": "Thu, 01 Jan 1970 00:00:00 GMT",
        "mediawiki-login-suppressed": "true"
    },
    "config": {
        "transitional": {
            "silentJSONParsing": true,
            "forcedJSONParsing": true,
            "clarifyTimeoutError": false
        },
        "transformRequest": [
            null
        ],
        "transformResponse": [
            null
        ],
        "timeout": 0,
        "xsrfCookieName": "XSRF-TOKEN",
        "xsrfHeaderName": "X-XSRF-TOKEN",
        "maxContentLength": -1,
        "maxBodyLength": -1,
        "env": {},
        "headers": {
            "Accept": "application/json, text/plain, */*"
        },
        "method": "get",
        "url": "http://localhost/wikimedia/api.php?[object Object]&origin=*"
    },
    "request": {}
}

So far, I'm logging to the console the response of the first fetch -> token, but I'm not getting the response as expected:

{
    "batchcomplete": "",
    "query": {
        "tokens": {
            "logintoken": "9ed1499d99c0c34c73faa07157b3b6075b427365+\\"
        }
    }
}

3 Answers3

2

Kind of a big question, almost as big as "build my app for me". Check out https://stackoverflow.com/help/how-to-ask

But from a high level you want to connect the input values to react state. The documentation link you've posted describes how to do that. Maybe just try console.logging the state values in response to input changes first to make sure you have the inputs wired up correctly.

Once you have the input values available on state, you'll probably want to call the getToken method using the username and password. The react documentation on events describes how to do things in response to events such like button clicks or form submissions. https://reactjs.org/docs/handling-events.html

My assumption is that after calling getToken, you'd then want to log in using the retrieved token. So call that method using the username, password, and token.

You'll probably need a basic understanding of javascript promises to understand how to order the calls, ie only call login AFTER getting a response from getToken

https://web.dev/promises/ https://javascript.info/promise-basics https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

So 3 main steps:

  1. Connect form to state
  2. When form is submitted, make call to getToken, passing username and password (which you have from step 1)
  3. After you get a response from getToken make a new call to login, passing username, password and token (which you get from step 2)

Is there a more specific aspect you need help with?

ceckenrode
  • 4,543
  • 7
  • 28
  • 48
  • I have uploaded my question with my trial, but now I'm stuck at a different spot. I'm not getting the response I was expecting. – pipocaDourada Oct 15 '22 at 20:25
1

You are getting a CORS error. See https://www.mediawiki.org/wiki/API:Cross-site_requests on how to make CORS requests to the MediaWiki API.

Tgr
  • 27,442
  • 12
  • 81
  • 118
  • I've been checking that page, but to make an authenticated request it says to "set this to the originating domain". My React app is running here "http://localhost:3000". Shouldn't it be `origin: 'http://localhost:3000'` ? I'm very lost! – pipocaDourada Oct 15 '22 at 21:24
  • MediaWiki needs a list of allowed domains (imagine if any random website you visit could send requests in your name to your wiki, that would be pretty bad). localhost is not a real domain so I'm not sure you can use it like that. Also some browsers just refuse CORS from localost: https://stackoverflow.com/questions/10883211/why-does-my-http-localhost-cors-origin-not-work – Tgr Oct 16 '22 at 09:23
  • Depending on what you are trying to achieve, a better option might be to install the [OAuth extension](https://www.mediawiki.org/wiki/Extension:OAuth) and have your script send an API key (OAuth 2 bearer token) with its requests instead of logging in. Or use a [bot password](https://www.mediawiki.org/wiki/Manual:Bot_passwords). Although you'll probably still need to deal with the CORS issue at some point. – Tgr Oct 16 '22 at 09:28
  • 1
    Wrt CORS, you can either use a real domain (possibly via some tool like [ngrok](https://ngrok.com/)) and add that to your wiki's [$wgCrossSiteAJAXdomains](https://www.mediawiki.org/wiki/Manual:$wgCrossSiteAJAXdomains) setting; or you can use OAuth and write a tiny backend app to proxy your requests (which your frontend would then make to localhost) to the wiki. – Tgr Oct 16 '22 at 09:33
  • Thank you! I was able to find a solution later, thankfully! As you said, I had to set up the origin parameter and also the cookie value. – pipocaDourada Oct 16 '22 at 11:55
0

I would just use states to handle transferring the username and password data. I've added working code below. The doSomethingWithCreds function can be used to transfer your data to some route and eventually tie a token to it.

I highly recommend you take the time to learn states and about some of the general hooks like useRef and useEffect. It will make developing and understanding some of the key topics in react much easier.

If you have any questions feel free to comment and i'll do my best to respond.

State Doc Reference

import {useEffect, useState} from 'react'
import './App.css';

function App() {

  const[pass,setPass] = useState()
  const[user,setUser] = useState()

  const handlePassChange = event =>{//Updates pass every time a new char is entered instead of on submit
    setPass(event.target.value)
  }

  const handleUserChange = event =>{
    setUser(event.target.value)
  }
  
  const doSomethingWithCreds = () =>{
    console.log('password: ' + pass)
    console.log('user' + user)
  }

  return (
    <div className="form">
      <h1>Login Page</h1>
      <form>
        <div className="input-container">
            <label>Username </label>
            <input onChange={handleUserChange} type="text" name="uname" required />
        </div>
        <div className="input-container">
            <label>Password </label>
            <input onChange={handlePassChange} type="password" name="pass" required />
        </div>
        <div className="button-container">
            <input onClick={doSomethingWithCreds} type="submit" value="Login" />
        </div>
      </form>
    </div>
  );
}

export default App;