Quickstart

A quick dive into getting started with Lore

Step 5: Add Callback Route

In this step we're going to add the redirect route that Auth0 needs so that we can save the token and log the user in.

You can view the finished code for this step by checking out the authentication.5 branch of the completed project.

Auth0 Callback

When Auth0 redirects you to the URL https://localhost:300/auth/callback, it also includes a number of important query parameters in the URL that look like this:

  access_token=...&expires_in=...&token_type=...&state=...&id_token=...

Among those query parameters is one called id_token, which contains a JWT token we need. So let's build a component to extract that token and save it.

Create the AuthCallback Component

To do that, create a component called AuthCallback:

lore generate component AuthCallback

Then update the file to look like this:

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import Auth0 from 'auth0-js';
import ShowLoadingScreen from './ShowLoadingScreen';
import auth from '../utils/auth';

export default createReactClass({
  displayName: 'AuthCallback',

  propTypes: {
    router: PropTypes.object.isRequired
  },

  componentDidMount() {
    const { router } = this.props;
    const auth0 = new Auth0.WebAuth(lore.config.auth0);

    auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {
        auth.saveToken(authResult.idToken);
        router.push('/');
      } else if (err) {
        console.log(err);
        alert('An error occurred. See the console for more information.');
      }
    });
  },

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

});
import React from 'react';
import PropTypes from 'prop-types';
import Auth0 from 'auth0-js';
import ShowLoadingScreen from './ShowLoadingScreen';
import auth from '../utils/auth';

class AuthCallback extends React.Component {

  componentDidMount() {
    const { router } = this.props;
    const auth0 = new Auth0.WebAuth(lore.config.auth0);

    auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {
        auth.saveToken(authResult.idToken);
        router.push('/');
      } else if (err) {
        console.log(err);
        alert('An error occurred. See the console for more information.');
      }
    });
  }

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

}

AuthCallback.propTypes = {
  router: PropTypes.object.isRequired
};

export default AuthCallback;
import React from 'react';
import PropTypes from 'prop-types';
import Auth0 from 'auth0-js';
import ShowLoadingScreen from './ShowLoadingScreen';
import auth from '../utils/auth';

class AuthCallback extends React.Component {

  static propTypes = {
    router: PropTypes.object.isRequired
  };

  componentDidMount() {
    const { router } = this.props;
    const auth0 = new Auth0.WebAuth(lore.config.auth0);

    auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {
        auth.saveToken(authResult.idToken);
        router.push('/');
      } else if (err) {
        console.log(err);
        alert('An error occurred. See the console for more information.');
      }
    });
  }

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

}

export default AuthCallback;

When this component gets mounted, we're going to once again create the Auth0.WebAuth() object and provide it with our auth0 config at lore.config.auth0.

Then we're going to call auth0.parseHash(), which will extract the query parameters we need from the URL, and provide them through an object called authResult.

Then, if all the query parameters that we need exist, we'll save the idToken to localStorage using our auth.saveToken() helper, and redirect the user to the root route at /.

Create the /auth/callback route

Now that the component exists, let's create the corresponding route to display it.

Import your AuthCallback component into routes.js and update the routes to look like this:

// routes.js
...
import AuthCallback from './src/components/AuthCallback';

export default (
  <Route>
    <Route path="/login" component={Login} />
    <Route path="/auth/callback" component={AuthCallback} />

    <Route component={UserIsAuthenticated(Master)}>
      <Route path="/" component={Layout}>
        <IndexRoute component={Feed} />
      </Route>
    </Route>
  </Route>
);
// routes.js
...
import AuthCallback from './src/components/AuthCallback';

export default (
  <Route>
    <Route path="/login" component={Login} />
    <Route path="/auth/callback" component={AuthCallback} />

    <Route component={UserIsAuthenticated(Master)}>
      <Route path="/" component={Layout}>
        <IndexRoute component={Feed} />
      </Route>
    </Route>
  </Route>
)
// routes.js
...
import AuthCallback from './src/components/AuthCallback';

export default (
  <Route>
    <Route path="/login" component={Login} />
    <Route path="/auth/callback" component={AuthCallback} />

    <Route component={UserIsAuthenticated(Master)}>
      <Route path="/" component={Layout}>
        <IndexRoute component={Feed} />
      </Route>
    </Route>
  </Route>
)

Once that's done, refresh the browser and navigate to /login. This time, once you log in, Auth0 will redirect you to the /auth/callback route, which will store the token we need, and redirect you back to the home route.

Visual Check-in

If everything went well, your application should now look like this.

Code Changes

Below is a list of files modified during this step.

src/components/AuthCallback.js

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import Auth0 from 'auth0-js';
import ShowLoadingScreen from './ShowLoadingScreen';
import auth from '../utils/auth';

export default createReactClass({
  displayName: 'AuthCallback',

  propTypes: {
    router: PropTypes.object.isRequired
  },

  componentDidMount() {
    const { router } = this.props;
    const auth0 = new Auth0.WebAuth(lore.config.auth0);

    auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {
        auth.saveToken(authResult.idToken);
        router.push('/');
      } else if (err) {
        console.log(err);
        alert('An error occurred. See the console for more information.');
      }
    });
  },

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

});
import React from 'react';
import PropTypes from 'prop-types';
import Auth0 from 'auth0-js';
import ShowLoadingScreen from './ShowLoadingScreen';
import auth from '../utils/auth';

class AuthCallback extends React.Component {

  componentDidMount() {
    const { router } = this.props;
    const auth0 = new Auth0.WebAuth(lore.config.auth0);

    auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {
        auth.saveToken(authResult.idToken);
        router.push('/');
      } else if (err) {
        console.log(err);
        alert('An error occurred. See the console for more information.');
      }
    });
  }

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

}

AuthCallback.propTypes = {
  router: PropTypes.object.isRequired
};

export default AuthCallback;
import React from 'react';
import PropTypes from 'prop-types';
import Auth0 from 'auth0-js';
import ShowLoadingScreen from './ShowLoadingScreen';
import auth from '../utils/auth';

class AuthCallback extends React.Component {

  static propTypes = {
    router: PropTypes.object.isRequired
  };

  componentDidMount() {
    const { router } = this.props;
    const auth0 = new Auth0.WebAuth(lore.config.auth0);

    auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {
        auth.saveToken(authResult.idToken);
        router.push('/');
      } else if (err) {
        console.log(err);
        alert('An error occurred. See the console for more information.');
      }
    });
  }

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

}

export default AuthCallback;

routes.js

import React from 'react';
import { Route, IndexRoute, Redirect } from 'react-router';

/**
 * Wrapping the Master component with this decorator provides an easy way
 * to redirect the user to a login experience if we don't know who they are.
 */
import UserIsAuthenticated from './src/decorators/UserIsAuthenticated';

/**
 * Routes are used to declare your view hierarchy
 * See: https://github.com/ReactTraining/react-router/blob/v3/docs/API.md
 */
import Master from './src/components/Master';
import Layout from './src/components/Layout';
import Feed from './src/components/Feed';
import Login from './src/components/Login';
import AuthCallback from './src/components/AuthCallback';

export default (
  <Route>
    <Route path="/login" component={Login} />
    <Route path="/auth/callback" component={AuthCallback} />

    <Route component={UserIsAuthenticated(Master)}>
      <Route path="/" component={Layout}>
        <IndexRoute component={Feed} />
      </Route>
    </Route>
  </Route>
);

Next Steps

Next we're going to add a logout route.