In this guide we will add authentication UI and logic so we can connect our app to an actual BAQ server and use it with real data.
Code to follow this guide can be found on Github or StackBlitz.
Here’s an overview of authentication in BAQ:
Prompt the user for its entity (<Login>
component).
Register the app with the user’s server (SDK).
Redirect to the authorization flow (<Login>
component).
The user authorizes the app and their server redirects back.
The app extracts the authorization ID from the URL and can now start making requests against the server (<App>
component).
<Login>
We create a new Login.tsx
component that will be displayed before our user is authenticated. All that we need to start the authentication process is a single text input to collect the user’s entity.
On “enter”, we return the entity to our parent component.
import {UnauthenticatedState} from "@baqhub/sdk-react";
import {FC, FormEvent} from "react";
interface LoginProps {
state: UnauthenticatedState;
onConnectClick: (entity: string) => void;
}
export const Login: FC<LoginProps> = props => {
const {state, onConnectClick} = props;
const isConnecting = state.connectStatus !== "idle";
const onSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const form = e.currentTarget;
const formData = new FormData(form);
const entity = formData.get("entity")?.toString();
if (!entity) {
return;
}
onConnectClick(entity);
};
return (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
width: "100%",
height: "200px",
}}
>
<h1>BAQ Todos</h1>
<form
style={{display: "flex", gap: "12px"}}
onSubmit={onSubmit}
>
<input
type="text"
name="entity"
placeholder="user.host.com"
disabled={isConnecting}
autoFocus
/>
<button type="submit" disabled={isConnecting}>
Sign in
</button>
</form>
</div>
);
};
What’s new:
UnauthenticatedState
is the SDK’s authentication system state.Once the app is registered with the user’s server, we need to navigate the browser to the provided URL for the authorization flow to begin.
-import {FC, FormEvent} from "react";
+import {FC, FormEvent, useEffect} from "react";
export const Login: FC<LoginProps> = props => {
const {state, onConnectClick} = props;
const isConnecting = state.connectStatus !== "idle";
+ useEffect(() => {
+ if (state.connectStatus !== "waiting_on_flow") {
+ return;
+ }
+
+ window.location.href = state.flowUrl;
+ }, [state]);
...
<App>
Finally, we tie it all together in App.tsx
by displaying our <Login>
component when not authenticated, or the app UI otherwise.
We wrap the app in a <Store>
component that will be managing our records and communicating with the BAQ server. This is required for the hooks we added in step 3 to work properly.
import {FC} from "react";
import {Header} from "./components/Header";
import {TaskList} from "./components/TaskList";
+import {Login} from "./components/Login";
+import {localStorageAdapter} from "@baqhub/sdk-react-dom";
+import {buildAuthentication} from "./baq/authentication";
+import {Store} from "./baq/store";
+// Build the redirect URL from the current location.
+const redirectUrl =
+ window.location.origin + "?authorization_id={authorization_id}";
+// Pick a storage provider for authentication data (here localStorage).
+const {useAuthentication} = buildAuthentication({
+ storage: localStorageAdapter,
+ redirectUrl,
+});
+// Read the "authorization_id" from the query params.
+const currentUrl = new URL(window.location.href);
+const authorizationId = currentUrl.searchParams.get("authorization_id");
export const App: FC = () => {
+ // Find the current authentication state.
+ const {state, onConnectRequest, onDisconnectRequest} = useAuthentication(
+ {authorizationId}
+ );
+ // If not authenticated, display the login page.
+ if (state.status === "unauthenticated") {
+ return <Login state={state} onConnectClick={onConnectRequest} />;
+ }
+ // Otherwise, display the app.
return (
+ <Store identity={state.identity} onDisconnectRequest={onDisconnectRequest}>
<section className="todoapp">
<Header />
<TaskList />
</section>
+ </Store>
);
};
What’s new?
buildAuthentication
configures the authentication system.
localStorageAdapter
lets the SDK persist data in localStorage
.
redirectUrl
will be used to redirect back to the app.
authorizationId
is received when the auth flow completes.
useAuthentication
manages the current authentication state.
Store
manages records and keeps them in sync with the server.
That’s it! The app is now ready to authenticate with a BAQ server and use it to store its data. To give it a try, create a free account on BAQ.RUN and start the dev server if it’s not already running:
npm run dev
Navigating to http://localhost:5173/
displays the login page:
After going through the authorization flow with your newly created BAQ account, you should now be able to create tasks, mark them as completed, and delete them.
Things to try:
Refresh the app. All your data is still here!
Open two windows. State is synchronized between instances.
The app is now fully functional and can be deployed for public use.
The completed code can be found on Github or StackBlitz.
The app is deployed here for convenience.
To go further, check out how to create your own record type…
…or browse existing record types instead.
For a more in-depth look, follow up with these helpful guides: