Your frontend
Install and set up the Block-Auth widget in your React, Vue or Angular app.
Install the widget
npm install @block-auth.io/react
The styles are injected automatically; there is no CSS file to import.
Angular: the widget brings wallet libraries with CommonJS modules and optional dependencies the Angular builder can't resolve. Add them to the build options in angular.json, or the build warns and fails:
angular.json
{
"projects": {
"your-app": {
"architect": {
"build": {
"options": {
"allowedCommonJsDependencies": ["crypto-js", "qrcode", "bs58", "socket.io-client"],
"externalDependencies": [
"@base-org/account",
"@coinbase/wallet-sdk",
"@metamask/connect-evm",
"porto",
"porto/internal",
"@safe-global/safe-apps-provider",
"@safe-global/safe-apps-sdk",
"@walletconnect/ethereum-provider"
]
}
}
}
}
}
}
Configure the widget
Register the provider once, at the root of your app, with your public apiKey. The widget calls the backend routes you will add later under /auth/blockauth.
import { BlockAuthProvider } from '@block-auth.io/react'
import { Header } from './Header'
const config = {
apiKey: import.meta.env.VITE_BLOCKAUTH_API_KEY,
locale: 'en',
}
export default function App() {
return (
<BlockAuthProvider config={config}>
<Header />
</BlockAuthProvider>
)
}
Never put the apiSecret in your frontend, not even in an environment
variable like VITE_… or NEXT_PUBLIC_…: the bundler copies it into the
JavaScript every visitor downloads. The widget doesn't need it: if you pass
it, the SDK sends it from the browser.
Config options
apiKey(string, required): your app's API key. It's public: it only says which app is calling.authEndpoint(string | false): base path of your backend routes. By default/auth/blockauth: set it only if your routes live under another path. Serve it from the same origin as your frontend, so the session cookie travels with every request. If a route is missing or doesn't answer, the browser console says what to fix. Set it tofalseonly if your app has no backend: the widget then signs the user in by itself, and your page knows their address but your server has no session it can trust.locale(string): language of the widget,"en"or"es". By default, the browser's language.sessionCheckSeconds(number): how often the widget asks/mewhether the session is still open, besides every time the tab gets focus. By default60;0turns it off.onSessionEnded(function): called when your backend has closed the session, for example after the user signed out from Block-Auth Identity or the extension. The widget already shows the user as signed out: use it to leave private pages.wsUrl(string): the Block-Auth server the widget talks to. By default production (wss://wss.block-auth.io): change it only if Block-Auth gives you another environment to test against.
The login itself happens on the Block-Auth login page (auth.block-auth.io), so you don't configure Google, Microsoft, wallets or WalletConnect in your app.
Add the login button
Place FlowBlockAuth where you want the sign-in button. After the login it turns into the profile menu, and on reload it restores the session by asking your backend.
import { FlowBlockAuth } from '@block-auth.io/react'
export function Header() {
return (
<header>
<FlowBlockAuth
onSuccess={(address) => console.log('Signed in as', address)}
onError={() => alert('Sign-in failed, try again.')}
onLogout={() => window.location.assign('/')}
/>
</header>
)
}
Reading the session (roles, user profile)
Inside the provider you can read the authenticated user — useBlockAuthUser (React hook / Vue composable) or the BlockAuthService signals (Angular) — with helpers such as roleNames, hasRoleBySlug(slug), and isLoadingUser:
import { useBlockAuthUser } from "@block-auth.io/react";
function RolesPanel() {
const { roleNames, hasRole, isLoadingUser } = useBlockAuthUser();
if (isLoadingUser) return <p>Loading roles…</p>;
return <p>Your roles: {roleNames.join(", ") || "none"}</p>;
}
Customize the widget
Your apiKey, and authEndpoint if you change it, go in config (Configure the widget). The components only take behavior, callback and styling props.
Props are documented with their React names; the same surface exists in every binding with the framework's idiom:
- Vue: same props in kebab-case (
:on-success,:dropdown-btn-size,:show-dropdown-when-connected, …). - Angular: styling and behavior props are inputs (
[dropdownBtnSize],[showDropdownWhenConnected], …) and callbacks are outputs:(success),(errored)and(loggedOut)inFlowBlockAuth,(linked)and(errored)inLinkBlockAuth.
FlowBlockAuth: callbacks
onSuccess(function): called after a successful login, with the user'saddress.onError(function): called when the login fails or the user closes the login window.onLogout(function): called after the user logs out from the profile menu.
FlowBlockAuth: behavior
showDropdownWhenConnected(boolean): shows the profile menu when the user is signed in (default:true).isInsideModal(boolean): set it totruewhen the button is inside your own modal (default:false).containerRef(ref): the modal's container, used withisInsideModalto detect clicks outside.buttonText(string): label of the sign-in button.
FlowBlockAuth: when your backend handles the login
With the backend routes you don't need these props. Use them only if your page already knows the user or your backend redeems the code its own way. In Angular both are inputs: [user] and [onAuthCode].
user(object | null): the signed-in user as your backend knows it ({ address, did, roles }), ornullif nobody is signed in. With it, the widget shows that user and doesn't ask/mewhen the page loads, for example when your server already renders the user.onAuthCode(function): receives{ code, codeVerifier, redirectUri }after the login instead of sending it to/code. Send it to your backend, redeem it there and return the user. Unless you also setauthEndpoint, the widget then doesn't call/meor/logoutnor check the session: close your session inonLogout.
FlowBlockAuth: styling
btnClassNamesbtnTextClassNamesdropdownBtnSize(string):"xs","small","base"or"large"(default:"small").dropdownBtnBgClassNamesdropdownBtnTextColorClassNamesdropdownBtnBorderClassNamesdropdownHeight(number)additionalDropdownItems(array): extra options for the profile menu. Each item is{ onClick, text, firstIcon?, disabled? }, wheretextis a string or a map per language ({ es, en }).
LinkBlockAuth
The account linking button. It hides itself once the user is linked.
onLinked(function): called after the account is linked.onError(function): called with the reason:already_linked,not_signed_inorfailed.buttonText(string): label of the button.btnClassNames,btnTextClassNames: same as inFlowBlockAuth.

