React JS - How to authenticate credentials via a fetch statement

29,676

Don't put your authorization token in the body. Put it in the Headers. The first function is going to pass in username, password, and authentication type (ie grant_type=password). Then my second function is going to use that to authenticate the request. There is no longer a need to pass any user information, because my api knows who is requesting based on the token that is passed in. The current documentation for OAuth 2.0 is here, and you can find more information about using headers with fetch at Mozilla's fetch documentation.

// request the token
// subscribe to this event and use the returned json to save your token to state or session storage
export function requestAccessToken(data) {
  const loginInfo = `${data}&grant_type=password`;
  return fetch(`${API_URL}Token`, {
    method: 'POST',
    headers: new Headers({
      'Content-Type': 'application/x-www-form-urlencoded',
    }),
    body: loginInfo,
  })
    .then((response) => response.json());

    // in your case set state to returned token
}

// use said token to authenticate request
export function requestUserInfo(token) {
  return fetch(`${API_URL}api/participant/userinfo`, {
    method: 'GET',
    headers: new Headers({
      Authorization: `Bearer ${token}`,
    }),
  })
    .then((response) => response.json());
}

I would also recommend:

  1. Calling fetch from a thunk or a saga, but this is out of scope of the question.

  2. No need to put your token in a hidden field. Which is still accessible btw. Just keep it in state. There are other things you can do to secure it a little, but this too, is out of scope of the question.

Share:
29,676
user1724708
Author by

user1724708

Updated on May 11, 2020

Comments

  • user1724708
    user1724708 almost 4 years

    My goal is to create a React JS login page that runs off a json Rest service. In Postman, when I enter the URL for the service, set it to run as POST and enter the following JSON into the body: {username: "myUserName", password: "myPassword"} ...a token is returned. So in my fetch clause, I'm using JSON.stringify to pass the username and password to the server.

    I'm new to using Fetch with react, So my question is, how do I get started in authenticating various users, just using react JS with fetch only? I assume, I'm to write my logic within the second then of my Fetch clause?

    Currently, my page accepts any credentials and routes the user to a landing page upon clicking the submit button. I have a function containing fetch and now calling the fetch function once the onSubmit button is clicked, which now grabs the token.

    This is my code:

    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    import './Login.css';
    import { withRouter } from 'react-router-dom';
    
    class Login extends Component {
    
        constructor() {
            super();
            this.state = {
                data: [],
                username: "",
                password: "",
                token: "",
            };
        } //end constructor
    
        componentWillMount() {
        }
    
        componentDidMount() {
            this.fetchData();
        }
    
        fetchData() {
            fetch('http://theapi/api/auth', {
                method: 'POST',
                headers: {
                    'Content-type': 'application/json',
                },
                 body: JSON.stringify({
                    username: 'myUserName',
                    password: 'myPassword',
                    Authorization: 'TheReturnedToken',
                })
            }) /*end fetch */
            .then(results => results.json())
            .then(data => this.setState({ data: data })
    
            )
        }
    
        //request the token
          requestAccessToken(data) {
            const loginInfo = '${data}&grant_type=password';
            return fetch('${API_URL}Token', {
              method: 'POST',
              headers: new Headers({
                'Content-Type': 'application/json',
              }),
              body: loginInfo,
            })
              .then((response) => response.json());
          }
    
          //authenticate request
          requestUserInfo(token) {
            return fetch('${API_URL}api/participant/userinfo', {
              method: 'GET',
              headers: new Headers({
                Authorization: 'Bearer ${token}',
              }),
            })
              .then((response) => response.json());
          }
    
        change = (e) => {
            this.setState({
                [e.target.name]: e.target.value
            });
        }; //end change
    
        onSubmit = (e) =>{
            this.fetchData();
            e.preventDefault();
            //console.log(this.state);
            this.setState({
                 username: "",
                 password: "",
                });
    
            this.props.history.push('/landing');
            };
    
        render() {
        console.log(this.state.data);
            return (
               <div>
                    <div className="loginContainer">
                    <h2>Member Login</h2>
                        <form>
                                <input
                                id="username"
                                name="username"
                                placeholder="User Name"
                                value={this.state.username}
                                onChange={e => this.change(e) }
                                className="form-control"
                                />  <br />
    
                                <input
                                id="password"
                                name="password"
                                type="password"
                                placeholder="Password"
                                value={this.state.password}
                                onChange={e => this.change(e) }
                                className="form-control"
                                />  <br />
    
                            <button onClick={e => this.onSubmit(e)} className="btn btn-primary">Submit</button>
                            </form>
                        </div>
                </div>
            );
          }
    }
    
    export default withRouter(Login);
    

    How do I get started in getting my form to authenticate various users? Basically, I'm attempting to have my page to accept a username and password and if the two match, and then route the user to a landing page.

  • user1724708
    user1724708 almost 6 years
    I applied some modifications to the code I posted. I placed my fetch into a function & now calling it on the onSubmit event. In starting with requestAccessToken function, will ${API_URL}Token be my API's url with the token object appended? for example theapi/api/auth{token}. Please let me know if we may further discuss using stack-overflow chat. Am thinking what I have setup in my fetchData function, can be done using the snippet you provided.
  • Jay Jordan
    Jay Jordan almost 6 years
    No, see the headers: new Headers( part of my example. ${API_URL} is just part of my url. I'm asking for userinfo, with no parameters in the body or the url. I'm passing the bearer token in the header, and it returns the userinfo based off of that token. ${token} however, is actually the token from the api.
  • user1724708
    user1724708 almost 6 years
    I see that requestAccessToken has the parameter: data, and data is what contains the object, that being the user's token. Now,- requestUserInfo has the parameter token. So in order for the fetch contained within the requestAccessToken and requestUserInfo functions, ${API_URL} should be part of the API's URL am using in my FetchData function? what am trying to understanding is how the two functions are accessing my API. would the URL in the request and authenticate function be setup like so: theapi/api/auth/${API_URL}Token
  • Jay Jordan
    Jay Jordan almost 6 years
    ${API_URL} = 'https://api.mywebsite.com/' so when combined in second function it resolves to https://api.mywebsite.com/api/participant/userinfo which returns various other info about user like first name, last name, user roles, and ids that the front end needs.
  • Jay Jordan
    Jay Jordan almost 6 years
    The first function is retrieving my token and saving it to session storage. The second function is using that token in the header. I no longer need to pass user data. The api knows who is requesting based on the token.
  • user1724708
    user1724708 almost 6 years
    How are you storing api.mywebsite.com into ${API_URL} ? ...are you storing the API's url into ${API_URL} via another function? ...For loginInfo, is the client's info being stored and setup into a session storage through another function and passed to requestAccessToken? I apologize, am still new when it comes to dealing with API consumption. ...thank you
  • Jay Jordan
    Jay Jordan almost 6 years