Okta React SDK builds on top of the Okta Auth SDK.
This SDK is a toolkit to build Okta integration with many common "router" packages, such as react-router, reach-router, and others.
Users migrating from version 1.x of this SDK that required react-router should see Migrating from 1.x to learn what changes are necessary.
With the Okta Auth SDK, you can:
All of these features are supported by this SDK. Additionally, using this SDK, you can:
This SDK does not provide any UI components.
This SDK does not currently support Server Side Rendering (SSR)
This library currently supports:
:heavy_check_mark: The current stable major version series is: 6.x
Version | Status |
---|---|
6.x |
:heavy_check_mark: Stable |
5.x |
:heavy_check_mark: Stable |
4.x |
:x: Retired |
3.x |
:x: Retired |
2.x |
:x: Retired |
1.x |
:x: Retired |
The latest release can always be found on the [releases page][github-releases].
This library is available through npm.
Install @okta/okta-react
npm install --save @okta/okta-react
Install peer dependencies
npm install --save react
npm install --save react-dom
npm install --save react-router-dom # see note below
npm install --save @okta/okta-auth-js # requires at least version 5.3.1
⚠️ NOTE ⚠️
The SecureRoute component packaged in this SDK only works withreact-router-dom
5.x
. If you're usingreact-router-dom
6.x
, you'll have to write your ownSecureRoute
component.
See these samples to get started
okta-react
provides the means to connect a React SPA with Okta OIDC information. Most commonly, you will connect to a router library such as react-router.
okta-react
provides a number of pre-built components to connect a react-router
-based SPA to Okta OIDC information. You can use these components directly, or use them as a basis for building your own components.
Route
except authentication is needed to render the component.⚠️ NOTE ⚠️
The SecureRoute component packaged in this SDK only works withreact-router-dom
5.x
. If you're usingreact-router-dom
6.x
, you'll have to write your ownSecureRoute
component.
See these samples to get started
okta-react
provides the necessary tools to build an integration with most common React-based SPA routers.
<LoginCallback>
accepts an optional prop errorComponent
that will be used to format the output for any error in handling the callback. This component will be passed an error
prop that is an error describing the problem. (see the <OktaError>
component for the default rendering)Users of routers other than react-router
can use useOktaAuth to see if authState
is not null and authState.isAuthenticated
is true. If it is false, you can send them to login via oktaAuth.signInWithRedirect(). See the implementation of <LoginCallback>
as an example.
These hooks can be used in a component that is a descendant of a Security
component (<Security>
provides the necessary context). Class-based components can gain access to the same information via the withOktaAuth
Higher Order Component, which provides oktaAuth
and authState
as props to the wrapped component.
oktaAuth
- the Okta Auth SDK instance.authState
- the AuthState object that shows the current authentication state of the user to your app (initial state is null
).This example defines 3 routes:
Note: Make sure you have the /login/callback
url (absolute url) added in your Okta App's configuration.
A common mistake is to try and apply an authentication requirement to all pages, THEN add an exception for the login page. This often fails because of how routes are evaluated in most routing packages. To avoid this problem, declare specific routes or branches of routes that require authentication without exceptions.
// src/App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, withRouter } from 'react-router-dom';
import { SecureRoute, Security, LoginCallback } from '@okta/okta-react';
import { OktaAuth, toRelativeUrl } from '@okta/okta-auth-js';
import Home from './Home';
import Protected from './Protected';
class App extends Component {
constructor(props) {
super(props);
this.oktaAuth = new OktaAuth({
issuer: 'https://{yourOktaDomain}/oauth2/default',
clientId: '{clientId}',
redirectUri: window.location.origin + '/login/callback'
});
this.restoreOriginalUri = async (_oktaAuth, originalUri) => {
props.history.replace(toRelativeUrl(originalUri || '/', window.location.origin));
};
}
render() {
return (
<Security oktaAuth={this.oktaAuth} restoreOriginalUri={this.restoreOriginalUri} >
<Route path='/' exact={true} component={Home} />
<SecureRoute path='/protected' component={Protected} />
<Route path='/login/callback' component={LoginCallback} />
</Security>
);
}
}
const AppWithRouterAccess = withRouter(App);
export default class extends Component {
render() {
return (<Router><AppWithRouterAccess/></Router>);
}
}
import React from 'react';
import { SecureRoute, Security, LoginCallback } from '@okta/okta-react';
import { OktaAuth, toRelativeUrl } from '@okta/okta-auth-js';
import { BrowserRouter as Router, Route, useHistory } from 'react-router-dom';
import Home from './Home';
import Protected from './Protected';
const oktaAuth = new OktaAuth({
issuer: 'https://{yourOktaDomain}/oauth2/default',
clientId: '{clientId}',
redirectUri: window.location.origin + '/login/callback'
});
const App = () => {
const history = useHistory();
const restoreOriginalUri = async (_oktaAuth, originalUri) => {
history.replace(toRelativeUrl(originalUri || '/', window.location.origin));
};
return (
<Security oktaAuth={oktaAuth} restoreOriginalUri={restoreOriginalUri}>
<Route path='/' exact={true} component={Home} />
<SecureRoute path='/protected' component={Protected} />
<Route path='/login/callback' component={LoginCallback} />
</Security>
);
};
const AppWithRouterAccess = () => (
<Router>
<App />
</Router>
);
export default AppWithRouterAccess;
// src/Home.js
import React, { Component } from 'react';
import { withOktaAuth } from '@okta/okta-react';
export default withOktaAuth(class Home extends Component {
constructor(props) {
super(props);
this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
}
async login() {
this.props.oktaAuth.signInWithRedirect();
}
async logout() {
this.props.oktaAuth.signOut('/');
}
render() {
if (!this.props.authState) return <div>Loading...</div>;
return this.props.authState.isAuthenticated ?
<button onClick={this.logout}>Logout</button> :
<button onClick={this.login}>Login</button>;
}
});
// src/Home.js
const Home = () => {
const { oktaAuth, authState } = useOktaAuth();
const login = async () => oktaAuth.signInWithRedirect();
const logout = async () => oktaAuth.signOut('/');
if(!authState) {
return <div>Loading...</div>;
}
if(!authState.isAuthenticated) {
return (
<div>
<p>Not Logged in yet</p>
<button onClick={login}>Login</button>
</div>
);
}
return (
<div>
<p>Logged in!</p>
<button onClick={logout}>Logout</button>
</div>
);
};
export default Home;
When your users are authenticated, your React application has an access token that was issued by your Okta Authorization server. You can use this token to authenticate requests for resources on your server or API. As a hypothetical example, let's say you have an API that provides messages for a user. You could create a MessageList
component that gets the access token and uses it to make an authenticated request to your server.
Here is what the React component could look like for this hypothetical example:
import fetch from 'isomorphic-fetch';
import React, { Component } from 'react';
import { withOktaAuth } from '@okta/okta-react';
export default withOktaAuth(class MessageList extends Component {
constructor(props) {
super(props)
this.state = {
messages: null
}
}
async componentDidMount() {
try {
const response = await fetch('http://localhost:{serverPort}/api/messages', {
headers: {
Authorization: 'Bearer ' + this.props.authState.accessToken.accessToken
}
});
const data = await response.json();
this.setState({ messages: data.messages });
} catch (err) {
// handle error as needed
}
}
render() {
if (!this.state.messages) return <div>Loading...</div>;
const items = this.state.messages.map(message =>
<li key={message}>{message}</li>
);
return <ul>{items}</ul>;
}
});
When your users are authenticated, your React application has an access token that was issued by your Okta Authorization server. You can use this token to authenticate requests for resources on your server or API. As a hypothetical example, let's say you have an API that provides messages for a user. You could create a MessageList
component that gets the access token and uses it to make an authenticated request to your server.
Here is what the React component could look like for this hypothetical example:
import fetch from 'isomorphic-fetch';
import React, { useState, useEffect } from 'react';
import { useOktaAuth } from '@okta/okta-react';
export default MessageList = () => {
const { authState } = useOktaAuth();
const [messages, setMessages] = useState(null);
useEffect( () => {
if(authState.isAuthenticated) {
const apiCall = async () => {
try {
const response = await fetch('http://localhost:{serverPort}/api/messages', {
headers: {
Authorization: 'Bearer ' + authState.accessToken.accessToken
}
});
const data = await response.json();
setMessages( data.messages );
} catch (err) {
// handle error as needed
}
}
apiCall();
}
}, [authState] );
if (!messages) return <div>Loading...</div>;
const items = messages.map(message =>
<li key={message}>{message}</li>
);
return <ul>{items}</ul>;
};
Security
<Security>
is the top-most component of okta-react. It accepts oktaAuth instance and addtional configuration options as props.
(required) The pre-initialized oktaAuth instance. See Configuration Reference for details of how to initialize the instance.
(required) Callback function. Called to restore original URI during oktaAuth.handleLoginRedirect() is called. Will override restoreOriginalUri option of oktaAuth
(optional) Callback function. Called when authentication is required. If this is not supplied, okta-react
redirects to Okta. This callback will receive oktaAuth instance as the first function parameter. This is triggered when a SecureRoute is accessed without authentication. A common use case for this callback is to redirect users to a custom login route when authentication is required for a SecureRoute.
import { useHistory } from 'react-router-dom';
import { OktaAuth, toRelativeUrl } from '@okta/okta-auth-js';
const oktaAuth = new OktaAuth({
issuer: 'https://{yourOktaDomain}/oauth2/default',
clientId: '{clientId}',
redirectUri: window.location.origin + '/login/callback'
});
export default App = () => {
const history = useHistory();
const customAuthHandler = (oktaAuth) => {
// Redirect to the /login page that has a CustomLoginComponent
// This example is specific to React-Router
history.push('/login');
};
const restoreOriginalUri = async (_oktaAuth, originalUri) => {
history.replace(toRelativeUrl(originalUri || '/', window.location.origin));
};
return (
<Security
oktaAuth={oktaAuth}
onAuthRequired={customAuthHandler}
restoreOriginalUri={restoreOriginalUri}
>
<Route path='/login' component={CustomLoginComponent}>
{/* some routes here */}
</Security>
);
};
Assuming you have configured your application to allow the Authorization code
grant type, you can implement the PKCE flow with the following steps:
true
) and pass it to the Security
component./login/callback
route with LoginCallback component to handle login redirect from OKTA.import { OktaAuth } from '@okta/okta-auth-js';
const oktaAuth = new OktaAuth({
issuer: 'https://{yourOktaDomain}/oauth2/default',
clientId: '{clientId}',
redirectUri: window.location.origin + '/login/callback',
});
class App extends Component {
render() {
return (
<Security oktaAuth={oktaAuth} restoreOriginalUri={restoreOriginalUri}>
<Route path='/' exact={true} component={Home} />
<Route path='/login/callback' component={LoginCallback} />
</Security>
);
}
}
SecureRoute
SecureRoute
ensures that a route is only rendered if the user is authenticated. If the user is not authenticated, it calls onAuthRequired if it exists, otherwise, it redirects to Okta.
SecureRoute
accepts onAuthRequired
as an optional prop, it overrides onAuthRequired from the Security component if exists.
SecureRoute
runs internal handleLogin
process which may throw Error when authState.isAuthenticated
is false. By default, the Error will be rendered with OktaError
component. If you wish to customise the display of such error messages, you can pass your own component as an errorComponent
prop to <SecureRoute>
. The error value will be passed to the errorComponent
as the error
prop.
react-router
related propsSecureRoute
integrates with react-router
. Other routers will need their own methods to ensure authentication using the hooks/HOC props provided by this SDK.
As with Route
from react-router-dom
, <SecureRoute>
can take one of:
component
prop that is passed a componentrender
prop that is passed a function that returns a component. This function will be passed any additional props that react-router injects (such as history
or match
)LoginCallback
LoginCallback
handles the callback after the redirect to and back from the Okta-hosted login page. By default, it parses the tokens from the uri, stores them, then redirects to /
. If a SecureRoute
caused the redirect, then the callback redirects to the secured route. For more advanced cases, this component can be copied to your own source tree and modified as needed.
By default, LoginCallback will display any errors from authState.error
. If you wish to customise the display of such error messages, you can pass your own component as an errorComponent
prop to <LoginCallback>
. The authState.error
value will be passed to the errorComponent
as the error
prop.
By default, LoginCallback will display nothing during handling the callback. If you wish to customize this, you can pass your React element (not component) as loadingElement
prop to <LoginCallback>
. Example: <p>Loading...</p>
When an external auth (such as a social IDP) redirects back to your application AND your Okta sign-in policies require additional authentication factors before authentication is complete, the redirect to your application redirectUri callback will be an interaction_required
error.
An interaction_required
error is an indication that you should resume the authentication flow. You can pass an onAuthResume
function as a prop to <LoginCallback>
, and the <LoginCallback>
will call the onAuthResume
function when an interaction_required
error is returned to the redirectUri of your application.
If using the Okta SignIn Widget, redirecting to your login route will allow the widget to automatically resume your authentication transaction.
// Example assumes you are using react-router with a customer-hosted Okta SignIn Widget on your /login route
// This code is wherever you have your <Security> component, which must be inside your <Router> for react-router
const onAuthResume = async () => {
history.push('/login');
};
return (
<Security
oktaAuth={oktaAuth}
restoreOriginalUri={restoreOriginalUri}
>
<Switch>
<SecureRoute path='/protected' component={Protected} />
<Route path='/login/callback' render={ (props) => <LoginCallback {...props} onAuthResume={ onAuthResume } /> } />
<Route path='/login' component={CustomLogin} />
<Route path='/' component={Home} />
</Switch>
</Security>
);
withOktaAuth
withOktaAuth
is a higher-order component which injects an oktaAuth instance and an authState object as props into the component. Function-based components will want to use the useOktaAuth
hook instead. These props provide a way for components to make decisions based on authState or to call Okta Auth SDK methods, such as .signInWithRedirect()
or .signOut()
. Components wrapped in withOktaAuth()
need to be a child or descendant of a <Security>
component to have the necessary context.
useOktaAuth
useOktaAuth()
is a React Hook that returns an object containing the authState object and the oktaAuth instance. Class-based components will want to use the withOktaAuth HOC instead. Using this hook will trigger a re-render when the authState object updates. Components calling this hook need to be a child or descendant of a <Security>
component to have the necessary context.
useOktaAuth
import React from 'react';
import { useOktaAuth } from '@okta/okta-react';
export default MyComponent = () => {
const { authState } = useOktaAuth();
if( !authState ) {
return <div>Loading...</div>;
}
if( authState.isAuthenticated ) {
return <div>Hello User!</div>;
}
return <div>You need to login</div>;
};
@okta/okta-react
6.x requires @okta/okta-auth-js
5.x (see notes for migration). Some changes affects @okta/okta-react
:
AuthState
is nullisPending
from AuthState
originalUri
is nullFrom version 5.0, the Security component explicitly requires prop restoreOriginalUri to decouple from react-router
.
Example of implementation of this callback for react-router
:
import { Security } from '@okta/okta-react';
import { useHistory } from 'react-router-dom';
import { OktaAuth, toRelativeUrl } from '@okta/okta-auth-js';
const oktaAuth = new OktaAuth({
issuer: 'https://{yourOktaDomain}/oauth2/default',
clientId: '{clientId}',
redirectUri: window.location.origin + '/login/callback'
});
export default App = () => {
const history = useHistory();
const restoreOriginalUri = async (_oktaAuth, originalUri) => {
history.replace(toRelativeUrl(originalUri, window.location.origin));
};
return (
<Security
oktaAuth={oktaAuth}
restoreOriginalUri={restoreOriginalUri}
>
{/* some routes here */}
</Security>
);
};
Note: If you use basename
prop for <BrowserRouter>
, use this implementation to fix basename
duplication problem:
import { toRelativeUrl } from '@okta/okta-auth-js';
const restoreOriginalUri = async (_oktaAuth, originalUri) => {
const basepath = history.createHref({});
const originalUriWithoutBasepath = originalUri.replace(basepath, '/');
history.replace(toRelativeUrl(originalUriWithoutBasepath, window.location.origin));
};
From version 4.0, the Security component starts to explicitly accept oktaAuth instance as prop to replace the internal authService
instance. You will need to replace the Okta Auth SDK related configurations with a pre-initialized oktaAuth instance.
@okta/okta-auth-js
is now a peer dependency for this SDK. You must add @okta/okta-auth-js
as a dependency to your project and install it separately from @okta/okta-react
.<Security>
still accept onAuthRequired as a prop.import { OktaAuth } from '@okta/okta-auth-js';
import { Security } from '@okta/okta-react';
const oktaAuth = new OktaAuth(oidcConfig);
export default () => (
<Security oktaAuth={oktaAuth} onAuthRequired={customAuthHandler}>
// children component
</Security>
);
The authService
module has been removed since version 4.0. The useOktaAuth hook and withOktaAuth HOC are exposing oktaAuth
instead of authService
.
Replace authService
with oktaAuth
when use useOktaAuth
import { useOktaAuth } from '@okta/okta-react';
export default () => {
const { oktaAuth, authState } = useOktaAuth();
// handle rest component logic
};
Replace props.authService
with props.oktaAuth
when use withOktaAuth
import { withOktaAuth } from '@okta/okta-react';
export default withOktaAuth((props) => {
// use props.oktaAuth
});
The oktaAuth
instance exposes similar public methods to handle logic for the removed authService
module.
login
is removed
This method called onAuthRequired
, if it was set in the config options, or redirect
if no onAuthRequired
option was set. If you had code that was calling this method, you may either call your onAuthRequired
function directly or signInWithRedirect
.
redirect
is replaced by signInWithRedirect
logout
is replaced by signOut
logout
accepted either a string or an object as options. signOut accepts only an options object.
If you had code like this:
authService.logout('/goodbye');
it should be rewritten as:
oktaAuth.signOut({ postLogoutRedirectUri: window.location.origin + '/goodbye' });
Note that the value for postLogoutRedirectUri
must be an absolute URL. This URL must also be on the "allowed list" in your Okta app's configuration. If no options are passed or no postLogoutRedirectUri
is set on the options object, it will redirect to window.location.origin
after sign out is complete.
getAccessToken
and getIdToken
have been changed to synchronous methods
With maintaining in-memory AuthState since Okta Auth SDK version 4.1, token values can be accessed in synchronous manner.
handleAuthentication
is replaced by handleLoginRedirect
handleLoginRedirect
is called by the OktaLoginCallback
component as the last step of the login redirect authorization flow. It will obtain and store tokens and then call restoreOriginalUri
which will return the browser to the originalUri
which was set before the login redirect flow began.
authState
related methods have been collected in Okta Auth SDK AuthStateManager
authService.updateAuthState
to oktaAuth.authStateManager.updateAuthState
authService.getAuthState
to oktaAuth.authStateManager.getAuthState
on
to oktaAuth.authStateManager.subscribe
clearAuthState
, emitAuthState
and emit
have been removedBy default isAuthenticated
will be true if both accessToken and idToken are valid
If you have a custom isAuthenticated
function which implements the default logic, you should remove it.
getTokenManager
has been removed
You may access the TokenManager
with the tokenManager
property:
const tokens = oktaAuth.tokenManager.getTokens();
See breaking changes for version 3.0
The 1.x series for this SDK required the use of react-router. These instructions assume you are moving to version 2.0 of this SDK and are still using React Router (v5+)
The <Security>
component is now a generic (not router-specific) provider of Okta context for child components and is required to be an ancestor of any components using the useOktaAuth
hook, as well as any components using the withOktaAuth
Higher Order Component.
Auth.js
has been renamed AuthService.js
.
The auth
prop to the <Security>
component is now authService
. The other prop options to <Security>
have not changed from the 1.x series to the 2.0.x series
This SDK now provides authentication information via React Hooks (see useOktaAuth). If you want a component to receive the auth information as a direct prop to your class-based component, you can use the withOktaAuth
wrapper where you previously used the withAuth
wrapper. The exact props provided have changed to allow for synchronous access to authentication information. In addition to the authService
object prop (previously auth
), there is also an authState
object prop that has properties for the current authentication state.
.isAuthenticated()
, .getAccessToken()
, and .getIdToken()
inside a componentTwo complications of the 1.x series of this SDK have been simplified in the 2.x series:
null
, true
, and false
.To resolve these the authService
object holds the authentication information and provides it synchronously (following the first async determination) as an authState
object. While waiting on that first determination, the authState
object is null. When the authentication updates the authService object will emit an authStateChange
event after which a new authState object is available.
Any component that was using withAuth()
to get the auth
object and called the properties above has two options to migrate to the new SDK:
withAuth()
with withOktaAuth(), and replace any of these asynchronous calls to the auth
methods with the values of the related authState properties.
ORwithAuth()
and instead use the useOktaAuth() React Hook to get the authService and authState objects. Any use of the auth
methods (.isAuthenticated()
, .getAccessToken()
, and .getIdToken()
) should change to use the already calculated properties of authState.To use either of these options, your component must be a descendant of a <Security>
component, in order to have the necessary context.
These changes should result in less complexity within your components as these values are now synchronously available after the initial determination.
If you need access to the authService
instance directly, it is provided by withOktaAuth() as a prop or is available via the useOktaAuth() React Hook. You can use the examples in this README to see how to use authService to perform common tasks such as login/logout, or inspect the provided <LoginCallback>
component to see an example of the use of the authService
managing the redirect from the Okta site.
LoginCallback
.isAuthenticated()
, .getAccessToken()
, and .getIdToken()
.We welcome contributions to all of our open-source packages. Please see the contribution guide to understand how to structure a contribution.
We use yarn for dependency management when developing this package:
yarn install
Command | Description |
---|---|
yarn install |
Install dependencies |
yarn start |
Start the sample app using the SDK |
yarn test |
Run unit and integration tests |
yarn lint |
Run eslint linting tests |