React SDK
camel-accounts-react is a drop-in popup login widget: a styled button, a context provider that stores tokens, and a hook for reading the user. It handles PKCE, state validation, and the code exchange for you.
npm install camel-accounts-reactHow it works
login()generates a PKCE pair and opens a popup to/oauth/authorize- The user signs in and consents inside the popup (Camel Accounts' own UI)
- The popup lands on
/widget-callbackon the issuer andpostMessages the authorization code back to your window - The SDK exchanges the code at
/oauth/tokenand closes the popup - Tokens + userinfo are available via
useCamelAuth()
Because the callback is hosted on the issuer itself (https://accounts.camelcreatives.com/widget-callback), your app doesn't need its own callback route.
1. Register the client
Your client must include the widget callback in its registered redirect URIs:
https://accounts.camelcreatives.com/widget-callbackAdd it via the Developer Console (or API) alongside any other URIs you use:
{
"redirect_uris": [
"https://yourapp.com/callback",
"https://accounts.camelcreatives.com/widget-callback"
],
"allowed_scopes": ["openid", "profile", "email"]
}2. Wrap your app
The provider takes a single config object:
import { CamelAuthProvider } from "camel-accounts-react";
import "camel-accounts-react/style.css";
function App() {
return (
<CamelAuthProvider
config={{
issuer: "https://accounts.camelcreatives.com",
clientId: "your-app-web",
scopes: ["openid", "profile", "email"],
}}
>
<YourRoutes />
</CamelAuthProvider>
);
}Config reference
| Prop | Type | Default | Description |
|---|---|---|---|
issuer | string | (required) | Camel Accounts base URL |
clientId | string | (required) | Your registered client_id |
scopes | string[] | ["openid", "profile", "email"] | OAuth scopes to request |
redirectPath | string | "/widget-callback" | Path on the issuer used as the popup callback |
storage | "memory" | "localStorage" | "memory" | Where tokens persist |
Storage modes
"memory"(default) — tokens live in React state only; a page refresh signs the user out. Best when your own backend session is the real session."localStorage"— tokens survive refreshes undercamel_auth_tokens. Only use this if the browser is fully trusted with the tokens; prefer memory + your own httpOnly-cookie session where possible.
3. Add the button and read state
import { CamelLoginButton, useCamelAuth } from "camel-accounts-react";
function LoginSection() {
const { user, tokens, authenticated, loading, login, logout, getAccessToken } =
useCamelAuth();
if (loading) return <span>Loading…</span>;
if (authenticated) {
return (
<div>
<span>Signed in as {user?.name ?? user?.email ?? user?.sub}</span>
<button onClick={logout}>Sign out</button>
</div>
);
}
return <CamelLoginButton onClick={login} />;
}useCamelAuth() returns
| Property | Type | Description |
|---|---|---|
user | CamelAuthUser | null | Claims from /oauth/userinfo |
tokens | CamelAuthTokens | null | { accessToken, refreshToken, expiresIn, scope } |
authenticated | boolean | True while an access token exists |
loading | boolean | Initial token restoration in progress |
login | () => void | Opens the popup flow |
logout | () => void | Clears tokens + user state locally |
getAccessToken | () => string | null | Current access token for calling APIs |
Types
interface CamelAuthUser {
sub: string;
name?: string;
email?: string;
email_verified?: boolean;
phone_number?: string;
phone_number_verified?: boolean;
picture?: string;
}
interface CamelAuthTokens {
accessToken: string;
refreshToken: string;
expiresIn: number;
scope: string;
}<CamelLoginButton> props
| Prop | Type | Default |
|---|---|---|
onClick | () => void | — (pass login) |
disabled | boolean | false |
label | string | "Sign in with Camel Accounts" |
logo | string | — (built-in camel mark) |
Pass logo to show your own mark on the left (Google-style), e.g. <CamelLoginButton onClick={login} logo="/logo.jpeg" />.
Styling: import camel-accounts-react/style.css for the default look; the button uses BEM class .camel-login-btn, so you can override it freely.
Calling your own backend
Pass the access token as a bearer token; verify it server-side as described in Backend Token Verification:
const res = await fetch("/api/orders", {
headers: { Authorization: `Bearer ${getAccessToken()}` },
});
if (res.status === 401) {
// access token expired (15 min TTL) — exchange the refresh token,
// or simply have the user click Sign in again
}Heads-up: the SDK does not auto-refresh. Access tokens live 15 minutes; either call
/oauth/tokenwithgrant_type=refresh_tokenyourself when a 401 arrives, or treat the widget as a short-lived identity assertion in front of your own cookie session.
Popup notes
- The popup is 500×600 and centered; browsers block popups not triggered by a direct user gesture — always call
login()from an event handler. logout()only clears local state. To end the Camel Accounts browser session itself, send the user to the Cockpit and sign out there.