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:
- Add an OpenID Connect Client in Okta
- Create a React App
- Install Dependencies
- Create a Custom Sign-In Form
- Create Routes
- Connect the Routes
- 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 bySecureRoute
./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.