@authrobo/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # @authrobo/react
2
+
3
+ Drop-in React sign-in for [AuthRobo](https://authrobo.com): a configurable social
4
+ button and an auth modal/panel that renders exactly the sign-in methods you've
5
+ enabled in your AuthRobo dashboard (Google, LinkedIn, GitHub, Apple, email).
6
+
7
+ ```bash
8
+ npm i @authrobo/react
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ Wrap your app once with your AuthRobo config:
14
+
15
+ ```tsx
16
+ import { AuthRoboProvider } from "@authrobo/react";
17
+
18
+ <AuthRoboProvider
19
+ config={{
20
+ issuerUrl: "https://authrobo.com",
21
+ clientId: process.env.NEXT_PUBLIC_AUTHROBO_CLIENT_ID!, // arc_...
22
+ redirectUri: "https://yourapp.com/auth/callback",
23
+ }}
24
+ >
25
+ <App />
26
+ </AuthRoboProvider>;
27
+ ```
28
+
29
+ ### A single provider button
30
+
31
+ ```tsx
32
+ import { AuthRoboButton } from "@authrobo/react";
33
+
34
+ <AuthRoboButton provider="linkedin" />;
35
+ ```
36
+
37
+ ### The auto-configured auth panel / modal
38
+
39
+ `<AuthRoboAuth>` calls AuthRobo's public `/api/v1/config` endpoint and renders a
40
+ button for every method you've turned on — no need to hard-code the list.
41
+
42
+ ```tsx
43
+ import { AuthRoboAuth } from "@authrobo/react";
44
+
45
+ // Inline (renders in place)
46
+ <AuthRoboAuth />
47
+
48
+ // Modal
49
+ const [open, setOpen] = useState(false);
50
+ <button onClick={() => setOpen(true)}>Sign in</button>
51
+ <AuthRoboAuth mode="modal" open={open} onClose={() => setOpen(false)} />
52
+ ```
53
+
54
+ ## How the flow works
55
+
56
+ Clicking a method navigates the browser to AuthRobo's hosted flow. After the user
57
+ authenticates, AuthRobo redirects back to your `redirectUri` with a one-time
58
+ `code`. **Your backend** exchanges that code (with your `client_secret`, kept
59
+ server-side) for tokens — e.g. via [`@authrobo/sdk`](https://www.npmjs.com/package/@authrobo/sdk)'s
60
+ `exchange()`. This package never sees your secret.
61
+
62
+ ## API
63
+
64
+ - `AuthRoboProvider({ config, children })` — supplies config via context.
65
+ - `AuthRoboButton({ provider, config?, label?, className?, style?, onClick? })` —
66
+ one branded button. `provider` is `"google" | "linkedin" | "github" | "apple" | "password"`.
67
+ - `AuthRoboAuth({ config?, mode?, open?, onClose?, title?, methods? })` — renders
68
+ all enabled methods; `methods` can restrict/reorder them.
69
+ - `useAppConfig(config?)` → `{ data, loading, error }` — the app's branding +
70
+ enabled methods.
71
+ - `startSignIn(config, method, opts?)` — imperatively begin a flow.
72
+
73
+ `config` can come from `<AuthRoboProvider>` or be passed per-component.
74
+
75
+ Styling is self-contained (no CSS import needed); pass `className`/`style` to
76
+ override, or restyle the `.authrobo-*` classes.
package/dist/index.cjs ADDED
@@ -0,0 +1,245 @@
1
+ "use client";
2
+ "use strict";
3
+ "use client";
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/index.tsx
23
+ var index_exports = {};
24
+ __export(index_exports, {
25
+ AuthRoboAuth: () => AuthRoboAuth,
26
+ AuthRoboButton: () => AuthRoboButton,
27
+ AuthRoboProvider: () => AuthRoboProvider,
28
+ PROVIDER_META: () => PROVIDER_META,
29
+ startSignIn: () => startSignIn,
30
+ useAppConfig: () => useAppConfig
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+ var import_react = require("react");
34
+
35
+ // src/icons.tsx
36
+ var import_jsx_runtime = require("react/jsx-runtime");
37
+ var sz = { width: 18, height: 18, "aria-hidden": true };
38
+ function GoogleIcon() {
39
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 48 48", ...sz, children: [
40
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "#FFC107", d: "M43.6 20.5H42V20H24v8h11.3C33.7 32.9 29.3 36 24 36c-6.6 0-12-5.4-12-12s5.4-12 12-12c3.1 0 5.9 1.2 8 3.1l5.7-5.7C34.6 6.1 29.6 4 24 4 12.9 4 4 12.9 4 24s8.9 20 20 20 20-8.9 20-20c0-1.3-.1-2.3-.4-3.5z" }),
41
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "#FF3D00", d: "M6.3 14.7l6.6 4.8C14.7 16 19 13 24 13c3.1 0 5.9 1.2 8 3.1l5.7-5.7C34.6 6.1 29.6 4 24 4 16.3 4 9.7 8.3 6.3 14.7z" }),
42
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "#4CAF50", d: "M24 44c5.2 0 10-2 13.6-5.2l-6.3-5.2C29.2 35 26.7 36 24 36c-5.3 0-9.7-3.4-11.3-8.1l-6.5 5C9.5 39.6 16.2 44 24 44z" }),
43
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "#1976D2", d: "M43.6 20.5H42V20H24v8h11.3c-.8 2.2-2.2 4.1-4.1 5.6l6.3 5.2C41.9 36 44 30.5 44 24c0-1.3-.1-2.3-.4-3.5z" })
44
+ ] });
45
+ }
46
+ function LinkedInIcon() {
47
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", ...sz, fill: "#0A66C2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.13 1.45-2.13 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.45v6.29zM5.34 7.43a2.06 2.06 0 1 1 0-4.13 2.06 2.06 0 0 1 0 4.13zM7.12 20.45H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z" }) });
48
+ }
49
+ function GitHubIcon() {
50
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", ...sz, fill: "#181717", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 .3a12 12 0 0 0-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.6-1.4-1.3-1.8-1.3-1.8-1.1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1.1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.8-1.6-2.7-.3-5.5-1.3-5.5-6a4.7 4.7 0 0 1 1.2-3.2c-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.3 1.2a11.5 11.5 0 0 1 6 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 2.9.1 3.2a4.7 4.7 0 0 1 1.2 3.2c0 4.7-2.8 5.7-5.5 6 .4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0 0 12 .3" }) });
51
+ }
52
+ function AppleIcon() {
53
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", ...sz, fill: "#000", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17.05 12.04c-.03-2.7 2.2-4 2.3-4.06-1.25-1.84-3.2-2.09-3.9-2.12-1.66-.17-3.24.98-4.08.98-.84 0-2.14-.96-3.52-.93-1.81.03-3.48 1.05-4.41 2.67-1.88 3.27-.48 8.1 1.35 10.76.9 1.3 1.97 2.76 3.38 2.71 1.36-.05 1.87-.88 3.51-.88 1.64 0 2.1.88 3.53.85 1.46-.03 2.38-1.33 3.27-2.63 1.03-1.51 1.46-2.97 1.48-3.05-.03-.01-2.84-1.09-2.87-4.32M14.4 4.2c.74-.9 1.24-2.15 1.1-3.4-1.07.04-2.36.71-3.13 1.61-.69.8-1.29 2.07-1.13 3.29 1.19.09 2.41-.61 3.16-1.5" }) });
54
+ }
55
+ function MailIcon() {
56
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", ...sz, fill: "none", stroke: "#334155", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [
57
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
58
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m3 7 9 6 9-6" })
59
+ ] });
60
+ }
61
+ var PROVIDER_META = {
62
+ google: { label: "Google", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GoogleIcon, {}) },
63
+ linkedin: { label: "LinkedIn", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkedInIcon, {}) },
64
+ github: { label: "GitHub", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitHubIcon, {}) },
65
+ apple: { label: "Apple", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppleIcon, {}) },
66
+ password: { label: "email", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MailIcon, {}) }
67
+ };
68
+
69
+ // src/index.tsx
70
+ var import_jsx_runtime2 = require("react/jsx-runtime");
71
+ var ConfigContext = (0, import_react.createContext)(null);
72
+ function AuthRoboProvider({
73
+ config,
74
+ children
75
+ }) {
76
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ConfigContext.Provider, { value: config, children });
77
+ }
78
+ function useResolvedConfig(override) {
79
+ const ctx = (0, import_react.useContext)(ConfigContext);
80
+ const c = override ?? ctx;
81
+ if (!c) {
82
+ throw new Error(
83
+ "@authrobo/react: wrap your app in <AuthRoboProvider config={...}> or pass a `config` prop."
84
+ );
85
+ }
86
+ return c;
87
+ }
88
+ function randomState() {
89
+ const b = new Uint8Array(16);
90
+ (globalThis.crypto ?? globalThis.crypto).getRandomValues(b);
91
+ return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
92
+ }
93
+ function startSignIn(config, method, opts) {
94
+ const base = config.issuerUrl.replace(/\/$/, "");
95
+ const state = opts?.state ?? randomState();
96
+ try {
97
+ sessionStorage.setItem("authrobo:state", state);
98
+ } catch {
99
+ }
100
+ const params = new URLSearchParams({
101
+ client_id: config.clientId,
102
+ redirect_uri: config.redirectUri,
103
+ state
104
+ });
105
+ if (opts?.loginHint) params.set("login_hint", opts.loginHint);
106
+ const url = method === "password" ? `${base}/login?${params.toString()}` : `${base}/api/v1/auth/${method}/start?${params.toString()}`;
107
+ window.location.assign(url);
108
+ }
109
+ function useAppConfig(config) {
110
+ const c = useResolvedConfig(config);
111
+ const [state, setState] = (0, import_react.useState)({
112
+ data: null,
113
+ loading: true,
114
+ error: null
115
+ });
116
+ (0, import_react.useEffect)(() => {
117
+ let active = true;
118
+ setState({ data: null, loading: true, error: null });
119
+ const base = c.issuerUrl.replace(/\/$/, "");
120
+ fetch(`${base}/api/v1/config?client_id=${encodeURIComponent(c.clientId)}`).then((r) => r.ok ? r.json() : Promise.reject(new Error(`config ${r.status}`))).then((j) => {
121
+ if (!active) return;
122
+ setState({
123
+ data: { name: j.name, brandColor: j.brand_color, logoUrl: j.logo_url, methods: j.methods },
124
+ loading: false,
125
+ error: null
126
+ });
127
+ }).catch((e) => active && setState({ data: null, loading: false, error: String(e?.message ?? e) }));
128
+ return () => {
129
+ active = false;
130
+ };
131
+ }, [c.issuerUrl, c.clientId]);
132
+ return state;
133
+ }
134
+ var STYLE_ID = "authrobo-react-styles";
135
+ var CSS = `
136
+ .authrobo-btn{display:flex;width:100%;align-items:center;justify-content:center;gap:10px;
137
+ border:1px solid #d1d5db;background:#fff;color:#111827;border-radius:10px;padding:10px 14px;
138
+ font:600 14px/1.2 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer;
139
+ transition:background .15s ease,border-color .15s ease,box-shadow .15s ease;}
140
+ .authrobo-btn:hover{background:#f8fafc;border-color:#9ca3af;}
141
+ .authrobo-btn:focus-visible{outline:2px solid #2b8aff;outline-offset:2px;}
142
+ .authrobo-stack{display:flex;flex-direction:column;gap:10px;}
143
+ .authrobo-divider{display:flex;align-items:center;gap:10px;color:#9ca3af;font:500 12px system-ui,sans-serif;}
144
+ .authrobo-divider::before,.authrobo-divider::after{content:"";height:1px;flex:1;background:#e5e7eb;}
145
+ .authrobo-overlay{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;
146
+ justify-content:center;padding:16px;background:rgba(2,6,23,.55);backdrop-filter:blur(2px);
147
+ animation:authrobo-fade .16s ease-out both;}
148
+ .authrobo-card{position:relative;width:100%;max-width:360px;background:#fff;border-radius:16px;
149
+ padding:24px;box-shadow:0 20px 60px -12px rgba(2,6,23,.4);animation:authrobo-pop .2s cubic-bezier(.16,1,.3,1) both;}
150
+ .authrobo-close{position:absolute;top:12px;right:12px;border:0;background:transparent;cursor:pointer;
151
+ color:#94a3b8;font-size:18px;line-height:1;padding:4px;}
152
+ .authrobo-close:hover{color:#334155;}
153
+ @keyframes authrobo-fade{from{opacity:0}to{opacity:1}}
154
+ @keyframes authrobo-pop{from{opacity:0;transform:translateY(8px) scale(.97)}to{opacity:1;transform:none}}
155
+ @media (prefers-reduced-motion: reduce){.authrobo-overlay,.authrobo-card{animation:none}}
156
+ `;
157
+ function useInjectStyles() {
158
+ (0, import_react.useEffect)(() => {
159
+ if (typeof document === "undefined" || document.getElementById(STYLE_ID)) return;
160
+ const el = document.createElement("style");
161
+ el.id = STYLE_ID;
162
+ el.textContent = CSS;
163
+ document.head.appendChild(el);
164
+ }, []);
165
+ }
166
+ function AuthRoboButton({
167
+ provider,
168
+ config,
169
+ label,
170
+ className,
171
+ style,
172
+ onClick
173
+ }) {
174
+ useInjectStyles();
175
+ const c = useResolvedConfig(config);
176
+ const meta = PROVIDER_META[provider];
177
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
178
+ "button",
179
+ {
180
+ type: "button",
181
+ className: className ? `authrobo-btn ${className}` : "authrobo-btn",
182
+ style,
183
+ onClick: () => {
184
+ onClick?.();
185
+ startSignIn(c, provider);
186
+ },
187
+ children: [
188
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { display: "inline-flex", alignItems: "center" }, children: meta.icon }),
189
+ label ?? `Continue with ${meta.label}`
190
+ ]
191
+ }
192
+ );
193
+ }
194
+ function AuthRoboAuth({
195
+ config,
196
+ mode = "inline",
197
+ open = true,
198
+ onClose,
199
+ title,
200
+ methods: only
201
+ }) {
202
+ useInjectStyles();
203
+ const c = useResolvedConfig(config);
204
+ const { data, loading, error } = useAppConfig(c);
205
+ (0, import_react.useEffect)(() => {
206
+ if (mode !== "modal" || !open) return;
207
+ const onKey = (e) => e.key === "Escape" && onClose?.();
208
+ document.addEventListener("keydown", onKey);
209
+ return () => document.removeEventListener("keydown", onKey);
210
+ }, [mode, open, onClose]);
211
+ if (mode === "modal" && !open) return null;
212
+ const available = data?.methods ?? [];
213
+ const shown = (only ? only.filter((m) => available.includes(m)) : available).filter(
214
+ (m) => m in PROVIDER_META
215
+ );
216
+ const social = shown.filter((m) => m !== "password");
217
+ const hasPassword = shown.includes("password");
218
+ const panel = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "authrobo-stack", children: [
219
+ (data?.logoUrl || data?.name) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 4 }, children: [
220
+ data?.logoUrl && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("img", { src: data.logoUrl, alt: "", width: 28, height: 28, style: { borderRadius: 6 } }),
221
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { font: "700 16px system-ui,sans-serif", color: "#0f172a" }, children: title ?? `Sign in${data?.name ? ` to ${data.name}` : ""}` })
222
+ ] }),
223
+ loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { color: "#64748b", font: "14px system-ui,sans-serif" }, children: "Loading sign-in options\u2026" }),
224
+ error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { color: "#b91c1c", font: "14px system-ui,sans-serif" }, children: "Couldn't load sign-in options." }),
225
+ social.map((m) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AuthRoboButton, { provider: m, config: c }, m)),
226
+ hasPassword && social.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "authrobo-divider", children: "or" }),
227
+ hasPassword && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AuthRoboButton, { provider: "password", config: c, label: "Continue with email" }),
228
+ !loading && !error && shown.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { color: "#64748b", font: "14px system-ui,sans-serif" }, children: "No sign-in methods are enabled for this app." })
229
+ ] });
230
+ if (mode === "inline") return panel;
231
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "authrobo-overlay", onClick: () => onClose?.(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "authrobo-card", role: "dialog", "aria-modal": "true", onClick: (e) => e.stopPropagation(), children: [
232
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "authrobo-close", "aria-label": "Close", onClick: () => onClose?.(), children: "\u2715" }),
233
+ panel
234
+ ] }) });
235
+ }
236
+ // Annotate the CommonJS export names for ESM import in node:
237
+ 0 && (module.exports = {
238
+ AuthRoboAuth,
239
+ AuthRoboButton,
240
+ AuthRoboProvider,
241
+ PROVIDER_META,
242
+ startSignIn,
243
+ useAppConfig
244
+ });
245
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.tsx","../src/icons.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { createContext, useContext, useEffect, useState } from \"react\";\nimport { PROVIDER_META, type Method } from \"./icons\";\n\nexport type { Method } from \"./icons\";\nexport { PROVIDER_META } from \"./icons\";\n\nexport interface AuthRoboConfig {\n /** Your AuthRobo instance, e.g. \"https://authrobo.com\". */\n issuerUrl: string;\n /** Your app's client_id (arc_...). */\n clientId: string;\n /** Where AuthRobo returns the one-time `code` after sign-in. */\n redirectUri: string;\n}\n\n/** Shape returned by GET /api/v1/config. */\nexport interface AppConfig {\n name: string;\n brandColor: string | null;\n logoUrl: string | null;\n methods: Method[];\n}\n\nconst ConfigContext = createContext<AuthRoboConfig | null>(null);\n\nexport function AuthRoboProvider({\n config,\n children,\n}: {\n config: AuthRoboConfig;\n children: React.ReactNode;\n}) {\n return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>;\n}\n\nfunction useResolvedConfig(override?: AuthRoboConfig): AuthRoboConfig {\n const ctx = useContext(ConfigContext);\n const c = override ?? ctx;\n if (!c) {\n throw new Error(\n \"@authrobo/react: wrap your app in <AuthRoboProvider config={...}> or pass a `config` prop.\",\n );\n }\n return c;\n}\n\nfunction randomState(): string {\n const b = new Uint8Array(16);\n (globalThis.crypto ?? (globalThis as { crypto: Crypto }).crypto).getRandomValues(b);\n return Array.from(b, (x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * Begin sign-in: navigate the browser to AuthRobo's hosted flow for `method`.\n * Social methods hit /api/v1/auth/<provider>/start; \"password\" opens the hosted\n * email login page. AuthRobo returns to your `redirectUri` with a `code` that\n * your backend exchanges (with your client_secret) for tokens.\n */\nexport function startSignIn(\n config: AuthRoboConfig,\n method: Method,\n opts?: { state?: string; loginHint?: string },\n): void {\n const base = config.issuerUrl.replace(/\\/$/, \"\");\n const state = opts?.state ?? randomState();\n try {\n sessionStorage.setItem(\"authrobo:state\", state);\n } catch {\n /* storage unavailable — state is still sent, just not persisted for a check */\n }\n const params = new URLSearchParams({\n client_id: config.clientId,\n redirect_uri: config.redirectUri,\n state,\n });\n if (opts?.loginHint) params.set(\"login_hint\", opts.loginHint);\n const url =\n method === \"password\"\n ? `${base}/login?${params.toString()}`\n : `${base}/api/v1/auth/${method}/start?${params.toString()}`;\n window.location.assign(url);\n}\n\n/** Fetch an app's public config (branding + enabled methods) for `clientId`. */\nexport function useAppConfig(config?: AuthRoboConfig): {\n data: AppConfig | null;\n loading: boolean;\n error: string | null;\n} {\n const c = useResolvedConfig(config);\n const [state, setState] = useState<{ data: AppConfig | null; loading: boolean; error: string | null }>({\n data: null,\n loading: true,\n error: null,\n });\n\n useEffect(() => {\n let active = true;\n setState({ data: null, loading: true, error: null });\n const base = c.issuerUrl.replace(/\\/$/, \"\");\n fetch(`${base}/api/v1/config?client_id=${encodeURIComponent(c.clientId)}`)\n .then((r) => (r.ok ? r.json() : Promise.reject(new Error(`config ${r.status}`))))\n .then((j) => {\n if (!active) return;\n setState({\n data: { name: j.name, brandColor: j.brand_color, logoUrl: j.logo_url, methods: j.methods },\n loading: false,\n error: null,\n });\n })\n .catch((e) => active && setState({ data: null, loading: false, error: String(e?.message ?? e) }));\n return () => {\n active = false;\n };\n }, [c.issuerUrl, c.clientId]);\n\n return state;\n}\n\n/* ----------------------------- Styling ---------------------------------- */\n\nconst STYLE_ID = \"authrobo-react-styles\";\nconst CSS = `\n.authrobo-btn{display:flex;width:100%;align-items:center;justify-content:center;gap:10px;\n border:1px solid #d1d5db;background:#fff;color:#111827;border-radius:10px;padding:10px 14px;\n font:600 14px/1.2 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer;\n transition:background .15s ease,border-color .15s ease,box-shadow .15s ease;}\n.authrobo-btn:hover{background:#f8fafc;border-color:#9ca3af;}\n.authrobo-btn:focus-visible{outline:2px solid #2b8aff;outline-offset:2px;}\n.authrobo-stack{display:flex;flex-direction:column;gap:10px;}\n.authrobo-divider{display:flex;align-items:center;gap:10px;color:#9ca3af;font:500 12px system-ui,sans-serif;}\n.authrobo-divider::before,.authrobo-divider::after{content:\"\";height:1px;flex:1;background:#e5e7eb;}\n.authrobo-overlay{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;\n justify-content:center;padding:16px;background:rgba(2,6,23,.55);backdrop-filter:blur(2px);\n animation:authrobo-fade .16s ease-out both;}\n.authrobo-card{position:relative;width:100%;max-width:360px;background:#fff;border-radius:16px;\n padding:24px;box-shadow:0 20px 60px -12px rgba(2,6,23,.4);animation:authrobo-pop .2s cubic-bezier(.16,1,.3,1) both;}\n.authrobo-close{position:absolute;top:12px;right:12px;border:0;background:transparent;cursor:pointer;\n color:#94a3b8;font-size:18px;line-height:1;padding:4px;}\n.authrobo-close:hover{color:#334155;}\n@keyframes authrobo-fade{from{opacity:0}to{opacity:1}}\n@keyframes authrobo-pop{from{opacity:0;transform:translateY(8px) scale(.97)}to{opacity:1;transform:none}}\n@media (prefers-reduced-motion: reduce){.authrobo-overlay,.authrobo-card{animation:none}}\n`;\n\nfunction useInjectStyles() {\n useEffect(() => {\n if (typeof document === \"undefined\" || document.getElementById(STYLE_ID)) return;\n const el = document.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = CSS;\n document.head.appendChild(el);\n }, []);\n}\n\n/* ----------------------------- Button ----------------------------------- */\n\nexport function AuthRoboButton({\n provider,\n config,\n label,\n className,\n style,\n onClick,\n}: {\n provider: Method;\n config?: AuthRoboConfig;\n label?: string;\n className?: string;\n style?: React.CSSProperties;\n onClick?: () => void;\n}) {\n useInjectStyles();\n const c = useResolvedConfig(config);\n const meta = PROVIDER_META[provider];\n return (\n <button\n type=\"button\"\n className={className ? `authrobo-btn ${className}` : \"authrobo-btn\"}\n style={style}\n onClick={() => {\n onClick?.();\n startSignIn(c, provider);\n }}\n >\n <span style={{ display: \"inline-flex\", alignItems: \"center\" }}>{meta.icon}</span>\n {label ?? `Continue with ${meta.label}`}\n </button>\n );\n}\n\n/* ---------------------- Auth modal / inline panel ----------------------- */\n\nexport function AuthRoboAuth({\n config,\n mode = \"inline\",\n open = true,\n onClose,\n title,\n methods: only,\n}: {\n config?: AuthRoboConfig;\n /** \"inline\" renders in place; \"modal\" renders a centered overlay. */\n mode?: \"inline\" | \"modal\";\n /** Only used in modal mode. */\n open?: boolean;\n onClose?: () => void;\n /** Heading; defaults to \"Sign in to <app name>\". */\n title?: string;\n /** Restrict/reorder which methods to show (defaults to all enabled ones). */\n methods?: Method[];\n}) {\n useInjectStyles();\n const c = useResolvedConfig(config);\n const { data, loading, error } = useAppConfig(c);\n\n useEffect(() => {\n if (mode !== \"modal\" || !open) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && onClose?.();\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [mode, open, onClose]);\n\n if (mode === \"modal\" && !open) return null;\n\n const available = data?.methods ?? [];\n const shown = (only ? only.filter((m) => available.includes(m)) : available).filter(\n (m) => m in PROVIDER_META,\n );\n const social = shown.filter((m) => m !== \"password\");\n const hasPassword = shown.includes(\"password\");\n\n const panel = (\n <div className=\"authrobo-stack\">\n {(data?.logoUrl || data?.name) && (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 10, marginBottom: 4 }}>\n {data?.logoUrl && (\n <img src={data.logoUrl} alt=\"\" width={28} height={28} style={{ borderRadius: 6 }} />\n )}\n <span style={{ font: \"700 16px system-ui,sans-serif\", color: \"#0f172a\" }}>\n {title ?? `Sign in${data?.name ? ` to ${data.name}` : \"\"}`}\n </span>\n </div>\n )}\n\n {loading && <p style={{ color: \"#64748b\", font: \"14px system-ui,sans-serif\" }}>Loading sign-in options…</p>}\n {error && (\n <p style={{ color: \"#b91c1c\", font: \"14px system-ui,sans-serif\" }}>\n Couldn&apos;t load sign-in options.\n </p>\n )}\n\n {social.map((m) => (\n <AuthRoboButton key={m} provider={m} config={c} />\n ))}\n {hasPassword && social.length > 0 && <div className=\"authrobo-divider\">or</div>}\n {hasPassword && <AuthRoboButton provider=\"password\" config={c} label=\"Continue with email\" />}\n\n {!loading && !error && shown.length === 0 && (\n <p style={{ color: \"#64748b\", font: \"14px system-ui,sans-serif\" }}>\n No sign-in methods are enabled for this app.\n </p>\n )}\n </div>\n );\n\n if (mode === \"inline\") return panel;\n\n return (\n <div className=\"authrobo-overlay\" onClick={() => onClose?.()}>\n <div className=\"authrobo-card\" role=\"dialog\" aria-modal=\"true\" onClick={(e) => e.stopPropagation()}>\n <button type=\"button\" className=\"authrobo-close\" aria-label=\"Close\" onClick={() => onClose?.()}>\n ✕\n </button>\n {panel}\n </div>\n </div>\n );\n}\n","import * as React from \"react\";\n\n// Sign-in methods AuthRobo can surface. \"password\" routes to the hosted email\n// login page; the rest are social OAuth providers.\nexport type Method = \"google\" | \"linkedin\" | \"github\" | \"apple\" | \"password\";\n\nconst sz = { width: 18, height: 18, \"aria-hidden\": true } as const;\n\nfunction GoogleIcon() {\n return (\n <svg viewBox=\"0 0 48 48\" {...sz}>\n <path fill=\"#FFC107\" d=\"M43.6 20.5H42V20H24v8h11.3C33.7 32.9 29.3 36 24 36c-6.6 0-12-5.4-12-12s5.4-12 12-12c3.1 0 5.9 1.2 8 3.1l5.7-5.7C34.6 6.1 29.6 4 24 4 12.9 4 4 12.9 4 24s8.9 20 20 20 20-8.9 20-20c0-1.3-.1-2.3-.4-3.5z\" />\n <path fill=\"#FF3D00\" d=\"M6.3 14.7l6.6 4.8C14.7 16 19 13 24 13c3.1 0 5.9 1.2 8 3.1l5.7-5.7C34.6 6.1 29.6 4 24 4 16.3 4 9.7 8.3 6.3 14.7z\" />\n <path fill=\"#4CAF50\" d=\"M24 44c5.2 0 10-2 13.6-5.2l-6.3-5.2C29.2 35 26.7 36 24 36c-5.3 0-9.7-3.4-11.3-8.1l-6.5 5C9.5 39.6 16.2 44 24 44z\" />\n <path fill=\"#1976D2\" d=\"M43.6 20.5H42V20H24v8h11.3c-.8 2.2-2.2 4.1-4.1 5.6l6.3 5.2C41.9 36 44 30.5 44 24c0-1.3-.1-2.3-.4-3.5z\" />\n </svg>\n );\n}\n\nfunction LinkedInIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" {...sz} fill=\"#0A66C2\">\n <path d=\"M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.13 1.45-2.13 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.45v6.29zM5.34 7.43a2.06 2.06 0 1 1 0-4.13 2.06 2.06 0 0 1 0 4.13zM7.12 20.45H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z\" />\n </svg>\n );\n}\n\nfunction GitHubIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" {...sz} fill=\"#181717\">\n <path d=\"M12 .3a12 12 0 0 0-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.6-1.4-1.3-1.8-1.3-1.8-1.1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1.1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.8-1.6-2.7-.3-5.5-1.3-5.5-6a4.7 4.7 0 0 1 1.2-3.2c-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.3 1.2a11.5 11.5 0 0 1 6 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 2.9.1 3.2a4.7 4.7 0 0 1 1.2 3.2c0 4.7-2.8 5.7-5.5 6 .4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0 0 12 .3\" />\n </svg>\n );\n}\n\nfunction AppleIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" {...sz} fill=\"#000\">\n <path d=\"M17.05 12.04c-.03-2.7 2.2-4 2.3-4.06-1.25-1.84-3.2-2.09-3.9-2.12-1.66-.17-3.24.98-4.08.98-.84 0-2.14-.96-3.52-.93-1.81.03-3.48 1.05-4.41 2.67-1.88 3.27-.48 8.1 1.35 10.76.9 1.3 1.97 2.76 3.38 2.71 1.36-.05 1.87-.88 3.51-.88 1.64 0 2.1.88 3.53.85 1.46-.03 2.38-1.33 3.27-2.63 1.03-1.51 1.46-2.97 1.48-3.05-.03-.01-2.84-1.09-2.87-4.32M14.4 4.2c.74-.9 1.24-2.15 1.1-3.4-1.07.04-2.36.71-3.13 1.61-.69.8-1.29 2.07-1.13 3.29 1.19.09 2.41-.61 3.16-1.5\" />\n </svg>\n );\n}\n\nfunction MailIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" {...sz} fill=\"none\" stroke=\"#334155\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" />\n <path d=\"m3 7 9 6 9-6\" />\n </svg>\n );\n}\n\nexport const PROVIDER_META: Record<Method, { label: string; icon: React.ReactNode }> = {\n google: { label: \"Google\", icon: <GoogleIcon /> },\n linkedin: { label: \"LinkedIn\", icon: <LinkedInIcon /> },\n github: { label: \"GitHub\", icon: <GitHubIcon /> },\n apple: { label: \"Apple\", icon: <AppleIcon /> },\n password: { label: \"email\", icon: <MailIcon /> },\n};\n"],"mappings":"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","names":["import_jsx_runtime"]}
@@ -0,0 +1,65 @@
1
+ import * as React from 'react';
2
+
3
+ type Method = "google" | "linkedin" | "github" | "apple" | "password";
4
+ declare const PROVIDER_META: Record<Method, {
5
+ label: string;
6
+ icon: React.ReactNode;
7
+ }>;
8
+
9
+ interface AuthRoboConfig {
10
+ /** Your AuthRobo instance, e.g. "https://authrobo.com". */
11
+ issuerUrl: string;
12
+ /** Your app's client_id (arc_...). */
13
+ clientId: string;
14
+ /** Where AuthRobo returns the one-time `code` after sign-in. */
15
+ redirectUri: string;
16
+ }
17
+ /** Shape returned by GET /api/v1/config. */
18
+ interface AppConfig {
19
+ name: string;
20
+ brandColor: string | null;
21
+ logoUrl: string | null;
22
+ methods: Method[];
23
+ }
24
+ declare function AuthRoboProvider({ config, children, }: {
25
+ config: AuthRoboConfig;
26
+ children: React.ReactNode;
27
+ }): React.JSX.Element;
28
+ /**
29
+ * Begin sign-in: navigate the browser to AuthRobo's hosted flow for `method`.
30
+ * Social methods hit /api/v1/auth/<provider>/start; "password" opens the hosted
31
+ * email login page. AuthRobo returns to your `redirectUri` with a `code` that
32
+ * your backend exchanges (with your client_secret) for tokens.
33
+ */
34
+ declare function startSignIn(config: AuthRoboConfig, method: Method, opts?: {
35
+ state?: string;
36
+ loginHint?: string;
37
+ }): void;
38
+ /** Fetch an app's public config (branding + enabled methods) for `clientId`. */
39
+ declare function useAppConfig(config?: AuthRoboConfig): {
40
+ data: AppConfig | null;
41
+ loading: boolean;
42
+ error: string | null;
43
+ };
44
+ declare function AuthRoboButton({ provider, config, label, className, style, onClick, }: {
45
+ provider: Method;
46
+ config?: AuthRoboConfig;
47
+ label?: string;
48
+ className?: string;
49
+ style?: React.CSSProperties;
50
+ onClick?: () => void;
51
+ }): React.JSX.Element;
52
+ declare function AuthRoboAuth({ config, mode, open, onClose, title, methods: only, }: {
53
+ config?: AuthRoboConfig;
54
+ /** "inline" renders in place; "modal" renders a centered overlay. */
55
+ mode?: "inline" | "modal";
56
+ /** Only used in modal mode. */
57
+ open?: boolean;
58
+ onClose?: () => void;
59
+ /** Heading; defaults to "Sign in to <app name>". */
60
+ title?: string;
61
+ /** Restrict/reorder which methods to show (defaults to all enabled ones). */
62
+ methods?: Method[];
63
+ }): React.JSX.Element | null;
64
+
65
+ export { type AppConfig, AuthRoboAuth, AuthRoboButton, type AuthRoboConfig, AuthRoboProvider, type Method, PROVIDER_META, startSignIn, useAppConfig };
@@ -0,0 +1,65 @@
1
+ import * as React from 'react';
2
+
3
+ type Method = "google" | "linkedin" | "github" | "apple" | "password";
4
+ declare const PROVIDER_META: Record<Method, {
5
+ label: string;
6
+ icon: React.ReactNode;
7
+ }>;
8
+
9
+ interface AuthRoboConfig {
10
+ /** Your AuthRobo instance, e.g. "https://authrobo.com". */
11
+ issuerUrl: string;
12
+ /** Your app's client_id (arc_...). */
13
+ clientId: string;
14
+ /** Where AuthRobo returns the one-time `code` after sign-in. */
15
+ redirectUri: string;
16
+ }
17
+ /** Shape returned by GET /api/v1/config. */
18
+ interface AppConfig {
19
+ name: string;
20
+ brandColor: string | null;
21
+ logoUrl: string | null;
22
+ methods: Method[];
23
+ }
24
+ declare function AuthRoboProvider({ config, children, }: {
25
+ config: AuthRoboConfig;
26
+ children: React.ReactNode;
27
+ }): React.JSX.Element;
28
+ /**
29
+ * Begin sign-in: navigate the browser to AuthRobo's hosted flow for `method`.
30
+ * Social methods hit /api/v1/auth/<provider>/start; "password" opens the hosted
31
+ * email login page. AuthRobo returns to your `redirectUri` with a `code` that
32
+ * your backend exchanges (with your client_secret) for tokens.
33
+ */
34
+ declare function startSignIn(config: AuthRoboConfig, method: Method, opts?: {
35
+ state?: string;
36
+ loginHint?: string;
37
+ }): void;
38
+ /** Fetch an app's public config (branding + enabled methods) for `clientId`. */
39
+ declare function useAppConfig(config?: AuthRoboConfig): {
40
+ data: AppConfig | null;
41
+ loading: boolean;
42
+ error: string | null;
43
+ };
44
+ declare function AuthRoboButton({ provider, config, label, className, style, onClick, }: {
45
+ provider: Method;
46
+ config?: AuthRoboConfig;
47
+ label?: string;
48
+ className?: string;
49
+ style?: React.CSSProperties;
50
+ onClick?: () => void;
51
+ }): React.JSX.Element;
52
+ declare function AuthRoboAuth({ config, mode, open, onClose, title, methods: only, }: {
53
+ config?: AuthRoboConfig;
54
+ /** "inline" renders in place; "modal" renders a centered overlay. */
55
+ mode?: "inline" | "modal";
56
+ /** Only used in modal mode. */
57
+ open?: boolean;
58
+ onClose?: () => void;
59
+ /** Heading; defaults to "Sign in to <app name>". */
60
+ title?: string;
61
+ /** Restrict/reorder which methods to show (defaults to all enabled ones). */
62
+ methods?: Method[];
63
+ }): React.JSX.Element | null;
64
+
65
+ export { type AppConfig, AuthRoboAuth, AuthRoboButton, type AuthRoboConfig, AuthRoboProvider, type Method, PROVIDER_META, startSignIn, useAppConfig };
package/dist/index.js ADDED
@@ -0,0 +1,216 @@
1
+ "use client";
2
+ "use client";
3
+
4
+ // src/index.tsx
5
+ import { createContext, useContext, useEffect, useState } from "react";
6
+
7
+ // src/icons.tsx
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var sz = { width: 18, height: 18, "aria-hidden": true };
10
+ function GoogleIcon() {
11
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 48", ...sz, children: [
12
+ /* @__PURE__ */ jsx("path", { fill: "#FFC107", d: "M43.6 20.5H42V20H24v8h11.3C33.7 32.9 29.3 36 24 36c-6.6 0-12-5.4-12-12s5.4-12 12-12c3.1 0 5.9 1.2 8 3.1l5.7-5.7C34.6 6.1 29.6 4 24 4 12.9 4 4 12.9 4 24s8.9 20 20 20 20-8.9 20-20c0-1.3-.1-2.3-.4-3.5z" }),
13
+ /* @__PURE__ */ jsx("path", { fill: "#FF3D00", d: "M6.3 14.7l6.6 4.8C14.7 16 19 13 24 13c3.1 0 5.9 1.2 8 3.1l5.7-5.7C34.6 6.1 29.6 4 24 4 16.3 4 9.7 8.3 6.3 14.7z" }),
14
+ /* @__PURE__ */ jsx("path", { fill: "#4CAF50", d: "M24 44c5.2 0 10-2 13.6-5.2l-6.3-5.2C29.2 35 26.7 36 24 36c-5.3 0-9.7-3.4-11.3-8.1l-6.5 5C9.5 39.6 16.2 44 24 44z" }),
15
+ /* @__PURE__ */ jsx("path", { fill: "#1976D2", d: "M43.6 20.5H42V20H24v8h11.3c-.8 2.2-2.2 4.1-4.1 5.6l6.3 5.2C41.9 36 44 30.5 44 24c0-1.3-.1-2.3-.4-3.5z" })
16
+ ] });
17
+ }
18
+ function LinkedInIcon() {
19
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", ...sz, fill: "#0A66C2", children: /* @__PURE__ */ jsx("path", { d: "M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.13 1.45-2.13 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.45v6.29zM5.34 7.43a2.06 2.06 0 1 1 0-4.13 2.06 2.06 0 0 1 0 4.13zM7.12 20.45H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z" }) });
20
+ }
21
+ function GitHubIcon() {
22
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", ...sz, fill: "#181717", children: /* @__PURE__ */ jsx("path", { d: "M12 .3a12 12 0 0 0-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.6-1.4-1.3-1.8-1.3-1.8-1.1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1.1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.8-1.6-2.7-.3-5.5-1.3-5.5-6a4.7 4.7 0 0 1 1.2-3.2c-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.3 1.2a11.5 11.5 0 0 1 6 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 2.9.1 3.2a4.7 4.7 0 0 1 1.2 3.2c0 4.7-2.8 5.7-5.5 6 .4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0 0 12 .3" }) });
23
+ }
24
+ function AppleIcon() {
25
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", ...sz, fill: "#000", children: /* @__PURE__ */ jsx("path", { d: "M17.05 12.04c-.03-2.7 2.2-4 2.3-4.06-1.25-1.84-3.2-2.09-3.9-2.12-1.66-.17-3.24.98-4.08.98-.84 0-2.14-.96-3.52-.93-1.81.03-3.48 1.05-4.41 2.67-1.88 3.27-.48 8.1 1.35 10.76.9 1.3 1.97 2.76 3.38 2.71 1.36-.05 1.87-.88 3.51-.88 1.64 0 2.1.88 3.53.85 1.46-.03 2.38-1.33 3.27-2.63 1.03-1.51 1.46-2.97 1.48-3.05-.03-.01-2.84-1.09-2.87-4.32M14.4 4.2c.74-.9 1.24-2.15 1.1-3.4-1.07.04-2.36.71-3.13 1.61-.69.8-1.29 2.07-1.13 3.29 1.19.09 2.41-.61 3.16-1.5" }) });
26
+ }
27
+ function MailIcon() {
28
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", ...sz, fill: "none", stroke: "#334155", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [
29
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
30
+ /* @__PURE__ */ jsx("path", { d: "m3 7 9 6 9-6" })
31
+ ] });
32
+ }
33
+ var PROVIDER_META = {
34
+ google: { label: "Google", icon: /* @__PURE__ */ jsx(GoogleIcon, {}) },
35
+ linkedin: { label: "LinkedIn", icon: /* @__PURE__ */ jsx(LinkedInIcon, {}) },
36
+ github: { label: "GitHub", icon: /* @__PURE__ */ jsx(GitHubIcon, {}) },
37
+ apple: { label: "Apple", icon: /* @__PURE__ */ jsx(AppleIcon, {}) },
38
+ password: { label: "email", icon: /* @__PURE__ */ jsx(MailIcon, {}) }
39
+ };
40
+
41
+ // src/index.tsx
42
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
43
+ var ConfigContext = createContext(null);
44
+ function AuthRoboProvider({
45
+ config,
46
+ children
47
+ }) {
48
+ return /* @__PURE__ */ jsx2(ConfigContext.Provider, { value: config, children });
49
+ }
50
+ function useResolvedConfig(override) {
51
+ const ctx = useContext(ConfigContext);
52
+ const c = override ?? ctx;
53
+ if (!c) {
54
+ throw new Error(
55
+ "@authrobo/react: wrap your app in <AuthRoboProvider config={...}> or pass a `config` prop."
56
+ );
57
+ }
58
+ return c;
59
+ }
60
+ function randomState() {
61
+ const b = new Uint8Array(16);
62
+ (globalThis.crypto ?? globalThis.crypto).getRandomValues(b);
63
+ return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
64
+ }
65
+ function startSignIn(config, method, opts) {
66
+ const base = config.issuerUrl.replace(/\/$/, "");
67
+ const state = opts?.state ?? randomState();
68
+ try {
69
+ sessionStorage.setItem("authrobo:state", state);
70
+ } catch {
71
+ }
72
+ const params = new URLSearchParams({
73
+ client_id: config.clientId,
74
+ redirect_uri: config.redirectUri,
75
+ state
76
+ });
77
+ if (opts?.loginHint) params.set("login_hint", opts.loginHint);
78
+ const url = method === "password" ? `${base}/login?${params.toString()}` : `${base}/api/v1/auth/${method}/start?${params.toString()}`;
79
+ window.location.assign(url);
80
+ }
81
+ function useAppConfig(config) {
82
+ const c = useResolvedConfig(config);
83
+ const [state, setState] = useState({
84
+ data: null,
85
+ loading: true,
86
+ error: null
87
+ });
88
+ useEffect(() => {
89
+ let active = true;
90
+ setState({ data: null, loading: true, error: null });
91
+ const base = c.issuerUrl.replace(/\/$/, "");
92
+ fetch(`${base}/api/v1/config?client_id=${encodeURIComponent(c.clientId)}`).then((r) => r.ok ? r.json() : Promise.reject(new Error(`config ${r.status}`))).then((j) => {
93
+ if (!active) return;
94
+ setState({
95
+ data: { name: j.name, brandColor: j.brand_color, logoUrl: j.logo_url, methods: j.methods },
96
+ loading: false,
97
+ error: null
98
+ });
99
+ }).catch((e) => active && setState({ data: null, loading: false, error: String(e?.message ?? e) }));
100
+ return () => {
101
+ active = false;
102
+ };
103
+ }, [c.issuerUrl, c.clientId]);
104
+ return state;
105
+ }
106
+ var STYLE_ID = "authrobo-react-styles";
107
+ var CSS = `
108
+ .authrobo-btn{display:flex;width:100%;align-items:center;justify-content:center;gap:10px;
109
+ border:1px solid #d1d5db;background:#fff;color:#111827;border-radius:10px;padding:10px 14px;
110
+ font:600 14px/1.2 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer;
111
+ transition:background .15s ease,border-color .15s ease,box-shadow .15s ease;}
112
+ .authrobo-btn:hover{background:#f8fafc;border-color:#9ca3af;}
113
+ .authrobo-btn:focus-visible{outline:2px solid #2b8aff;outline-offset:2px;}
114
+ .authrobo-stack{display:flex;flex-direction:column;gap:10px;}
115
+ .authrobo-divider{display:flex;align-items:center;gap:10px;color:#9ca3af;font:500 12px system-ui,sans-serif;}
116
+ .authrobo-divider::before,.authrobo-divider::after{content:"";height:1px;flex:1;background:#e5e7eb;}
117
+ .authrobo-overlay{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;
118
+ justify-content:center;padding:16px;background:rgba(2,6,23,.55);backdrop-filter:blur(2px);
119
+ animation:authrobo-fade .16s ease-out both;}
120
+ .authrobo-card{position:relative;width:100%;max-width:360px;background:#fff;border-radius:16px;
121
+ padding:24px;box-shadow:0 20px 60px -12px rgba(2,6,23,.4);animation:authrobo-pop .2s cubic-bezier(.16,1,.3,1) both;}
122
+ .authrobo-close{position:absolute;top:12px;right:12px;border:0;background:transparent;cursor:pointer;
123
+ color:#94a3b8;font-size:18px;line-height:1;padding:4px;}
124
+ .authrobo-close:hover{color:#334155;}
125
+ @keyframes authrobo-fade{from{opacity:0}to{opacity:1}}
126
+ @keyframes authrobo-pop{from{opacity:0;transform:translateY(8px) scale(.97)}to{opacity:1;transform:none}}
127
+ @media (prefers-reduced-motion: reduce){.authrobo-overlay,.authrobo-card{animation:none}}
128
+ `;
129
+ function useInjectStyles() {
130
+ useEffect(() => {
131
+ if (typeof document === "undefined" || document.getElementById(STYLE_ID)) return;
132
+ const el = document.createElement("style");
133
+ el.id = STYLE_ID;
134
+ el.textContent = CSS;
135
+ document.head.appendChild(el);
136
+ }, []);
137
+ }
138
+ function AuthRoboButton({
139
+ provider,
140
+ config,
141
+ label,
142
+ className,
143
+ style,
144
+ onClick
145
+ }) {
146
+ useInjectStyles();
147
+ const c = useResolvedConfig(config);
148
+ const meta = PROVIDER_META[provider];
149
+ return /* @__PURE__ */ jsxs2(
150
+ "button",
151
+ {
152
+ type: "button",
153
+ className: className ? `authrobo-btn ${className}` : "authrobo-btn",
154
+ style,
155
+ onClick: () => {
156
+ onClick?.();
157
+ startSignIn(c, provider);
158
+ },
159
+ children: [
160
+ /* @__PURE__ */ jsx2("span", { style: { display: "inline-flex", alignItems: "center" }, children: meta.icon }),
161
+ label ?? `Continue with ${meta.label}`
162
+ ]
163
+ }
164
+ );
165
+ }
166
+ function AuthRoboAuth({
167
+ config,
168
+ mode = "inline",
169
+ open = true,
170
+ onClose,
171
+ title,
172
+ methods: only
173
+ }) {
174
+ useInjectStyles();
175
+ const c = useResolvedConfig(config);
176
+ const { data, loading, error } = useAppConfig(c);
177
+ useEffect(() => {
178
+ if (mode !== "modal" || !open) return;
179
+ const onKey = (e) => e.key === "Escape" && onClose?.();
180
+ document.addEventListener("keydown", onKey);
181
+ return () => document.removeEventListener("keydown", onKey);
182
+ }, [mode, open, onClose]);
183
+ if (mode === "modal" && !open) return null;
184
+ const available = data?.methods ?? [];
185
+ const shown = (only ? only.filter((m) => available.includes(m)) : available).filter(
186
+ (m) => m in PROVIDER_META
187
+ );
188
+ const social = shown.filter((m) => m !== "password");
189
+ const hasPassword = shown.includes("password");
190
+ const panel = /* @__PURE__ */ jsxs2("div", { className: "authrobo-stack", children: [
191
+ (data?.logoUrl || data?.name) && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 4 }, children: [
192
+ data?.logoUrl && /* @__PURE__ */ jsx2("img", { src: data.logoUrl, alt: "", width: 28, height: 28, style: { borderRadius: 6 } }),
193
+ /* @__PURE__ */ jsx2("span", { style: { font: "700 16px system-ui,sans-serif", color: "#0f172a" }, children: title ?? `Sign in${data?.name ? ` to ${data.name}` : ""}` })
194
+ ] }),
195
+ loading && /* @__PURE__ */ jsx2("p", { style: { color: "#64748b", font: "14px system-ui,sans-serif" }, children: "Loading sign-in options\u2026" }),
196
+ error && /* @__PURE__ */ jsx2("p", { style: { color: "#b91c1c", font: "14px system-ui,sans-serif" }, children: "Couldn't load sign-in options." }),
197
+ social.map((m) => /* @__PURE__ */ jsx2(AuthRoboButton, { provider: m, config: c }, m)),
198
+ hasPassword && social.length > 0 && /* @__PURE__ */ jsx2("div", { className: "authrobo-divider", children: "or" }),
199
+ hasPassword && /* @__PURE__ */ jsx2(AuthRoboButton, { provider: "password", config: c, label: "Continue with email" }),
200
+ !loading && !error && shown.length === 0 && /* @__PURE__ */ jsx2("p", { style: { color: "#64748b", font: "14px system-ui,sans-serif" }, children: "No sign-in methods are enabled for this app." })
201
+ ] });
202
+ if (mode === "inline") return panel;
203
+ return /* @__PURE__ */ jsx2("div", { className: "authrobo-overlay", onClick: () => onClose?.(), children: /* @__PURE__ */ jsxs2("div", { className: "authrobo-card", role: "dialog", "aria-modal": "true", onClick: (e) => e.stopPropagation(), children: [
204
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "authrobo-close", "aria-label": "Close", onClick: () => onClose?.(), children: "\u2715" }),
205
+ panel
206
+ ] }) });
207
+ }
208
+ export {
209
+ AuthRoboAuth,
210
+ AuthRoboButton,
211
+ AuthRoboProvider,
212
+ PROVIDER_META,
213
+ startSignIn,
214
+ useAppConfig
215
+ };
216
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.tsx","../src/icons.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { createContext, useContext, useEffect, useState } from \"react\";\nimport { PROVIDER_META, type Method } from \"./icons\";\n\nexport type { Method } from \"./icons\";\nexport { PROVIDER_META } from \"./icons\";\n\nexport interface AuthRoboConfig {\n /** Your AuthRobo instance, e.g. \"https://authrobo.com\". */\n issuerUrl: string;\n /** Your app's client_id (arc_...). */\n clientId: string;\n /** Where AuthRobo returns the one-time `code` after sign-in. */\n redirectUri: string;\n}\n\n/** Shape returned by GET /api/v1/config. */\nexport interface AppConfig {\n name: string;\n brandColor: string | null;\n logoUrl: string | null;\n methods: Method[];\n}\n\nconst ConfigContext = createContext<AuthRoboConfig | null>(null);\n\nexport function AuthRoboProvider({\n config,\n children,\n}: {\n config: AuthRoboConfig;\n children: React.ReactNode;\n}) {\n return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>;\n}\n\nfunction useResolvedConfig(override?: AuthRoboConfig): AuthRoboConfig {\n const ctx = useContext(ConfigContext);\n const c = override ?? ctx;\n if (!c) {\n throw new Error(\n \"@authrobo/react: wrap your app in <AuthRoboProvider config={...}> or pass a `config` prop.\",\n );\n }\n return c;\n}\n\nfunction randomState(): string {\n const b = new Uint8Array(16);\n (globalThis.crypto ?? (globalThis as { crypto: Crypto }).crypto).getRandomValues(b);\n return Array.from(b, (x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * Begin sign-in: navigate the browser to AuthRobo's hosted flow for `method`.\n * Social methods hit /api/v1/auth/<provider>/start; \"password\" opens the hosted\n * email login page. AuthRobo returns to your `redirectUri` with a `code` that\n * your backend exchanges (with your client_secret) for tokens.\n */\nexport function startSignIn(\n config: AuthRoboConfig,\n method: Method,\n opts?: { state?: string; loginHint?: string },\n): void {\n const base = config.issuerUrl.replace(/\\/$/, \"\");\n const state = opts?.state ?? randomState();\n try {\n sessionStorage.setItem(\"authrobo:state\", state);\n } catch {\n /* storage unavailable — state is still sent, just not persisted for a check */\n }\n const params = new URLSearchParams({\n client_id: config.clientId,\n redirect_uri: config.redirectUri,\n state,\n });\n if (opts?.loginHint) params.set(\"login_hint\", opts.loginHint);\n const url =\n method === \"password\"\n ? `${base}/login?${params.toString()}`\n : `${base}/api/v1/auth/${method}/start?${params.toString()}`;\n window.location.assign(url);\n}\n\n/** Fetch an app's public config (branding + enabled methods) for `clientId`. */\nexport function useAppConfig(config?: AuthRoboConfig): {\n data: AppConfig | null;\n loading: boolean;\n error: string | null;\n} {\n const c = useResolvedConfig(config);\n const [state, setState] = useState<{ data: AppConfig | null; loading: boolean; error: string | null }>({\n data: null,\n loading: true,\n error: null,\n });\n\n useEffect(() => {\n let active = true;\n setState({ data: null, loading: true, error: null });\n const base = c.issuerUrl.replace(/\\/$/, \"\");\n fetch(`${base}/api/v1/config?client_id=${encodeURIComponent(c.clientId)}`)\n .then((r) => (r.ok ? r.json() : Promise.reject(new Error(`config ${r.status}`))))\n .then((j) => {\n if (!active) return;\n setState({\n data: { name: j.name, brandColor: j.brand_color, logoUrl: j.logo_url, methods: j.methods },\n loading: false,\n error: null,\n });\n })\n .catch((e) => active && setState({ data: null, loading: false, error: String(e?.message ?? e) }));\n return () => {\n active = false;\n };\n }, [c.issuerUrl, c.clientId]);\n\n return state;\n}\n\n/* ----------------------------- Styling ---------------------------------- */\n\nconst STYLE_ID = \"authrobo-react-styles\";\nconst CSS = `\n.authrobo-btn{display:flex;width:100%;align-items:center;justify-content:center;gap:10px;\n border:1px solid #d1d5db;background:#fff;color:#111827;border-radius:10px;padding:10px 14px;\n font:600 14px/1.2 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer;\n transition:background .15s ease,border-color .15s ease,box-shadow .15s ease;}\n.authrobo-btn:hover{background:#f8fafc;border-color:#9ca3af;}\n.authrobo-btn:focus-visible{outline:2px solid #2b8aff;outline-offset:2px;}\n.authrobo-stack{display:flex;flex-direction:column;gap:10px;}\n.authrobo-divider{display:flex;align-items:center;gap:10px;color:#9ca3af;font:500 12px system-ui,sans-serif;}\n.authrobo-divider::before,.authrobo-divider::after{content:\"\";height:1px;flex:1;background:#e5e7eb;}\n.authrobo-overlay{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;\n justify-content:center;padding:16px;background:rgba(2,6,23,.55);backdrop-filter:blur(2px);\n animation:authrobo-fade .16s ease-out both;}\n.authrobo-card{position:relative;width:100%;max-width:360px;background:#fff;border-radius:16px;\n padding:24px;box-shadow:0 20px 60px -12px rgba(2,6,23,.4);animation:authrobo-pop .2s cubic-bezier(.16,1,.3,1) both;}\n.authrobo-close{position:absolute;top:12px;right:12px;border:0;background:transparent;cursor:pointer;\n color:#94a3b8;font-size:18px;line-height:1;padding:4px;}\n.authrobo-close:hover{color:#334155;}\n@keyframes authrobo-fade{from{opacity:0}to{opacity:1}}\n@keyframes authrobo-pop{from{opacity:0;transform:translateY(8px) scale(.97)}to{opacity:1;transform:none}}\n@media (prefers-reduced-motion: reduce){.authrobo-overlay,.authrobo-card{animation:none}}\n`;\n\nfunction useInjectStyles() {\n useEffect(() => {\n if (typeof document === \"undefined\" || document.getElementById(STYLE_ID)) return;\n const el = document.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = CSS;\n document.head.appendChild(el);\n }, []);\n}\n\n/* ----------------------------- Button ----------------------------------- */\n\nexport function AuthRoboButton({\n provider,\n config,\n label,\n className,\n style,\n onClick,\n}: {\n provider: Method;\n config?: AuthRoboConfig;\n label?: string;\n className?: string;\n style?: React.CSSProperties;\n onClick?: () => void;\n}) {\n useInjectStyles();\n const c = useResolvedConfig(config);\n const meta = PROVIDER_META[provider];\n return (\n <button\n type=\"button\"\n className={className ? `authrobo-btn ${className}` : \"authrobo-btn\"}\n style={style}\n onClick={() => {\n onClick?.();\n startSignIn(c, provider);\n }}\n >\n <span style={{ display: \"inline-flex\", alignItems: \"center\" }}>{meta.icon}</span>\n {label ?? `Continue with ${meta.label}`}\n </button>\n );\n}\n\n/* ---------------------- Auth modal / inline panel ----------------------- */\n\nexport function AuthRoboAuth({\n config,\n mode = \"inline\",\n open = true,\n onClose,\n title,\n methods: only,\n}: {\n config?: AuthRoboConfig;\n /** \"inline\" renders in place; \"modal\" renders a centered overlay. */\n mode?: \"inline\" | \"modal\";\n /** Only used in modal mode. */\n open?: boolean;\n onClose?: () => void;\n /** Heading; defaults to \"Sign in to <app name>\". */\n title?: string;\n /** Restrict/reorder which methods to show (defaults to all enabled ones). */\n methods?: Method[];\n}) {\n useInjectStyles();\n const c = useResolvedConfig(config);\n const { data, loading, error } = useAppConfig(c);\n\n useEffect(() => {\n if (mode !== \"modal\" || !open) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && onClose?.();\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [mode, open, onClose]);\n\n if (mode === \"modal\" && !open) return null;\n\n const available = data?.methods ?? [];\n const shown = (only ? only.filter((m) => available.includes(m)) : available).filter(\n (m) => m in PROVIDER_META,\n );\n const social = shown.filter((m) => m !== \"password\");\n const hasPassword = shown.includes(\"password\");\n\n const panel = (\n <div className=\"authrobo-stack\">\n {(data?.logoUrl || data?.name) && (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 10, marginBottom: 4 }}>\n {data?.logoUrl && (\n <img src={data.logoUrl} alt=\"\" width={28} height={28} style={{ borderRadius: 6 }} />\n )}\n <span style={{ font: \"700 16px system-ui,sans-serif\", color: \"#0f172a\" }}>\n {title ?? `Sign in${data?.name ? ` to ${data.name}` : \"\"}`}\n </span>\n </div>\n )}\n\n {loading && <p style={{ color: \"#64748b\", font: \"14px system-ui,sans-serif\" }}>Loading sign-in options…</p>}\n {error && (\n <p style={{ color: \"#b91c1c\", font: \"14px system-ui,sans-serif\" }}>\n Couldn&apos;t load sign-in options.\n </p>\n )}\n\n {social.map((m) => (\n <AuthRoboButton key={m} provider={m} config={c} />\n ))}\n {hasPassword && social.length > 0 && <div className=\"authrobo-divider\">or</div>}\n {hasPassword && <AuthRoboButton provider=\"password\" config={c} label=\"Continue with email\" />}\n\n {!loading && !error && shown.length === 0 && (\n <p style={{ color: \"#64748b\", font: \"14px system-ui,sans-serif\" }}>\n No sign-in methods are enabled for this app.\n </p>\n )}\n </div>\n );\n\n if (mode === \"inline\") return panel;\n\n return (\n <div className=\"authrobo-overlay\" onClick={() => onClose?.()}>\n <div className=\"authrobo-card\" role=\"dialog\" aria-modal=\"true\" onClick={(e) => e.stopPropagation()}>\n <button type=\"button\" className=\"authrobo-close\" aria-label=\"Close\" onClick={() => onClose?.()}>\n ✕\n </button>\n {panel}\n </div>\n </div>\n );\n}\n","import * as React from \"react\";\n\n// Sign-in methods AuthRobo can surface. \"password\" routes to the hosted email\n// login page; the rest are social OAuth providers.\nexport type Method = \"google\" | \"linkedin\" | \"github\" | \"apple\" | \"password\";\n\nconst sz = { width: 18, height: 18, \"aria-hidden\": true } as const;\n\nfunction GoogleIcon() {\n return (\n <svg viewBox=\"0 0 48 48\" {...sz}>\n <path fill=\"#FFC107\" d=\"M43.6 20.5H42V20H24v8h11.3C33.7 32.9 29.3 36 24 36c-6.6 0-12-5.4-12-12s5.4-12 12-12c3.1 0 5.9 1.2 8 3.1l5.7-5.7C34.6 6.1 29.6 4 24 4 12.9 4 4 12.9 4 24s8.9 20 20 20 20-8.9 20-20c0-1.3-.1-2.3-.4-3.5z\" />\n <path fill=\"#FF3D00\" d=\"M6.3 14.7l6.6 4.8C14.7 16 19 13 24 13c3.1 0 5.9 1.2 8 3.1l5.7-5.7C34.6 6.1 29.6 4 24 4 16.3 4 9.7 8.3 6.3 14.7z\" />\n <path fill=\"#4CAF50\" d=\"M24 44c5.2 0 10-2 13.6-5.2l-6.3-5.2C29.2 35 26.7 36 24 36c-5.3 0-9.7-3.4-11.3-8.1l-6.5 5C9.5 39.6 16.2 44 24 44z\" />\n <path fill=\"#1976D2\" d=\"M43.6 20.5H42V20H24v8h11.3c-.8 2.2-2.2 4.1-4.1 5.6l6.3 5.2C41.9 36 44 30.5 44 24c0-1.3-.1-2.3-.4-3.5z\" />\n </svg>\n );\n}\n\nfunction LinkedInIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" {...sz} fill=\"#0A66C2\">\n <path d=\"M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.13 1.45-2.13 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.45v6.29zM5.34 7.43a2.06 2.06 0 1 1 0-4.13 2.06 2.06 0 0 1 0 4.13zM7.12 20.45H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z\" />\n </svg>\n );\n}\n\nfunction GitHubIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" {...sz} fill=\"#181717\">\n <path d=\"M12 .3a12 12 0 0 0-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.6-1.4-1.3-1.8-1.3-1.8-1.1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1.1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.8-1.6-2.7-.3-5.5-1.3-5.5-6a4.7 4.7 0 0 1 1.2-3.2c-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.3 1.2a11.5 11.5 0 0 1 6 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 2.9.1 3.2a4.7 4.7 0 0 1 1.2 3.2c0 4.7-2.8 5.7-5.5 6 .4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0 0 12 .3\" />\n </svg>\n );\n}\n\nfunction AppleIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" {...sz} fill=\"#000\">\n <path d=\"M17.05 12.04c-.03-2.7 2.2-4 2.3-4.06-1.25-1.84-3.2-2.09-3.9-2.12-1.66-.17-3.24.98-4.08.98-.84 0-2.14-.96-3.52-.93-1.81.03-3.48 1.05-4.41 2.67-1.88 3.27-.48 8.1 1.35 10.76.9 1.3 1.97 2.76 3.38 2.71 1.36-.05 1.87-.88 3.51-.88 1.64 0 2.1.88 3.53.85 1.46-.03 2.38-1.33 3.27-2.63 1.03-1.51 1.46-2.97 1.48-3.05-.03-.01-2.84-1.09-2.87-4.32M14.4 4.2c.74-.9 1.24-2.15 1.1-3.4-1.07.04-2.36.71-3.13 1.61-.69.8-1.29 2.07-1.13 3.29 1.19.09 2.41-.61 3.16-1.5\" />\n </svg>\n );\n}\n\nfunction MailIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" {...sz} fill=\"none\" stroke=\"#334155\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" />\n <path d=\"m3 7 9 6 9-6\" />\n </svg>\n );\n}\n\nexport const PROVIDER_META: Record<Method, { label: string; icon: React.ReactNode }> = {\n google: { label: \"Google\", icon: <GoogleIcon /> },\n linkedin: { label: \"LinkedIn\", icon: <LinkedInIcon /> },\n github: { label: \"GitHub\", icon: <GitHubIcon /> },\n apple: { label: \"Apple\", icon: <AppleIcon /> },\n password: { label: \"email\", icon: <MailIcon /> },\n};\n"],"mappings":";;;;AAGA,SAAS,eAAe,YAAY,WAAW,gBAAgB;;;ACO3D,SACE,KADF;AAJJ,IAAM,KAAK,EAAE,OAAO,IAAI,QAAQ,IAAI,eAAe,KAAK;AAExD,SAAS,aAAa;AACpB,SACE,qBAAC,SAAI,SAAQ,aAAa,GAAG,IAC3B;AAAA,wBAAC,UAAK,MAAK,WAAU,GAAE,0MAAyM;AAAA,IAChO,oBAAC,UAAK,MAAK,WAAU,GAAE,mHAAkH;AAAA,IACzI,oBAAC,UAAK,MAAK,WAAU,GAAE,oHAAmH;AAAA,IAC1I,oBAAC,UAAK,MAAK,WAAU,GAAE,yGAAwG;AAAA,KACjI;AAEJ;AAEA,SAAS,eAAe;AACtB,SACE,oBAAC,SAAI,SAAQ,aAAa,GAAG,IAAI,MAAK,WACpC,8BAAC,UAAK,GAAE,oXAAmX,GAC7X;AAEJ;AAEA,SAAS,aAAa;AACpB,SACE,oBAAC,SAAI,SAAQ,aAAa,GAAG,IAAI,MAAK,WACpC,8BAAC,UAAK,GAAE,sZAAqZ,GAC/Z;AAEJ;AAEA,SAAS,YAAY;AACnB,SACE,oBAAC,SAAI,SAAQ,aAAa,GAAG,IAAI,MAAK,QACpC,8BAAC,UAAK,GAAE,gcAA+b,GACzc;AAEJ;AAEA,SAAS,WAAW;AAClB,SACE,qBAAC,SAAI,SAAQ,aAAa,GAAG,IAAI,MAAK,QAAO,QAAO,WAAU,aAAa,GAAG,eAAc,SAAQ,gBAAe,SACjH;AAAA,wBAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI;AAAA,IAChD,oBAAC,UAAK,GAAE,gBAAe;AAAA,KACzB;AAEJ;AAEO,IAAM,gBAA0E;AAAA,EACrF,QAAQ,EAAE,OAAO,UAAU,MAAM,oBAAC,cAAW,EAAG;AAAA,EAChD,UAAU,EAAE,OAAO,YAAY,MAAM,oBAAC,gBAAa,EAAG;AAAA,EACtD,QAAQ,EAAE,OAAO,UAAU,MAAM,oBAAC,cAAW,EAAG;AAAA,EAChD,OAAO,EAAE,OAAO,SAAS,MAAM,oBAAC,aAAU,EAAG;AAAA,EAC7C,UAAU,EAAE,OAAO,SAAS,MAAM,oBAAC,YAAS,EAAG;AACjD;;;ADvBS,gBAAAA,MAgJL,QAAAC,aAhJK;AATT,IAAM,gBAAgB,cAAqC,IAAI;AAExD,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AACF,GAGG;AACD,SAAO,gBAAAD,KAAC,cAAc,UAAd,EAAuB,OAAO,QAAS,UAAS;AAC1D;AAEA,SAAS,kBAAkB,UAA2C;AACpE,QAAM,MAAM,WAAW,aAAa;AACpC,QAAM,IAAI,YAAY;AACtB,MAAI,CAAC,GAAG;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,cAAsB;AAC7B,QAAM,IAAI,IAAI,WAAW,EAAE;AAC3B,GAAC,WAAW,UAAW,WAAkC,QAAQ,gBAAgB,CAAC;AAClF,SAAO,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AACtE;AAQO,SAAS,YACd,QACA,QACA,MACM;AACN,QAAM,OAAO,OAAO,UAAU,QAAQ,OAAO,EAAE;AAC/C,QAAM,QAAQ,MAAM,SAAS,YAAY;AACzC,MAAI;AACF,mBAAe,QAAQ,kBAAkB,KAAK;AAAA,EAChD,QAAQ;AAAA,EAER;AACA,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,WAAW,OAAO;AAAA,IAClB,cAAc,OAAO;AAAA,IACrB;AAAA,EACF,CAAC;AACD,MAAI,MAAM,UAAW,QAAO,IAAI,cAAc,KAAK,SAAS;AAC5D,QAAM,MACJ,WAAW,aACP,GAAG,IAAI,UAAU,OAAO,SAAS,CAAC,KAClC,GAAG,IAAI,gBAAgB,MAAM,UAAU,OAAO,SAAS,CAAC;AAC9D,SAAO,SAAS,OAAO,GAAG;AAC5B;AAGO,SAAS,aAAa,QAI3B;AACA,QAAM,IAAI,kBAAkB,MAAM;AAClC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA6E;AAAA,IACrG,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,EACT,CAAC;AAED,YAAU,MAAM;AACd,QAAI,SAAS;AACb,aAAS,EAAE,MAAM,MAAM,SAAS,MAAM,OAAO,KAAK,CAAC;AACnD,UAAM,OAAO,EAAE,UAAU,QAAQ,OAAO,EAAE;AAC1C,UAAM,GAAG,IAAI,4BAA4B,mBAAmB,EAAE,QAAQ,CAAC,EAAE,EACtE,KAAK,CAAC,MAAO,EAAE,KAAK,EAAE,KAAK,IAAI,QAAQ,OAAO,IAAI,MAAM,UAAU,EAAE,MAAM,EAAE,CAAC,CAAE,EAC/E,KAAK,CAAC,MAAM;AACX,UAAI,CAAC,OAAQ;AACb,eAAS;AAAA,QACP,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,EAAE,aAAa,SAAS,EAAE,UAAU,SAAS,EAAE,QAAQ;AAAA,QACzF,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC,EACA,MAAM,CAAC,MAAM,UAAU,SAAS,EAAE,MAAM,MAAM,SAAS,OAAO,OAAO,OAAO,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;AAClG,WAAO,MAAM;AACX,eAAS;AAAA,IACX;AAAA,EACF,GAAG,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC;AAE5B,SAAO;AACT;AAIA,IAAM,WAAW;AACjB,IAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBZ,SAAS,kBAAkB;AACzB,YAAU,MAAM;AACd,QAAI,OAAO,aAAa,eAAe,SAAS,eAAe,QAAQ,EAAG;AAC1E,UAAM,KAAK,SAAS,cAAc,OAAO;AACzC,OAAG,KAAK;AACR,OAAG,cAAc;AACjB,aAAS,KAAK,YAAY,EAAE;AAAA,EAC9B,GAAG,CAAC,CAAC;AACP;AAIO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,kBAAgB;AAChB,QAAM,IAAI,kBAAkB,MAAM;AAClC,QAAM,OAAO,cAAc,QAAQ;AACnC,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAW,YAAY,gBAAgB,SAAS,KAAK;AAAA,MACrD;AAAA,MACA,SAAS,MAAM;AACb,kBAAU;AACV,oBAAY,GAAG,QAAQ;AAAA,MACzB;AAAA,MAEA;AAAA,wBAAAD,KAAC,UAAK,OAAO,EAAE,SAAS,eAAe,YAAY,SAAS,GAAI,eAAK,MAAK;AAAA,QACzE,SAAS,iBAAiB,KAAK,KAAK;AAAA;AAAA;AAAA,EACvC;AAEJ;AAIO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,OAAO;AAAA,EACP,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,SAAS;AACX,GAWG;AACD,kBAAgB;AAChB,QAAM,IAAI,kBAAkB,MAAM;AAClC,QAAM,EAAE,MAAM,SAAS,MAAM,IAAI,aAAa,CAAC;AAE/C,YAAU,MAAM;AACd,QAAI,SAAS,WAAW,CAAC,KAAM;AAC/B,UAAM,QAAQ,CAAC,MAAqB,EAAE,QAAQ,YAAY,UAAU;AACpE,aAAS,iBAAiB,WAAW,KAAK;AAC1C,WAAO,MAAM,SAAS,oBAAoB,WAAW,KAAK;AAAA,EAC5D,GAAG,CAAC,MAAM,MAAM,OAAO,CAAC;AAExB,MAAI,SAAS,WAAW,CAAC,KAAM,QAAO;AAEtC,QAAM,YAAY,MAAM,WAAW,CAAC;AACpC,QAAM,SAAS,OAAO,KAAK,OAAO,CAAC,MAAM,UAAU,SAAS,CAAC,CAAC,IAAI,WAAW;AAAA,IAC3E,CAAC,MAAM,KAAK;AAAA,EACd;AACA,QAAM,SAAS,MAAM,OAAO,CAAC,MAAM,MAAM,UAAU;AACnD,QAAM,cAAc,MAAM,SAAS,UAAU;AAE7C,QAAM,QACJ,gBAAAC,MAAC,SAAI,WAAU,kBACX;AAAA,WAAM,WAAW,MAAM,SACvB,gBAAAA,MAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,IAAI,cAAc,EAAE,GAC3E;AAAA,YAAM,WACL,gBAAAD,KAAC,SAAI,KAAK,KAAK,SAAS,KAAI,IAAG,OAAO,IAAI,QAAQ,IAAI,OAAO,EAAE,cAAc,EAAE,GAAG;AAAA,MAEpF,gBAAAA,KAAC,UAAK,OAAO,EAAE,MAAM,iCAAiC,OAAO,UAAU,GACpE,mBAAS,UAAU,MAAM,OAAO,OAAO,KAAK,IAAI,KAAK,EAAE,IAC1D;AAAA,OACF;AAAA,IAGD,WAAW,gBAAAA,KAAC,OAAE,OAAO,EAAE,OAAO,WAAW,MAAM,4BAA4B,GAAG,2CAAwB;AAAA,IACtG,SACC,gBAAAA,KAAC,OAAE,OAAO,EAAE,OAAO,WAAW,MAAM,4BAA4B,GAAG,4CAEnE;AAAA,IAGD,OAAO,IAAI,CAAC,MACX,gBAAAA,KAAC,kBAAuB,UAAU,GAAG,QAAQ,KAAxB,CAA2B,CACjD;AAAA,IACA,eAAe,OAAO,SAAS,KAAK,gBAAAA,KAAC,SAAI,WAAU,oBAAmB,gBAAE;AAAA,IACxE,eAAe,gBAAAA,KAAC,kBAAe,UAAS,YAAW,QAAQ,GAAG,OAAM,uBAAsB;AAAA,IAE1F,CAAC,WAAW,CAAC,SAAS,MAAM,WAAW,KACtC,gBAAAA,KAAC,OAAE,OAAO,EAAE,OAAO,WAAW,MAAM,4BAA4B,GAAG,0DAEnE;AAAA,KAEJ;AAGF,MAAI,SAAS,SAAU,QAAO;AAE9B,SACE,gBAAAA,KAAC,SAAI,WAAU,oBAAmB,SAAS,MAAM,UAAU,GACzD,0BAAAC,MAAC,SAAI,WAAU,iBAAgB,MAAK,UAAS,cAAW,QAAO,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAC/F;AAAA,oBAAAD,KAAC,YAAO,MAAK,UAAS,WAAU,kBAAiB,cAAW,SAAQ,SAAS,MAAM,UAAU,GAAG,oBAEhG;AAAA,IACC;AAAA,KACH,GACF;AAEJ;","names":["jsx","jsxs"]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@authrobo/react",
3
+ "version": "0.1.0",
4
+ "description": "React sign-in buttons and a configurable auth modal for AuthRobo — Google, LinkedIn, GitHub, Apple, and email.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "files": ["dist", "README.md"],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "typecheck": "tsc --noEmit",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "peerDependencies": {
25
+ "react": ">=18",
26
+ "react-dom": ">=18"
27
+ },
28
+ "devDependencies": {
29
+ "@types/react": "^18.3.12",
30
+ "react": "^18.3.1",
31
+ "react-dom": "^18.3.1",
32
+ "tsup": "^8.3.5",
33
+ "typescript": "^5.6.3"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "keywords": [
39
+ "authrobo",
40
+ "authentication",
41
+ "oauth",
42
+ "react",
43
+ "sign-in",
44
+ "login",
45
+ "google",
46
+ "linkedin",
47
+ "github",
48
+ "apple"
49
+ ]
50
+ }