Giter Site home page Giter Site logo

dominic-farquharson / react-linkedin-login Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 948 KB

LinkedIn Oauth w/o passport!

HTML 12.44% CSS 2.91% JavaScript 81.36% EJS 3.30%
linkedin linkedin-signin linkedin-login react javascript expressjs express-middleware nodejs

react-linkedin-login's Introduction

LinkedIn Oauth

Retrieve linkedin access token and user data w/o passport.

Dependencies

  • Node
  • Express
  • Express-Session
  • dotenv
  • randomstring
  • axios

Installation

  • Clone or download the repo.
  • cd into the directory and run npm install.
  • Create a .env file in the root of your project.
  • Add relevant local variables to your .env file.
  • run npm run start to start the node server.
    • The built react app will be served.

Sample .env file

SECRET_KEY = secret_key
CLIENT_ID = linkedin_client_id
CLIENT_SECRET = linkedin_secret
NODE_ENV = 'development'

Overview

The server initially renders a react frontend.

// served from the public folder

app.use(express.static(__dirname + '/public'));

When the App component of the React app initially mounts a request is made to the server to retrieve a state string.

// Within client/src//App.js
login() {
  axios({
    url: '/login',
    method: 'GET',
  })
    .then(res => {
      if(res.error) throw 'error fetching profile info';
      // state and the redirect uri are fetched from the server
      this.fetchProfileInfo(res.data.state, res.data.redirect_uri)
    })
    .catch(err => {
      console.log('error ', err)
    })
}

The state string is a unique string, generated on the backend, that will be stored in a session. The random string is generated using the randomstring npm package.

  // generate random string
  const state = randomString.generate();
  // store string in session
  req.session.state = state;

  // determine proper redirect uri based on the environment
  const redirect_uri = process.env.NODE_ENV === 'development' ? `http://localhost:8080/auth/linkedin/callback` : `https://react-linkedin-login.herokuapp.com/auth/linkedin/callback`;

  // send error if any, state string, and redirect uri to cient (React app)
  res.json({
    // pass down errors
    error: req.session.error || null,
    state, 
    redirect_uri

  });

The state string must then be stored in the session, and passed to linkedin as a query string parameter. The client is then redirected to that url.

The client id can be stored on the frontend, the client secret must be kept on the server.

fetchProfileInfo(state, redirect_uri) {
  // state and redirect uri fetched from server and inserted into string, could also pass client id
  window.location.href =`https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=${your client id here}&redirect_uri=${redirect_uri}&state=${state}&scope=r_basicprofile r_emailaddress`;

  // requesting basic profile and email address permisions. These must be set from linkedin dev menu
}

If the user signs in and provides permission, the user is redirected to the provided redirect_uri.

// that route is handled here
app.get('/auth/linkedin/callback', checkState, exchangeAccessToken, profileData);

The request is then passed through three middleware: checkState, exchangeAccessToken, profileData.

checkState middleware

// check if state from query matches string in session
const checkState = (req, res, next) => {
  const { code, state, error, error_description } = req.query;

  // first check if state matches
  if(state !== req.session.state) {
    return res.status(401).send('not authorized');
  }

  if(error) {
    return res.json({
      error,
      error_description  
    });
  }

  next();
}

exchangeAccessToken middleware

const exchangeAccessToken = (req, res, next) => {
  const { code, state, error, error_description } = req.query;

  // determine redirect uri based on env
  const redirect_uri = process.env.NODE_ENV === 'development' ? `http://localhost:8080/auth/linkedin/callback` : `https://react-linkedin-login.herokuapp.com/auth/linkedin/callback`;

  const data = {
    grant_type: 'authorization_code',
    code,
    redirect_uri,
    client_id: process.env.CLIENT_ID,
    client_secret: process.env.CLIENT_SECRET
  };
     
   axios({
    url: 'https://www.linkedin.com/oauth/v2/accessToken',
    method: 'POST',
    headers: {
      'Content-type': 'application/x-www-urlenconded'
    },
    params:  data
  })
    .then(response => {
      const { access_token, expires_in } = response.data;

      res.locals.access_token = access_token;
      res.locals.expires_in = expires_in;
      next();
    })
    .catch(function (error) {
      // handle errors
      if (error.response) {
        // The request was made and the server responded with a status code
        // that falls out of the range of 2xx
        console.log(error.response.data);
        console.log(error.response.status);
        console.log(error.response.headers);
        // res.send('error')
      } else if (error.request) {
        // The request was made but no response was received
        // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
        // http.ClientRequest in node.js
        console.log(error.request);
        // res.send('no request received')
      } else {
        // Something happened in setting up the request that triggered an Error
        console.log('Error', error.message);
        // res.send(error.message)
      }
      console.log('setting errr')
      req.session.error = 'Error logging in';

      // console.log(error.config);
      res.redirect('/login')
    }); 
}

profileData middleware

With the access token now successfully retrieved, the user's profile data can now be fetched.

const profileData = (req, res, next) => {
  const {access_token} = res.locals;

  // specifying info to include in response
  const info = 'first-name,last-name,location,picture-urls::(original),email-address,picture-url'

  axios({
    url: `https://api.linkedin.com/v1/people/~:(${info})?format=json`,
    method: 'GET',
    headers: {
      Authorization: `Bearer ${access_token}`
    }
  })
    .then(response => {
      // store profile data in session
      req.session.profile = response.data;   
      
      res.redirect('/')
    })
    .catch(function (error) {

      // handle errors
      if (error.response) {
        // The request was made and the server responded with a status code
        // that falls out of the range of 2xx
        console.log(error.response.data);
        console.log(error.response.status);
        console.log(error.response.headers);
        // res.send('error')
      } else if (error.request) {
        // The request was made but no response was received
        // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
        // http.ClientRequest in node.js
        console.log(error.request);
        // res.send('no request received')
      } else {
        // Something happened in setting up the request that triggered an Error
        console.log('Error', error.message);
        // res.send(error.message)
      }

      req.session.error = 'Error logging in';
      next(error)
    }); 
}

Usage

  1. Fork and /or clone this repository.
  2. cd into the directory.
  3. Run npm install
  4. Run npm dev

Note: The built client will be served from the public directory

To run the React app

  1. cd into the client directory
  2. Run npm install
  3. Run npm start

react-linkedin-login's People

Contributors

dominic-farquharson avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.