Skip to content

feat[client-js]: Adds React Router v8 sample app for @okta/okta-react-client-js - #322

Open
BenjaminTruong-okta wants to merge 5 commits into
feat/client-js-opt-in-supportfrom
feat/client-js-router-v8-sample-app
Open

feat[client-js]: Adds React Router v8 sample app for @okta/okta-react-client-js#322
BenjaminTruong-okta wants to merge 5 commits into
feat/client-js-opt-in-supportfrom
feat/client-js-router-v8-sample-app

Conversation

@BenjaminTruong-okta

@BenjaminTruong-okta BenjaminTruong-okta commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Exercises the opt-in loader factories (createFetchLoader, createTokenLoader, createLoginCallbackLoader) end-to-end against a real Okta org, and resets the sign-in flow's stuck inProgress state on a back/forward-cache restore so an abandoned redirect doesn't block starting a new one.

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Adding Tests
  • Build related changes
  • CI related changes
  • Documentation changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Reviewers

{
path: '/resource',
element: <Resource />,
loader: createFetchLoader(fetchClient, () => userInfoUrl),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using the userInfo URL is a bit confusing. It's an OAuth endpoint, so a developer should use OAuth2 to request it, not a FetchClient. Maybe instead use a static JSON blob and can be hosted via vite to illustrate this?

Comment thread test/apps/client-js-router-v8-app/tsconfig.json
Comment thread test/apps/client-js-router-v8-app/vite.config.js Outdated
// ever reset it on completion or failure, neither of which runs for an abandoned redirect. That stuck
// state makes every later `orchestrator.getToken()` call throw `flow already in progress` immediately.
// `pageshow`'s `persisted` flag is the standard signal for a bfcache restore, so reset here.
window.addEventListener('pageshow', (event) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was an issue you noticed or something Claude came up with?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran into it by hitting the back button in the middle of the sign in flow, and could not sign in again after that. This is Claude's explanation and fix for it.

import { orchestrator, signOutFlow } from './auth';

const Home: React.FC = () => {
const [credential, setCredential] = React.useState<Credential | null>(null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of storing a Credential instance on the App State? I think the only reason you should do this is if you are rendering the token

Comment on lines +26 to +50
export const router = createBrowserRouter([
{
path: '/',
element: <Home />,
errorElement: <ErrorBoundary />,
},
{
path: '/protected',
element: <Protected />,
loader: createTokenLoader(orchestrator),
errorElement: <ErrorBoundary />,
},
{
path: '/resource',
element: <Resource />,
loader: createFetchLoader(fetchClient, () => '/resource.json'),
errorElement: <ErrorBoundary />,
},
{
path: '/login/callback',
element: <LoginCallback />,
loader: createLoginCallbackLoader(orchestrator),
errorElement: <ErrorBoundary />,
},
]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const router = createBrowserRouter([
{
path: '/',
element: <Home />,
errorElement: <ErrorBoundary />,
},
{
path: '/protected',
element: <Protected />,
loader: createTokenLoader(orchestrator),
errorElement: <ErrorBoundary />,
},
{
path: '/resource',
element: <Resource />,
loader: createFetchLoader(fetchClient, () => '/resource.json'),
errorElement: <ErrorBoundary />,
},
{
path: '/login/callback',
element: <LoginCallback />,
loader: createLoginCallbackLoader(orchestrator),
errorElement: <ErrorBoundary />,
},
]);
const tokenLoader = createTokenLoader(orchestrator);
const fetchLoader = createFetchLoader(fetchClient);
const callbackLoader = createLoginCallbackLoader(orchestrator);
export const router = createBrowserRouter([
{
path: '/',
element: <Home />,
errorElement: <ErrorBoundary />,
},
{
path: '/protected',
element: <Protected />,
loader: tokenLoader,
errorElement: <ErrorBoundary />,
},
{
path: '/resource',
element: <Resource />,
loader: fetchLoader('/resource.json'),
errorElement: <ErrorBoundary />,
},
{
path: '/login/callback',
element: <LoginCallback />,
loader: callbackLoader,
errorElement: <ErrorBoundary />,
},
]);

…/client-js

Exercises the opt-in loader factories (createFetchLoader, createTokenLoader,
createLoginCallbackLoader) end-to-end against a real Okta org, and resets the
sign-in flow's stuck `inProgress` state on a back/forward-cache restore so an
abandoned redirect doesn't block starting a new one.

Co-Authored-By: Claude Code
…rinfo endpoint

Hand-building the userinfo URL bypassed the SDK's own OAuth2Client/Credential
userInfo() methods, which resolve the endpoint from OIDC discovery metadata.
Swap the sample's /resource route to a plain static JSON file so it just
demonstrates createFetchLoader without duplicating SDK logic.
…m vite.config.js

A clean build doesn't reproduce this react-router warning - it was vestigial
from an earlier scaffolding iteration. Restore the unconditional onwarn throw,
matching test-harness-app's convention.
createFetchLoader/createTokenLoader now bind to their client/orchestrator up
front and return a helper you call from within your own loader, rather than
being usable as a loader directly. Adopts createLoadersFromOrchestrator for
the token/login-callback pair, per the updated README's wiring example.
@BenjaminTruong-okta
BenjaminTruong-okta force-pushed the feat/client-js-router-v8-sample-app branch from 7457171 to e912e08 Compare August 18, 2026 04:49
Home never renders the token - it only needs a signed-in/signed-out
boolean to pick a button, and idToken to end the Okta session. Holding
the Credential object itself in state has no purpose here and risks
going stale (e.g. after signOut's credential.remove()); fetch it fresh
via Credential.getDefault() at the point of use instead.
@BenjaminTruong-okta
BenjaminTruong-okta marked this pull request as ready for review August 28, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants