closai-partner-integration
v1.0.3
Published
Closai Partner Integration
Downloads
433
Readme
Closai Partner Integration Library
Use the Closai Partner Integration Library to authenticate users with Closai from your web app.This guide shows how to install, configure, and use the Library, and how to handle the OAuth callback after login.
Overview
The Library exposes a configure function that returns two main helpers:
login()— redirects the user to the Closai login flowparseOAuthCallback(search)— reads the callback query string and extracts the userId
Installation
Install the package in your project:
npm install closai-partner-integration
# or
yarn add closai-partner-integration
# or
pnpm add closai-partner-integration
Requirements
Before using the library, make sure you have:
- A valid Closai Client ID
- A valid Redirect URI registered with Closai
The redirect URI is the URL Closai will redirect the user back to after authentication.
Example:
VITE_CLOSAI_CLIENT_ID=your_client_id
VITE_CLOSAI_REDIRECT_URI=http://localhost:3000/auth/closai/callbackLibrary Configuration
Create a small setup file to configure the Library once and reuse it across your app.
closai.ts
import { configure } from "closai-partner-integration";
const config = {
clientId: import.meta.env.VITE_CLOSAI_CLIENT_ID || "",
redirectUri: import.meta.env.VITE_CLOSAI_REDIRECT_URI || "",
};
const closai = configure(config);
export { closai };Config Shape
The library expects the following configuration object:
interface Config {
// The client ID provided by Closai
clientId: string;
// The redirect URI used as the callback URL after login
redirectUri: string;
}Basic Usage
After configuring the auth config, call closai.login() when the user clicks the login button.
import { closai } from "./closai";
function handleClosaiLogin() {
closai.login();
}Example in React:
import { closai } from "./closai";
export function LoginButton() {
return (
<button onClick={() => closai.login()}>
Continue with Closai
</button>
);
}How Login Works
When login() is called, the library:
- Validates the generated authentication URL
- Redirects the browser to the Closai authentication page
- After successful login, Closai redirects the user back to your
redirectUri
Internally, the library performs a browser redirect using:
window.location.href = uri;Because of that, login() should only be called in browser environments.
Handling the Callback
After Closai redirects the user back to your app, read the query string from the current URL and parse it with parseOAuthCallback.
Example callback URL
http://localhost:3000/auth/closai/callback?userId=123Parse the callback
import { closai } from "./closai";
const result = closai.parseOAuthCallback(window.location.search);
console.log(result.userId);Return type
{
userId: string | null;
}If the query string contains userId, it will be returned. Otherwise, the value will be null.
Example Callback Page in React
import { useEffect } from "react";
import { closai } from "./closai";
export function ClosaiCallbackPage() {
// Parse the callback query string
useEffect(() => {
const { userId } = closai.parseOAuthCallback(window.location.search);
if (!userId) {
console.error("Closai login failed: missing userId");
return;
}
console.log("Closai user authenticated:", userId);
// Example:
// - send the userId to your backend
// - exchange it for a session
// - fetch the user profile
// - redirect the user to the authenticated area
}, []);
return <p>Signing you in with Closai...</p>;
}Suggested Application Flow
A common integration flow looks like this:
- User clicks
Continue with Closai - App calls
closai.login() - User authenticates with Closai
- Closai redirects back to your app
- App reads
window.location.search - App calls
closai.parseOAuthCallback(...) - App uses the returned
userIdto complete authentication in its own system
Full Example
import { configure } from "closai-partner-integration";
const closai = configure({
clientId: import.meta.env.VITE_CLOSAI_CLIENT_ID || "",
redirectUri: import.meta.env.VITE_CLOSAI_REDIRECT_URI || "",
});
document.getElementById("login-btn")?.addEventListener("click", () => {
closai.login();
});
const { userId } = closai.parseOAuthCallback(window.location.search);
if (userId) {
console.log("Authenticated user:", userId);
}🔐 Popup Login (loginWithPopup)
In addition to the default redirect-based login, the library also provides a popup-based authentication flow using loginWithPopup().
This allows users to authenticate with Closai without leaving your current page.
When to use
Use loginWithPopup() when you want:
- A smoother UX (no full page redirect)
- To keep the user on the same screen
- To handle authentication asynchronously
Basic Usage
import { closai } from "./closai";
async function handleClosaiLoginPopup() {
try {
const result = await closai.loginWithPopup();
console.log("Closai user authenticated:", result.userId);
// Example:
// - send userId to your backend
// - create a session
// - fetch user profile
} catch (error) {
console.error("Closai popup login failed:", error);
}
}Example in React:
import { closai } from "./closai";
export function LoginButton() {
return (
<button onClick={() => closai.loginWithPopup()}>
Continue with Closai
</button>
);
}⚠️ Important
loginWithPopup() does NOT require a redirectUri.
Unlike login(), which depends on a full OAuth redirect flow, the popup method handles authentication entirely through window communication (postMessage).
This means:
- ✅ You do not need to configure a redirect URI to use
loginWithPopup() - ✅ No callback route is required
- ✅ No need to call
parseOAuthCallback
Key Differences
| Requirement | login() | loginWithPopup() |
| ------------------ | --------- | ------------------ |
| redirectUri | Required | ❌ Not required |
| Callback page | Required | ❌ Not required |
| parseOAuthCallback | Required | ❌ Not required |
| Page reload | Yes | ❌ No |
API Reference
configure(config)
Creates a configured Closai Library instance.
Parameters
configure({
clientId: string,
redirectUri?: string,
});Returns
{
login: () => void;
loginWithPopup: () => Promise<{ userId: string | null }>;
parseOAuthCallback: (search: string) => { userId: string | null };
}
login()
Redirects the current browser window to the Closai authentication URL.
Example
closai.login();Notes
- must run in the browser
- depends on a valid clientId and redirectUri
- if the authentication URI is not available, the library shows an error
parseOAuthCallback(search)
Parses the callback query string and returns the userId.
Parameters
parseOAuthCallback(search: string);Example
const { userId } = closai.parseOAuthCallback(window.location.search);Error Handling
If the library cannot generate a valid authentication URI, login() will not redirect and will show an error instead.
Example internal behavior:
if (!uri) {
showError(ERROR_MESSAGES.URI_REQUIRED);
return;
}To avoid that, ensure:
clientIdis definedredirectUriis defined- your environment variables are loaded correctly
Best Practices
- Configure once:
Create the library instance once and export it from a shared file.
export const closai = configure({
clientId: import.meta.env.VITE_CLOSAI_CLIENT_ID || "",
redirectUri: import.meta.env.VITE_CLOSAI_REDIRECT_URI || "",
});Notes
clientIdandredirectUrimust be defined to the login behavior (No popup)- your environment variables must be loaded correctly
- Keep secrets out of the frontend
Only public integration values such as client ID and redirect URI should live in the frontend. Any session creation or secure validation should happen on your backend.
- Validate callback results
Always check whether userId exists before continuing.
const { userId } = closai.parseOAuthCallback(window.location.search);
if (!userId) {
// handle failed login
}- Use a dedicated callback route
Create a route only for Closai auth callbacks, such as:
/auth/closai/callbackThis keeps the flow easier to maintain.
Troubleshooting
userId is null
Check whether:
- Closai redirected to the correct redirectUri
- the callback URL includes ?userId=...
- you passed window.location.search into parseOAuthCallback
Clicking login does nothing
Check whether:
- VITE_CLOSAI_CLIENT_ID is defined
- VITE_CLOSAI_REDIRECT_URI is defined
- the library was configured correctly
- the code is running in a browser
Redirect URI mismatch
Make sure the exact same callback URL is registered on the Closai side and used in your app configuration.
Example README Snippet for Consumers
import { configure } from "closai-partner-integration";
const closai = configure({
clientId: import.meta.env.VITE_CLOSAI_CLIENT_ID || "",
redirectUri: import.meta.env.VITE_CLOSAI_REDIRECT_URI || "",
});
function loginWithClosai() {
closai.login();
}
function handleClosaiCallback() {
const { userId } = closai.parseOAuthCallback(window.location.search);
if (!userId) {
throw new Error("Missing Closai userId");
}
return userId;
}Components
Closai Components is a framework-free JavaScript UI library designed to render closet-based product interfaces such as carousel, grid, and list views using pure HTML, CSS, and JavaScript.
No React. No Vue. No dependencies.
✨ Features
⚡ Zero dependencies (vanilla JS)
🧩 Easy to integrate into any project
🎨 Fully customizable via props and styles
🛍️ Built for product/closet UIs
📦 Supports:
- Carousel
- Grid
- List
🧠 Flexible data mapping (custom keys)
🎨 Required CSS (Important)
Since this is a pure JavaScript library, you must manually include the base styles.
Option 1 — Using HTML
<link
rel="stylesheet"
href="node_modules/closai-partner-integration/dist/styles/base.css"
/>Option 2 — Using bundlers (Vite, Webpack, etc.)
import "closai-partner-integration/dist/styles/base.css";⚠️ Important
- This CSS file is required for proper layout and styling
- Without it, components may render incorrectly
- You can override styles after importing it
🚀 Quick Start
Include the library:
<script src="/src/index.js"></script>or
import { renderClosetGrid } from "closai-partner-integration";Create a container:
<div id="grid"></div>Render a component:
renderClosetGrid("#grid", products);📊 Data Structure
Your product data should follow a structure like:
const products = [
{
id: "1",
name: "Black Jacket",
brand: "Zara",
price: 129.9,
image: "https://via.placeholder.com/300x400",
},
];🧩 Components
1. Carousel
renderClosetCarousel("#carousel", products, {
onItemClick: function (product) {
console.log(product);
},
});2. Grid
renderClosetGrid("#grid", products);3. List
renderClosetList("#list", products);⚙️ Configuration Options
You can fully customize how data is mapped and displayed:
renderClosetGrid("#grid", products, {
idKey: "id",
titleKey: "name",
subtitleKey: "brand",
priceKey: "price",
imageKey: "image",
currency: "$",
});🎨 Custom Props
You can pass additional props to customize behavior and styling.
Example:
renderClosetGrid("#grid", products, {
props: {
id: "custom-card",
customStyle: {
border: "2px solid black",
borderRadius: "20px",
},
},
});Supported Props
| Prop | Type | Description |
| ------------- | ------------- | ------------------------------ |
| id | string | Custom DOM id for each card |
| customStyle | object/string | Inline styles applied to cards |
🖱️ Events
onItemClick
renderClosetGrid("#grid", products, {
onItemClick: function (product) {
console.log("Clicked:", product);
},
});🧠 Advanced Usage
Custom Data Keys
If your API returns different field names:
renderClosetGrid("#grid", products, {
titleKey: "product_name",
subtitleKey: "designer",
imageKey: "thumbnail",
priceKey: "amount",
});Empty State
If no products are passed:
renderClosetGrid("#grid", [], {
emptyMessage: "No items in your closet yet",
});🎯 Use Cases
- Fashion apps
- Closet / wardrobe systems
- E-commerce widgets
- Embedded product UIs
- SDK integrations (like Closai 👀)
