Okta Auth JS and React

On This Page

This guide will walk you through integrating authentication into a React app with Okta by performing these steps:

  1. Add an OpenID Connect Client in Okta
  2. Create a React App
  3. Install Dependencies
  4. Create a Custom Sign-In Form
  5. Create Routes
  6. Connect the Routes
  7. Start Your App

Prerequisites

If you do not already have a Developer Edition Account, you can create one at https://developer.okta.com/signup/.

Add an OpenID Connect Client in Okta

  • Sign in to the Okta Developer Dashboard, and select Create New App
  • Choose Single Page App (SPA) as the platform, then populate your new OpenID Connect application with appropriate values for your app. For example:
Setting Value
App Name OpenId Connect App (must be unique)
Login redirect URIs http://localhost:3000/login/callback
Logout redirect URIs http://localhost:3000/login
Allowed grant types Authorization Code

Note: CORS is automatically enabled for the granted login redirect URIs.

Create a React App

To quickly create a React app, we recommend the create-react-app CLI. Follow their guide here.

Install Dependencies

A simple way to add authentication to a React app is using the Okta Auth JS library. We can install it via npm:

npm install @okta/okta-auth-js --save

We'll also need @okta/okta-react and react-router-dom to manage your routes (@okta/okta-react can be used to support other router libraries, but react-router-dom has pre-existing support).

npm install @okta/okta-react react-router-dom --save

Create a Custom Sign-In Form

If the Okta Sign-In Widget does not fit your needs, AuthJS provides lower-level access to User Lifecycle operations, MFA, and more. For this example, we'll create a simple username and password form without MFA.

Create a src/SignInForm.jsx file:

src/SignInForm.jsx using a function-based component:

// src/SignInForm.jsx

import React, { useState } from 'react';
import OktaAuth from '@okta/okta-auth-js';
import { useOktaAuth } from '@okta/okta-react';

const SignInForm = ({ issuer }) => {
  const { authService } = useOktaAuth();
  const [sessionToken, setSessionToken] = useState();
  const [username, setUsername] = useState();
  const [password, setPassword] = useState();

  const handleSubmit = (e) => {
    e.preventDefault();
    const oktaAuth = new OktaAuth({
      // If your app is configured to use the Implicit Flow
      // instead of the Authorization Code with Proof of Code Key Exchange (PKCE)
      // you will need to uncomment the below line:
      // pkce: false,
      issuer: issuer
      });
    oktaAuth.signIn({ username, password })
    .then(res => {
      const sessionToken = res.sessionToken;
      setSessionToken(sessionToken);
      // sessionToken is a one-use token, so make sure this is only called once
      authService.redirect({ sessionToken });
    })
    .catch(err => console.log('Found an error', err));
  };

  const handleUsernameChange = (e) => {
    setUsername(e.target.value);
  };

  const handlePasswordChange = (e) => {
    setPassword(e.target.value);
  };

  if (sessionToken) {
    // Hide form while sessionToken is converted into id/access tokens
    return null;
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Username:
        <input
          id="username" type="text"
          value={username}
          onChange={handleUsernameChange} />
      </label>
      <label>
        Password:
        <input
          id="password" type="password"
          value={password}
          onChange={handlePasswordChange} />
      </label>
      <input id="submit" type="submit" value="Submit" />
    </form>
  );
};
export default SignInForm;

src/SignInForm.jsx using a class-based component:

// src/SignInForm.jsx

import React, { Component } from 'react';
import OktaAuth from '@okta/okta-auth-js';
import { withOktaAuth } from '@okta/okta-react';

export default withOktaAuth(class SignInForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      sessionToken: null,
      username: '',
      password: ''
    };

    this.oktaAuth = new OktaAuth({
      // If your app is configured to use the Implicit Flow
      // instead of the Authorization Code with Proof of Code Key Exchange (PKCE)
      // you will need to uncomment the below line:
      // pkce: false,
      issuer: props.issuer
    });

    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleUsernameChange = this.handleUsernameChange.bind(this);
    this.handlePasswordChange = this.handlePasswordChange.bind(this);
  }

  handleSubmit(e) {
    e.preventDefault();
    this.oktaAuth.signIn({
      username: this.state.username,
      password: this.state.password
    })
    .then(res => {
      const sessionToken = res.sessionToken;
      this.setState(
        { sessionToken },
        // sessionToken is a one-use token, so make sure this is only called once
        () => this.props.authService.redirect({sessionToken})
      );
    })
    .catch(err => console.log('Found an error', err));
  }

  handleUsernameChange(e) {
    this.setState({username: e.target.value});
  }

  handlePasswordChange(e) {
    this.setState({password: e.target.value});
  }

  render() {
    if (this.state.sessionToken) {
      // Hide form while sessionToken is converted into id/access tokens
      return null;
    }

    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Username:
          <input
            id="username" type="text"
            value={this.state.username}
            onChange={this.handleUsernameChange} />
        </label>
        <label>
          Password:
          <input
            id="password" type="password"
            value={this.state.password}
            onChange={this.handlePasswordChange} />
        </label>
        <input id="submit" type="submit" value="Submit" />
      </form>
    );
  }
});

Create Routes

Some routes require authentication in order to render. Defining those routes is easy using SecureRoute from @okta/okta-react. Let's take a look at what routes are needed for this example:

  • /: A default page to handle basic control of the app.
  • /protected: A route protected by SecureRoute.
  • /login: Redirect to the org sign-in page.
  • /login/callback: A route to parse tokens after a redirect.

/

First, create src/Home.jsx to provide links to navigate our app:

src/Home.jsx using a function-based component:

// src/Home.jsx

import React from 'react';
import { Link } from 'react-router-dom';
import { useOktaAuth } from '@okta/okta-react';

const Home = () => {
  const { authState, authService } = useOktaAuth();

  if (authState.isPending) {
    return <div>Loading...</div>;
  }

  const button = authState.isAuthenticated ?
    <button onClick={() => {authService.logout()}}>Logout</button> :
    <button onClick={() => {authService.login()}}>Login</button>;

  return (
    <div>
      <Link to='/'>Home</Link><br/>
      <Link to='/protected'>Protected</Link><br/>
      {button}
    </div>
  );
};
export default Home;

src/Home.jsx using a class-based component:

// src/Home.jsx

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { withOktaAuth } from '@okta/okta-react';

export default withOktaAuth(class Home extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    if (this.props.authState.isPending) {
      return <div>Loading...</div>;
    }

    const button = this.props.authState.isAuthenticated ?
      <button onClick={() => {this.props.authService.logout()}}>Logout</button> :
      <button onClick={() => {this.props.authService.login()}}>Login</button>;

    return (
      <div>
        <Link to='/'>Home</Link><br/>
        <Link to='/protected'>Protected</Link><br/>
        {button}
      </div>
    );
  }
});

/protected

This route will only be visible to users with a valid accessToken.

Create a new component src/Protected.jsx:

// src/Protected.jsx

import React from 'react';

export default () => <h3>Protected</h3>;

/login

This route redirects if the user is already logged in. If the user is coming from a protected page, they'll be redirected back to the page upon successful sign in.

Create a new component src/SignIn.jsx:

src/SignIn.jsx using a function-based component:

// src/SignIn.jsx

import React from 'react';
import { Redirect } from 'react-router-dom';
import SignInForm from './SignInForm';
import { useOktaAuth } from '@okta/okta-react';

const SignIn = ({ issuer }) => {
  const { authState } = useOktaAuth();

  if (authState.isPending) {
    return <div>Loading...</div>;
  }
  return authState.isAuthenticated ?
    <Redirect to={{ pathname: '/' }}/> :
    <SignInForm issuer={issuer} />;
};

export default SignIn;

src/SignIn.jsx using a class-based component:

// src/SignIn.jsx

import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import SignInForm from './SignInForm';
import { withOktaAuth } from '@okta/okta-react';

export default withOktaAuth(class SignIn extends Component {
  render() {
    if (this.props.authState.isPending) {
      return <div>Loading...</div>;
    }
    return this.props.authState.isAuthenticated ?
      <Redirect to={{ pathname: '/' }}/> :
      <SignInForm issuer={this.props.issuer} />;
  }
});

/login/callback

The component for this route (LoginCallback) comes with @okta/okta-react. It handles token parsing, token storage, and redirecting to a protected page if one triggered the sign in.

Connect the Routes

Update src/App.jsx to include your project components and routes. Security is the component that controls the authentication flows, so it requires your OpenId Connect configuration. By default, @okta/okta-react redirects to Okta's sign in page when the user isn't authenticated.

In this example, onAuthRequired is overridden to redirect to the custom sign-in route instead, which requires a component that is a descendent of Router to have access to react-router's history. Other router libraries will have their own methods of managing browser history:

src/App.jsx and src/AppWithRouterAccess.jsx using function-based components:

// src/App.jsx

import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import AppWithRouterAccess from './AppWithRouterAccess';

const App = () => {
  return (
    <Router>
      <AppWithRouterAccess/>
    </Router>
  );
}

export default App;
// src/AppWithRouterAccess.jsx

import React from 'react';
import { Route, useHistory } from 'react-router-dom';
import { Security, SecureRoute, LoginCallback } from '@okta/okta-react';
import Home from './Home';
import SignIn from './SignIn';
import Protected from './Protected';

const AppWithRouterAccess = () => {
  const history = useHistory();
  const onAuthRequired = () => {
    history.push('/login');
  };

  return (
    <Security issuer='https://${yourOktaDomain}/oauth2/default'
              clientId='{clientId}'
              redirectUri={window.location.origin + '/login/callback'}
              onAuthRequired={onAuthRequired}
              pkce={true} >
      <Route path='/' exact={true} component={Home} />
      <SecureRoute path='/protected' component={Protected} />
      <Route path='/login' render={() => <SignIn issuer='https://${yourOktaDomain}/oauth2/default' />} />
      <Route path='/login/callback' component={LoginCallback} />
    </Security>
  );
};
export default AppWithRouterAccess;

src/App.jsx and src/AppWithRouterAccess.jsx using class-based components:

// src/App.jsx

import React, { Component } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import AppWithRouterAccess from './AppWithRouterAccess';

class App extends Component {
  render() {
    return (
      <Router>
        <AppWithRouterAccess/>
      </Router>
    );
  }
}

export default App;
// src/AppWithRouterAccess.jsx

import React, { Component } from 'react';
import { Route, withRouter } from 'react-router-dom';
import { Security, SecureRoute, LoginCallback } from '@okta/okta-react';
import Home from './Home';
import SignIn from './SignIn';
import Protected from './Protected';

export default withRouter(class AppWithRouterAccess extends Component {
  constructor(props) {
    super(props);
    this.onAuthRequired = this.onAuthRequired.bind(this);
  }

  onAuthRequired() {
    this.props.history.push('/login');
  }

  render() {
    return (
      <Security issuer='https://${yourOktaDomain}/oauth2/default'
                clientId='{clientId}'
                redirectUri={window.location.origin + '/login/callback'}
                onAuthRequired={this.onAuthRequired}
                pkce={true} >
        <Route path='/' exact={true} component={Home} />
        <SecureRoute path='/protected' component={Protected} />
        <Route path='/login' render={() => <SignIn issuer='https://${yourOktaDomain}/oauth2/default' />} />
        <Route path='/login/callback' component={LoginCallback} />
      </Security>
    );
  }
});

Start your app

Finally, start your app:

npm start

Conclusion

You have now successfully authenticated with Okta! Now what? With a user's id_token, you have basic claims for the user's identity. You can extend the set of claims by modifying the scopes to retrieve custom information about the user. This includes locale, address, groups, and more.

Want to learn how to use the user's access_token? Check out our React How To Guide to learn about protecting routes on your server, validating the access_token, and more!

Support

Have a question or see a bug? Post your question on the Okta Developer Forums.