npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

redux-mad-authentication

v2.0.1

Published

Authenticating with a Spring Security application and storing the currentUser in Redux.

Downloads

79

Readme

About

Build Status Codecov

This is 42's authentication module for Redux in combination with a specific Spring Security settings.

It can do the following things:

  1. Log the user in and out with your Spring application.
  2. Saving the current user in the Redux store.
  3. Send 'fetch' request with XSRF tokens and the cookies.
  4. Make a route only available for logged in users.
  5. Make a route available for specific users, based on properties of the current user.

Getting started.

We assume you have a working Redux project, if you do not yet have Redux add Redux to your project by following the Redux's instructions.

First install the following dependencies in the package.json:

  1. "react-redux": "5.0.3",
  2. "redux": "3.6.0",
  3. "react-router-dom": "4.0.0"

Now add the authentication-reducer to your rootReducer for example:

import { combineReducers } from 'redux';
import { authentication, AuthenticationStore } from 'redux-mad-authentication';

export interface Store {
  authentication: AuthenticationStore
};

// Use ES6 object literal shorthand syntax to define the object shape
const rootReducer: Store = combineReducers({
  authentication,
});

export default rootReducer;

This should add the AuthenticationStore to Redux, which will store the logged in user.

Next you have to configure the authentication module:

import { createStore } from 'redux';
import { configureAuthentication } from 'redux-mad-authentication';

export const store = createStore(
  rootReducer,
);

configureAuthentication({
  // The URL of your Spring back-end where the user can login (POST) and logout(DELETE)
  authenticationUrl: '/api/authentication',

  // The URL of your Spring back-end where the current user can be requested via GET
  currentUserUrl: '/api/authentication/current',

  // The route (in the front-end) the user should be redirected to when not logged in.
  loginRoute: '/login',

  // A reference to the dispatch function for the react store.
  dispatch: store.dispatch,

  // A function which returns the current 'authentication' store 
  authenticationStore: () => store.getState().authentication
});

The authentication module must be configured before the application is rendered.

How to

Writing a LoginForm.

In order to log the user in you must have a login form of some sorts. This library does not assume anything on how this login form should work or what it looks like.

Here's what a LoginForm should do:

  1. Call 'login' when the user submits the login form, with the correct body.
  2. Try to auto-login the user via current in the componentDidMount.
  3. In the render Redirect the user when he is logged in.

For example:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Redirect, locationShape } from 'react-router-dom';
import { login, current } from 'redux-mad-authentication';

import { Store } from '../redux/root-reducer';

interface Props {
  loggedIn: boolean,
  location: locationShape
};

interface State {
  username: string,
  password: string,
  error: boolean,
  autoLoginFailed: boolean
};

export class Login extends Component<Props, State> {
  state = {
    username: '',
    password: '',
    error: false,
    autoLoginFailed: false
  };

  // Calling `current()` automatically logs the user in when the session is still valid
  componentDidMount() {
    current().catch(() => {
      this.setState({ autoLoginFailed: true });
    });
  }

  onSubmit(event: Event) {
    event.preventDefault();

    const { username, password } = this.state;

    this.setState({ error: false });

    // `login` expects a body to send to the server
    login({  username, password }).catch((error) => {
      this.setState({ error: true });
    });
  }

  setUsername(username: string) {
    this.setState({ username });
  }

  setPassword(password: string) {
    this.setState({ password });
  }

  render() {
    // Be sure 
    if (this.props.loggedIn) {
      const { from } = this.props.location.state || { from: { pathname: '/' } };

      return <Redirect to={ from }/>;
    }

    if (this.state.autoLoginFailed === false) {
      return null;
    }

    const { username, password, error } = this.state;

    const errorMessage = error ? 'Username and password are incorrect' : '';

    return (
      <form>
        <h1>Please log in</h1>
        <p>
          <label htmlFor="username">Username</label>
          <input
            id="username"
            name="username"
            type="text"
            value={ username }
            onChange={ (event) => this.setUsername(event.target.value) }
          />
        </p>

        <p>
          <label htmlFor="password">Password</label>
          <input
            id="password"
            name="password"
            type="password"
            value={ password }
            onChange={ (event) => this.setPassword(event.target.value) }
          />
        </p>

        <p>{ errorMessage }</p>

        <button type="sumbit" onClick={ (event) => this.onSubmit(event) }>Log in</button>
      </form>
    );
  }
}

export default connect((store: Store) => {
  return {
    loggedIn: store.authentication.isLoggedIn
  };
})(Login);

Writing a Logout

In order to logout you must call the 'logout' function, and make sure you Redirect the user to the login form when the user is logged in.

For example:

import React, { Component } from 'react';

import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
import { Store } from '.redux/root-reducer';
import { logout } from 'redux-mad-authentication';

interface Props {
  isLoggedIn: boolean
};

export class Logout extends Component<Props, void> {

  onLogoutClick() {
    logout();
  }

  render() {
    if (this.props.isLoggedIn === false) {
      return <Redirect to="/"/>;
    }

    return (
      <a onClick={ () => this.onLogoutClick() }>Uitloggen</a>
    );
  }
}

export default connect((store: Store) => {
  return {
    isLoggedIn: store.authentication.isLoggedIn
  };
})(Logout);

Make a Route private

Some routes can only be accessible when the user is logged in. You can do this via the PrivateRoute for example:

<BrowserRouter history={ browserHistory }>
  <div>
    <Route exact path="/" component={ Dashboard } />
    <Route path="/login" component={ Login }/>
    <PrivateRoute path="/users" component={ Users } />
  </div>
</BrowserRouter>

PrivateRoute works exacly like Route, except that it does not support a render method. You must always provide a Component instead.

When the user tries to go to a PrivateRoute he will be redirected to the Config's route loginRoute.

Add authorization to a Route

Some routes can only be accessed by a type of user or a specific user. You can do this via the AuthorizedRoute.

<BrowserRouter>
  <div>
    <Route exact path="/" component={ Dashboard } />
    <Route path="/login" component={ Login }/>
    <PrivateRoute path="/users" component={ Users } />
    <AuthorizedRoute 
      path="/pictures" 
      component={ Pictures }
      authorizer={ (authenticationStore: AuthenticationStore) => {
        return authenticationStore.currentUser.role === 'ADMIN';
      }}
    />
  </div>
</BrowserRouter>

The authorizer function is only ran if the user is logged in. The authorizer is given the authenticationStore as the first parameter, and is expected to return a boolean.

AuthorizedRoute works exacly like Route, except that it does not support a render method. You must always provide a Component instead.

When the user tries to go to a AuthorizedRoute he will be redirected to the Config's route loginRoute.

Get the current user's info / login status

Lets say you have a component which must render the current user's name, you will then need the current user from the Redux store.

We can use react-redux's connect to achieve this.

For example:


import { connect } from 'react-redux';
import { Store } from '.redux/root-reducer';

function User(props) {
  if (isLoggedIn) {
    return <h1>Hi {{ props.currentUser.name }}</h1>
  } else {
    return <h1>Please log in</h1>
  }
}

export default connect((store: Store) => {
  return {
    isLoggedIn: store.authentication.isLoggedIn,
    currentUser: store.authentication.currentUser
  };
})(User);

Send a request with the XSRF token as the current user.

To perform request with the XSRF token and with the cookies from the current user. You can use the 'authFetch' utility from this library:


import 'authFetch' from './authentication'

function getUser() {
  authFetch('/user/1').then((response) => {
    console.log(response);
  });
}

authFetch is a thin wrapper around fetch, it only adds the credentials and XSRF token, so it has the exact same arguments as fetch.