I have finally found an approach that seems to work. I’m describing it here to preserve the context of the original topic. I’m contemplating the creation of a new topic that describes the more general problem it solves (passing parameters in the redirect URL).
This is all in the context of React front ends that rely on the useAuth0
hook.
I invite constructive feedback about issues this approach might create, especially around security. If sensitive information is being exchanged, then it might make sense to encrypt or hash the appStateJSON
or the values it contains.
The general approach is:
Preparing loginWithRedirect
:
-
Assemble the parameters to be passed in a Javascript literal object.
In this example, I pass principalID
, isNewUser
, localEmail
, and localName
. These capture application state that needs to be preserved across the redirect.
const appState = {
principalID: principalID,
isNewUser: isNewUser,
email: localEmail,
name: localName,
}
-
Use JSON.stringify
to obtain a JSON-encoded string. I call this “appStateJSON”. I use encodeURIComponent
to sanitize this for use in a URL.
const appStateJSON = encodeURIComponent(JSON.stringify(appState))
-
In the “options” of the various redirect Auth0 calls, add a parameter binding with name “appState” and whose value is “appStateJSON” (formed above).
const options = {
appState: {appStateJSON: appStateJSON},
redirectUri: redirectURI,
login_hint: login_hint,
prompt: 'login',
}
loginWithRedirect(options);
Handling the inbound redirect after login:
-
I modified the current recommended code for Auth0ProviderWithHistory
as follows:
const onRedirectCallback = (appState) => {
if (appState) {
const appStateJSON = appState.appStateJSON;
const queryString = `?appStateJSON=${appStateJSON}`
window.location.search = queryString;
}
history.push(appState?.returnTo || window.location.pathname);
};
This pushes appStateJSON onto the window.location.search
for use later.
-
I add the following at or near the top of the React app that receives the redirect:
const queryParameters = new URLSearchParams(window.location.search);
const appStateJSON = queryParameters.get("appStateJSON")
const transientStateFrom_ = (anAppStateJSON) => {
const answer = {}
if (anAppStateJSON) {
// Parse and handle incoming redirect state
const redirectState = JSON.parse(anAppStateJSON);
// Destructure redirectState
answer.principalID = redirectState.principalID;
answer.name = redirectState.name;
answer.email = redirectState.email;
answer.isNewUser = redirectState.isNewUser
}
return answer
};
const transientState = useMemo(
() => transientStateFrom_(appStateJSON),
[
appStateJSON
]
);
I store the bindings from the redirect call in an JS object called transientState
. I have a convention of using a trailing underscore (“_”) to indicate that a parameter is expected. The transientFromAppState_
arrow function parses the incoming appStateJSON
and destructures the values I need into transientState
. Since this do not change (ever!), I use the useMemo
hook to avoid thrashing on each render.
I haven’t found a “destructuring” incantation (in Javascript) that works, so I’ve open coded it.
With this furniture in place, the React app can collect whatever it needs from transientState
after a redirect.
In this example, ‘principalID’ is a React state variable created as follows:
const [principalID, setPrincipalID] = useState(null);
Here is a useEffect
hook that reconstructs the principalID
state on redirect:
useEffect(
() => {
if ( (principalID === null)
&& (transientState)
&& (transientState.principalID)) {
setPrincipalID(transientState.principalID)
}
},
[
principalID,
transientState,
setPrincipalID,
]
);
So far as I know, this approach is able to pass through arbitrary state (including embedded objects and such). I intentionally chose structures that do not require special magic for JSON.stringify
and JSON.parse
.
I’ve been using this code for a few weeks now and it seems to work fine.
Developers who are using Auth0 from React apps will surely need this mechanism. I’m happy to create a new topic with just this response if the community thinks it will be helpful.
I strongly encourage the Auth0 team to provide something along these lines in the technical documentation. It took me months to finally hack together something that works.