Hi I'm trying to use react-oidc-context to authenticate with auth0 and I have one problem. After successful authentication with auth0 I always get redirected back to the route I was on when I clicked the login button. The app has 2 unprotected routes /login and /public and I could be on either of those when I click login in the page header. I always get redirected to whichever of these pages I was on initially. The login button calls login from the useReactOidc hook. I want to be redirected to the /home route and have tried doing a history.push('/home') before calling login() but no luck.
In the case of the login page I could include logic to redirect to /home if we're logged in as we don't need to be there, but in the case of /public you could have a reason to be on the page if logged in or not.
I'm not really getting how the redirect_uri in the configuration fits into this, I've tried setting up a route for this but it never matches (<SecuredRoute path='/authentication/callback' ...). If anyone has any suggestions for how to redirect to a specific route upon successful login it would be greatfully appreciated, hopefully I've just missed something obvious. Thanks and here's the code:
configuration.js
const tenancy = 'https://dev-xxxxxxxxxxxxxx.eu.auth0.com';
const clientId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
export const configuration = {
client_name: 'My App',
client_id: clientId,
scope: 'openid profile',
response_type: 'code',
authority: `${tenancy}`,
redirect_uri: 'http://localhost:3000/authentication/callback',
// post_logout_redirect_uri: 'http://localhost:3000', // Auth0 uses returnTo
silent_redirect_uri: 'http://localhost:3000/authentication/silent_callback',
automaticSilentRenew: true,
loadUserInfo: true,
metadata: {
issuer: `${tenancy}/`,
authorization_endpoint: `${tenancy}/authorize`,
token_endpoint: `${tenancy}/oauth/token`,
userinfo_endpoint: `${tenancy}/userinfo`,
end_session_endpoint: `${tenancy}/v2/logout?returnTo=${encodeURIComponent('http://localhost:3000')}&client_id=${clientId}`
}
};
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { AuthenticationProvider, oidcLog, InMemoryWebStorage } from '@axa-fr/react-oidc-context';
import { configuration } from './configuration';
ReactDOM.render(
<React.StrictMode>
<AuthenticationProvider
configuration={configuration}
loggerLevel={oidcLog.DEBUG}
isEnabled={true}
callbackComponentOverride={() => (<></>)}
UserStore={InMemoryWebStorage}>
<App />
</AuthenticationProvider>
</React.StrictMode>,
document.getElementById('root')
);
App.js
import React from 'react';
import { useReactOidc } from '@axa-fr/react-oidc-context';
import { BrowserRouter as Router, Link, Redirect, Route, Switch } from 'react-router-dom';
import SecuredRoute from './SecuredRoute';
import { PageHeader, Space } from 'antd';
import './App.css';
const Login = () => <h3>Login Page - use the Login button top right</h3>;
const Home = () => <h3>Home Page</h3>;
const Public = () => <h3>Public Page</h3>;
const Header = () => {
const { oidcUser, login, logout } = useReactOidc();
return (
<PageHeader style={{ textAlign: "right", margin: "8px" }}>
{ oidcUser ? (
<button onClick={logout}>Log Out</button>
) : (
<button onClick={login}>Log in</button>
)}
</PageHeader>
);
};
const DefaultPage = () => {
const { oidcUser } = useReactOidc();
return (
oidcUser ? (
<Redirect to="/home" />
) : (
<Redirect to="/login" />
)
);
};
const App = () => {
return (
<div className="App">
<Router>
<Header />
<Switch>
<Route path='/' exact component={DefaultPage} />
<Route path='/login' component={Login} />
<Route path='/public' component={Public} />
<SecuredRoute path='/home' component={Home} />
<SecuredRoute path='/authentication/callback' component={Home} />
</Switch>
<Space>
<Link to="/home">Home Page (you must be logged in to access this)</Link>
<br/>
<Link to="/public">Public Page (you don't need to be logged in to access this)</Link>
</Space>
</Router>
</div>
);
};
export default App;
SecuredRoute.js
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { useReactOidc } from '@axa-fr/react-oidc-context';
const SecuredRoute = ({ component: Component, ...rest }) => {
const { oidcUser } = useReactOidc();
return (
<Route {...rest} render={(props) => (
oidcUser
? <Component {...props} />
: <Redirect to={{ pathname: '/', state: { from: props.location }}} />
)} />
);
};
export default SecuredRoute;
Versions
"dependencies": {
"@axa-fr/react-oidc-context": "^3.1.6",
"@axa-fr/react-oidc-context-fetch": "^3.1.6",
"@testing-library/jest-dom": "^5.11.5",
"@testing-library/react": "^11.1.1",
"@testing-library/user-event": "^12.2.0",
"antd": "^4.8.0",
"oidc-client": "^1.10.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.0",
"web-vitals": "^0.2.4"
},
I can't really help you with react-oidc-context, so this may only be a partial answer. It feels to me like you need some control over the following areas:
REQUIREMENT 1: CONTROL BEFORE REDIRECTING
The ability to do things like store the user's state and location in HTML5 storage before a redirect.
REQUIREMENT 2: CONTROL AFTER HANDLING A LOGIN RESPONSE
The ability to restore state from storage if needed, then control the post login location.
REQUIREMENT 3: CONTROL OVER EXPIRY
If a user leaves their browser running overnight, and tokens have expired, the UI may need to trigger a login redirect as the result of a failed API call.
OIDC CLIENT
This library fully supports the above behaviour. If you cannot get the same control with react-oidc-context, then one solution that will work is to use the library directly - it may be one less layer to manage.
EXAMPLE CODE
I would aim to have a couple of functions as in this code of mine that give you the control you need before and after logins.
REACT CODE
My personal preference is to use React only for views, and plain classes for API calls and Open Id Connect. Here also is a sample of mine here that follows this approach, though this sample is a little complicated.