@beamarco/auth-sdk
v0.1.23
Published
Beamar Auth SDK - Embed authentication in your React apps with Beamar Auth
Maintainers
Readme
@beamarco/auth-sdk
Embed authentication in your React apps with Beamar Auth. Works with the Beamar auth-service backend.
Installation
npm install @beamarco/auth-sdkQuick Start
1. Wrap your app with BeamarAuthProvider
import { BeamarAuthProvider } from '@beamarco/auth-sdk'
const config = {
apiUrl: 'https://auth.creztu.com', // or your auth-service URL
appName: 'my-app', // e.g. GOMEDI, developers-portal
// googleClientId: optional — only if you use your own client-side Google button (see "Google auth options" below)
}
function App() {
return (
<BeamarAuthProvider config={config}>
<YourApp />
</BeamarAuthProvider>
)
}2. Use SignedIn / SignedOut for conditional rendering
import { SignedIn, SignedOut, SignIn, SignUp } from '@beamarco/auth-sdk'
function App() {
return (
<>
<SignedIn>
<Dashboard />
</SignedIn>
<SignedOut>
<SignIn signUpUrl="/sign-up" />
</SignedOut>
</>
)
}3. Add routes (e.g. with React Router)
import { Routes, Route, Navigate } from 'react-router-dom'
import {
BeamarAuthProvider,
SignIn,
SignUp,
SignedIn,
SignedOut,
useAuth,
} from '@beamarco/auth-sdk'
function ProtectedRoute({ children }) {
const { isLoaded, isSignedIn } = useAuth()
if (!isLoaded) return <Spinner />
if (!isSignedIn) return <Navigate to="/sign-in" replace />
return children
}
function AuthRoute({ children }) {
const { isLoaded, isSignedIn } = useAuth()
if (!isLoaded) return <Spinner />
if (isSignedIn) return <Navigate to="/" replace />
return children
}
function App() {
return (
<BeamarAuthProvider config={config}>
<Routes>
<Route path="/" element={
<ProtectedRoute><Home /></ProtectedRoute>
} />
<Route path="/sign-in" element={
<AuthRoute><SignIn /></AuthRoute>
} />
<Route path="/sign-up" element={
<AuthRoute><SignUp /></AuthRoute>
} />
</Routes>
</BeamarAuthProvider>
)
}API
Components
| Component | Description |
|-----------|-------------|
| BeamarAuthProvider | Wrap your app; provides auth context |
| SignIn | Email/password + Google sign-in form (Google uses redirect flow by default) |
| SignUp | Email/password + Google sign-up form (Google uses redirect flow by default) |
| SignedIn | Renders children only when signed in |
| SignedOut | Renders children only when signed out |
| SignInButton | Link/button to sign-in page |
| SignUpButton | Link/button to sign-up page |
| UserButton | User avatar/menu with sign out |
| ProtectedRoute | Redirects to sign-in when not authenticated (fallbackRedirectUrl, loading props) |
| TenantSelector | Full-page organization picker (same-domain or popup for cross-domain; see Tenant selection) |
| OrgSwitcher | Compact dropdown for header/sidebar to switch organizations |
| TenantGuard | Blocks children until a tenant is selected; shows TenantSelector if none |
| TenantSelectCallback | Minimal callback page for popup flow; route at e.g. /auth/tenant-selected |
| HostedTenantSelectRedirect | Redirects to auth-service hosted tenant selection (full redirect, cross-domain) |
Hooks
| Hook | Description |
|------|-------------|
| useAuth() / useBeamarAuth() | Full auth context: signIn, signOut, signInWithGoogle, signUpWithGoogle, signInWithMicrosoft, signUpWithMicrosoft, getGoogleOAuthRedirectUrl, getMicrosoftOAuthRedirectUrl, getToken, validateEmail, user, isLoaded, isSignedIn, apiUrl, appName; tenant: listTenants, getCurrentTenant, selectTenant, createTenant; org members: listTenantMembers, inviteTenantMember, updateTenantMemberRole, removeTenantMember, requestTenantMemberPasswordReset |
| useUser() | Current user and loading state |
Note: useBeamarAuth is an alias for useAuth. AuthContext is exported for advanced usage (e.g. custom hooks).
validateEmail(loginId) checks if an email is already registered (e.g. for sign-up "email exists" checks).
getToken() returns a short-lived Bearer JWT for calling downstream services (e.g. screening-service). Only available for system-admin sessions; returns null otherwise. See Calling backend services below.
Component props
| Component | Key props |
|-----------|-----------|
| SignIn | afterSignInUrl, showGoogle, showMicrosoft, showSignUp, signUpUrl, forgotPasswordUrl, onSuccess, styles, children (render prop) |
| SignUp | afterSignUpUrl, showGoogle, showMicrosoft, signInUrl, onSuccess, styles, children (render prop) |
| SignInButton / SignUpButton | children, signInUrl/signUpUrl, fallbackRedirectUrl, style, className |
| UserButton | children, fallback, style, className |
| ProtectedRoute | fallbackRedirectUrl, loading |
| OrgSwitcher | usePopupOpener, selectionCallbackPath, showCreate, onSwitch, onError |
| TenantSelector | usePopupOpener, selectionCallbackPath, onSelected |
| TenantGuard | usePopupOpener, selectionCallbackPath, onSelected |
| HostedTenantSelectRedirect | redirectPath, fallback |
Render props: SignIn and SignUp accept a children function for fully custom layouts:
<SignIn>
{({ signIn, signInWithGoogle, error, isLoading }) => (
<YourCustomForm
onEmailSubmit={signIn}
onGoogleClick={signInWithGoogle}
error={error}
loading={isLoading}
/>
)}
</SignIn>Google auth – redirect helpers
| Function | Description |
|----------|-------------|
| getGoogleAuthUrl(options) | Build URL to hosted /sign-in page (email+password form + Google button) |
| redirectToGoogleSignIn(options) | Redirect to hosted sign-in page |
| redirectToGoogleSignUp(options) | Redirect to hosted sign-up page |
Two different redirect flows:
getGoogleAuthUrl/redirectToGoogleSignIn→ auth-service/sign-in(hosted page with full form + Google). Best for custom pages; no Google setup needed.getGoogleOAuthRedirectUrl(redirectUri)(fromuseAuth()) → auth-service/auth/google(two-step OAuth: redirect to Google, callback, establish). Google-only flow; no hosted page. Use when you want a "Sign in with Google" link that goes directly to OAuth.
Microsoft auth – redirect helpers
| Function | Description |
|----------|-------------|
| getMicrosoftAuthUrl(options) | Build URL to start Microsoft Entra ID OAuth (/auth/microsoft) |
| redirectToMicrosoftSignIn(options) | Redirect browser to Microsoft OAuth |
Config
interface BeamarAuthConfig {
apiUrl: string // auth-service base URL
appName: string // application name (multi-tenant)
googleClientId?: string // only for Option B (your own client-side Google button)
parentUrl?: string // base URL for password reset emails (e.g. your app origin). Auth-service links back here.
}BeamarAuthProvider also supports publishableKey (e.g. beamar_live_developers-portal) instead of config; apiUrl is then taken from VITE_AUTH_SERVICE_URL.
Google auth: two options
You can add Google sign-in in two ways:
Option A: Redirect to hosted auth (recommended, no Google setup)
No Google Cloud Console setup needed. The <SignIn> and <SignUp> components use this by default. Their "Sign in with Google" button redirects to the auth-service's hosted page, where the Google button runs on a domain already authorized in Google. No "authorized JavaScript origins" errors.
For custom pages, use the redirect helpers:
import { redirectToGoogleSignIn, getGoogleAuthUrl } from '@beamarco/auth-sdk'
// Option 1: Button with redirect
function CustomLoginPage() {
return (
<button
onClick={() => redirectToGoogleSignIn({
apiUrl: 'https://auth.s.beamar.co',
appName: 'my-app',
redirectUri: '/dashboard', // where to land after auth
})}
>
Sign in with Google
</button>
)
}
// Option 2: Plain link (no JS needed for navigation)
function CustomLoginPage() {
const googleUrl = getGoogleAuthUrl({
apiUrl: 'https://auth.s.beamar.co',
appName: 'my-app',
redirectUri: '/dashboard',
mode: 'signin', // or 'signup'
})
return <a href={googleUrl}>Sign in with Google</a>
}Flow: your button/link → auth-service /sign-in (hosted page with Google button) → user signs in with Google → redirect back to your redirectUri with session cookie set. Your app can then use BeamarAuthProvider and useUser() to read the session.
Option B: Your own Google client (client-side button)
If you prefer to embed the Google button directly in your app (e.g. with @react-oauth/google), you can:
- Add your app domain to Authorized JavaScript origins in Google Cloud Console
- Add
googleClientIdto your config - Use your Google client to get the credential, then call
signInWithGoogle({ credential, clientId })orsignUpWithGoogle({ credential, clientId })fromuseAuth()
import { useAuth } from '@beamarco/auth-sdk'
import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google'
function CustomGoogleButton() {
const { signInWithGoogle } = useAuth()
return (
<GoogleOAuthProvider clientId="your-client-id.apps.googleusercontent.com">
<GoogleLogin
onSuccess={(res) => {
if (res?.credential && res?.clientId) {
signInWithGoogle({ credential: res.credential, clientId: res.clientId })
}
}}
onError={() => {}}
/>
</GoogleOAuthProvider>
)
}Microsoft Entra ID OAuth
Configure auth-service with MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET, and optional MICROSOFT_TENANT_ID (default common). In Azure Entra, add redirect URI {AUTH_CENTRAL_CALLBACK_URL}/auth/microsoft/callback (same base as Google’s central callback).
Redirect flow (recommended): use getMicrosoftOAuthRedirectUrl from useAuth(), or getMicrosoftAuthUrl / redirectToMicrosoftSignIn from the SDK. Enable <SignIn showMicrosoft /> and <SignUp showMicrosoft /> to show buttons. The hosted /sign-in page also shows “Sign in with Microsoft” when Microsoft env is set.
Client-side MSAL: obtain an ID token, then call signInWithMicrosoft({ tokenId }) or signUpWithMicrosoft({ tokenId }) from useAuth().
Calling backend services
For apps that need to call Beamar backend APIs (e.g. screening-service) with user context, use getToken():
import { useAuth } from '@beamarco/auth-sdk'
function ScreeningDashboard() {
const { getToken } = useAuth()
const fetchData = async () => {
const token = await getToken()
if (!token) throw new Error('Not authenticated')
const res = await fetch('https://screening.example.com/api/lists', {
headers: { Authorization: `Bearer ${token}` },
})
// ...
}
}- System-admin only:
getToken()returns a JWT only for system-admin sessions; regular app users getnull. - Backend verification: Services verify the JWT using your app's JWKS URL. Retrieve the JWKS URL from the developers portal when viewing your auth application.
- Short-lived: Tokens expire in a few minutes; call
getToken()when needed rather than caching.
Tenant / Organization selection
Multi-tenant apps require the user to pick an organization before accessing data. The SDK provides components for same-domain and cross-domain flows.
Same domain (default)
When your app and auth-service share a domain (e.g. app.creztu.com and auth.creztu.com), tenant selection uses in-app API calls.
OrgSwitcher – compact dropdown for header/sidebar:
import { OrgSwitcher } from '@beamarco/auth-sdk'
// In your layout or header
<OrgSwitcher
onSwitch={() => refetchData()}
onError={(msg) => toast.error(msg)}
/>TenantSelector – full-page organization picker:
import { TenantSelector } from '@beamarco/auth-sdk'
<TenantSelector
onSelected={() => navigate('/dashboard')}
/>TenantGuard – blocks rendering until a tenant is selected; shows TenantSelector if none:
import { TenantGuard } from '@beamarco/auth-sdk'
<TenantGuard onSelected={() => navigate('/dashboard')}>
<Dashboard />
</TenantGuard>Cross-domain: popup opener (no reload)
When your app and auth-service are on different domains, cookie-based selection can fail due to third-party cookie rules. Use the popup opener flow so selection happens on the auth domain (first-party cookies).
- Add the callback route (e.g.
/auth/tenant-selected):
import { TenantSelectCallback } from '@beamarco/auth-sdk'
// In your router
<Route path="/auth/tenant-selected" element={<TenantSelectCallback />} />- Enable popup mode with
usePopupOpener:
// OrgSwitcher in header
<OrgSwitcher
usePopupOpener
selectionCallbackPath="/auth/tenant-selected"
onSwitch={() => refetch()}
/>
// TenantSelector (full page)
<TenantSelector
usePopupOpener
selectionCallbackPath="/auth/tenant-selected"
onSelected={() => navigate('/dashboard')}
/>
// TenantGuard
<TenantGuard
usePopupOpener
selectionCallbackPath="/auth/tenant-selected"
onSelected={() => navigate('/dashboard')}
>
<Dashboard />
</TenantGuard>Flow: user picks an org → popup opens → auth-service sets the cookie → redirects to your callback → callback posts a message and closes → parent updates without reload.
Ensure /auth/tenant-selected (or your chosen path) is allowed in your app’s redirectUris in the developers portal.
Cross-domain: hosted page (full redirect)
For full redirect without popup, use HostedTenantSelectRedirect or redirectToTenantSelect:
import {
HostedTenantSelectRedirect,
redirectToTenantSelect,
getSelectTenantUrl,
} from '@beamarco/auth-sdk'
// Redirect component (e.g. in TenantGuard fallback)
<HostedTenantSelectRedirect
redirectPath="/dashboard"
fallback={<Spinner />}
/>
// Or imperative
redirectToTenantSelect({
apiUrl,
appName,
redirectUri: '/dashboard',
})
// Or as link
<a href={getSelectTenantUrl({ apiUrl, appName, redirectUri: '/dashboard' })}>
Select organization
</a>The user lands on the auth-service hosted page, selects an org, and is redirected back to your app. No popup; page reload when they return.
Tenant selection helpers
| Function | Description |
|----------|-------------|
| getSelectTenantUrl(options) | Build URL to auth-service hosted select-tenant page |
| redirectToTenantSelect(options) | Redirect browser to hosted tenant selection |
| getSelectTenantAndRedirectUrl(options) | Build URL for popup flow (internal use; components call this when usePopupOpener is true) |
Tenant selector props
| Component | Prop | Default | Description |
|----------------|-----------------------|-------------------------|------------------------------------------------------------------|
| OrgSwitcher | usePopupOpener | false | Use popup for cross-domain selection |
| OrgSwitcher | selectionCallbackPath| '/auth/tenant-selected' | Callback path when usePopupOpener is true |
| OrgSwitcher | showCreate | true | Show "Create new organization" in dropdown |
| TenantSelector | usePopupOpener | false | Same as OrgSwitcher |
| TenantSelector | selectionCallbackPath| '/auth/tenant-selected' | Same as OrgSwitcher |
| TenantGuard | usePopupOpener | false | Same as OrgSwitcher |
| TenantGuard | selectionCallbackPath| '/auth/tenant-selected' | Same as OrgSwitcher |
Tenant selection helpers
| Function | Description |
|----------|-------------|
| getSelectTenantUrl(options) | Build URL to auth-service hosted select-tenant page |
| redirectToTenantSelect(options) | Redirect to hosted select-tenant page |
| getSelectTenantAndRedirectUrl(options) | Build URL for popup flow (/tenants/select?ti=…&redirect_uri=…); used internally when usePopupOpener is true |
Hooks for tenant operations
From useBeamarAuth():
listTenants()– list orgs the user belongs togetCurrentTenant()– currently selected orgselectTenant(tenantId)– switch org (in-app API; same-domain only)createTenant(name)– create and select a new org
Organization members (team) — session + X-Beamar-App: listTenantMembers(tenantId?), inviteTenantMember({ email, role?, tenantId? }), updateTenantMemberRole({ userId, role, tenantId? }), removeTenantMember({ userId, tenantId? }), requestTenantMemberPasswordReset({ userId, tenantId?, parentUrl? }). Omit tenantId or pass 'current' for the tenant in the session. See auth-service docs/openapi-tenant-membership.yaml. Errors expose AuthError.code (e.g. INSUFFICIENT_PRIVILEGE, LAST_ADMIN).
After getSession(), user.tenantRole reflects the role in the selected tenant (from /user/preference). For roles across all orgs, use listTenants().
Tenant selection helpers
| Function | Description |
|----------|-------------|
| getSelectTenantUrl(options) | Build URL to auth-service hosted select-tenant page |
| redirectToTenantSelect(options) | Redirect browser to hosted select-tenant page |
| getSelectTenantAndRedirectUrl(options) | Build URL for popup flow (GET /tenants/select with redirect_uri); used internally when usePopupOpener is true |
For custom flows, you can use getSelectTenantAndRedirectUrl({ apiUrl, appName, tenantId, redirectUri }) to build the popup redirect URL yourself.
Configuring redirect URIs
When using the popup flow (usePopupOpener), ensure your callback path (e.g. https://your-app.com/auth/tenant-selected) is added to your application’s redirect URIs in the developers portal. The auth-service validates redirect URIs before redirecting back to your app.
Password reset
The auth-service supports email-based password reset. Use forgotPasswordUrl on SignIn to link to your custom "Forgot password?" page:
<SignIn forgotPasswordUrl="/forgot-password" />Your forgot-password page can call the auth-service to request a reset email. For direct API access without the Provider, use BeamarAuthClient:
import { BeamarAuthClient } from '@beamarco/auth-sdk'
const client = new BeamarAuthClient({ apiUrl, appName, parentUrl })
await client.requestPasswordReset(email)
// User receives email with link. Your app handles the link (e.g. /reset-password?key=xxx)
// then: await client.updatePassword(key, newPassword)BeamarAuthClient (low-level)
For non-React usage or when you need direct API access:
import { BeamarAuthClient } from '@beamarco/auth-sdk'
const client = new BeamarAuthClient({
apiUrl: 'https://auth.s.beamar.co',
appName: 'my-app',
parentUrl: 'https://myapp.com', // optional, for password reset
})
// Methods: login, register, logout, getSession, getToken, validateEmail,
// loginWithGoogle, registerWithGoogle, requestPasswordReset, updatePassword,
// listTenants, getCurrentTenant, selectTenant, createTenant,
// listTenantMembers, inviteTenantMember, updateTenantMemberRole, removeTenantMember, requestTenantMemberPasswordResetAll requests use credentials: 'include' for cookie-based sessions.
Error handling
import { AuthError } from '@beamarco/auth-sdk'
try {
await signIn({ email, password })
} catch (err) {
if (err instanceof AuthError) {
console.error(err.message, err.statusCode, err.code)
}
}Types
interface User {
id: string
loginId: string
displayName?: string
imageUrl?: string
cellphone?: string
tenants?: TenantMember[]
}
interface TenantMember {
tenant: { id: string; name?: string }
userRole?: string
}
interface LoginCredentials { email: string; password: string }
interface RegisterCredentials { email: string; password: string }
interface GoogleCredential { credential: string; clientId: string }CORS & Cookies
The auth-service uses httpOnly cookies for sessions. Ensure:
- Same origin or CORS with credentials: Your frontend and auth-service must allow
credentials: 'include'. - auth-service CORS: Set
APP_CORSenv var to include your frontend origin(s). - Cookie domain: In production, auth-service may set
domainfor cross-subdomain cookies (e.g..creztu.com).
Developers Portal & Creztu
To protect developers-portal or embed auth in the Creztu portal:
// developers-portal - Beamar Auth
import { BeamarAuthProvider, SignIn, SignedIn, SignedOut, UserButton } from '@beamarco/auth-sdk'
const config = {
apiUrl: process.env.VITE_AUTH_API_URL,
appName: 'developers-portal',
// googleClientId optional — SignIn uses redirect flow by default
}
<BeamarAuthProvider config={config}>
<SignedIn>
<UserButton />
<MainContent />
</SignedIn>
<SignedOut>
<SignIn />
</SignedOut>
</BeamarAuthProvider>