React bindings for @openid/appauth
npm install @epfl-si/react-appauthAn unopinionated React binding for @openid/appauth.
- Browser-side OpenID-Connect implementation, meaning all the backend server has left to do is validate bearer tokens with the OIDC identity provider (IdP)
- Redirects the browser to the authorization server for the login operation
- When redirected back, consumes (and cleans out) the code=, state=, error= and session_state= parts from the URL bar, regardless of whether they are found before or after the hash mark and whether the login operation was successful
- Obtains OAuth2 tokens using fetch, not jQuery
- Schedules access token refresh a few seconds before it expires
- Brings out the best in @openid/appauth's underlying feature set
- Uses the modern and secure OAuth2 authorization code flow
- (Untested) Supports extra redirect parameters, to activate features such as user consent in authentication servers that support them
- PKCE support
- Supports cookie-less, local-storage-less operation
- This is in fact the default mode (unlike in @openid/appauth)
- Obviously, this has a cost with respect to security: no state= validation, PKCE is disabled
- Straightforward, unopinionated React bindings
- to pass in configuration and consume “back-office” events (i.e. access tokens)
- useOpenIDConnectContext React hook to consume “front-office” events (for the appearance of widgets such as the login button and the “hello, ${user}” widget)
- Fully unmount-proof: when the unmounts or changes its props, pending token refresh timers get canceled and callbacks stop calling back.
- Demo app
- ... With a Ruby back-end. But easy enough to set up with no prior knowledge
- Comes with Keycloak-in-a-container, fully configured out of the box with test realm and user
- Tested with Keycloak (see above)
The various React components (“widgets”) discussed below must be placed within an near the top of your app, for instance:
``jsx
import { OIDCContext } from "@epfl-si/react-appauth";
export function App() {
return
client = { { clientId: "myclient" } }
onNewToken={(token) => setFetchHeader("Authorization", Bearer ${token})}
onLogout={() => setFetchHeader("Authorization", null)}>
;
}
function setFetchHeader (header, value) {
// ... Integrate with your backend API code here
}
`
@epfl-si/react-appauth exports a ready-made React element, but you could just as well reimplement it like this:
`jsx
import { useOpenIDConnectContext, StateEnum as OIDCState } from "@epfl-si/react-appauth";
export function LoginButton () {
const oidc = useOpenIDConnectContext();
if (oidc.state === OIDCState.InProgress) {
return ;
}
const loggedIn = oidc.state === OIDCState.LoggedIn,
action = loggedIn ? "Logout" : "Login",
label = (oidc.error === undefined) ? action : [action, ⚠],
tooltip = oidc.error ? ${oidc.error} :
loggedIn ? "Log out" : "Log in with OpenID-Connect";
function onClick() {
if (loggedIn) {
oidc.logout();
} else {
oidc.login();
}
}
return ;
}
`
If you would like a “hello, user” widget, you will find a couple of building parts in src/sundry-widgets.tsx that could come in handy. For instance:
`jsx
import { IfOIDCState, StateEnum, LoggedInUser } from "@epfl-si/react-appauth";
function HelloUser () { Welcome,
return
}
`
But again, you could just implement it yourself out of useOIDCContext:
`jsx
function HelloUser () {
const oidc = useOpenIDConnectContext();
if (! oidc.idToken) return <>>;
return
Welcome, { oidc.idToken.preferred_username! }
`