@authrobo/react 0.1.0 → 0.2.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/dist/index.cjs +86 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -6
- package/dist/index.d.ts +11 -6
- package/dist/index.js +84 -25
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -27,7 +27,8 @@ __export(index_exports, {
|
|
|
27
27
|
AuthRoboProvider: () => AuthRoboProvider,
|
|
28
28
|
PROVIDER_META: () => PROVIDER_META,
|
|
29
29
|
startSignIn: () => startSignIn,
|
|
30
|
-
useAppConfig: () => useAppConfig
|
|
30
|
+
useAppConfig: () => useAppConfig,
|
|
31
|
+
useLastMethod: () => useLastMethod
|
|
31
32
|
});
|
|
32
33
|
module.exports = __toCommonJS(index_exports);
|
|
33
34
|
var import_react = require("react");
|
|
@@ -63,7 +64,8 @@ var PROVIDER_META = {
|
|
|
63
64
|
linkedin: { label: "LinkedIn", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkedInIcon, {}) },
|
|
64
65
|
github: { label: "GitHub", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitHubIcon, {}) },
|
|
65
66
|
apple: { label: "Apple", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppleIcon, {}) },
|
|
66
|
-
password: { label: "email", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MailIcon, {}) }
|
|
67
|
+
password: { label: "email", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MailIcon, {}) },
|
|
68
|
+
magic_link: { label: "magic link", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MailIcon, {}) }
|
|
67
69
|
};
|
|
68
70
|
|
|
69
71
|
// src/index.tsx
|
|
@@ -90,6 +92,7 @@ function randomState() {
|
|
|
90
92
|
(globalThis.crypto ?? globalThis.crypto).getRandomValues(b);
|
|
91
93
|
return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
92
94
|
}
|
|
95
|
+
var EMAIL_METHODS = ["password", "magic_link"];
|
|
93
96
|
function startSignIn(config, method, opts) {
|
|
94
97
|
const base = config.issuerUrl.replace(/\/$/, "");
|
|
95
98
|
const state = opts?.state ?? randomState();
|
|
@@ -103,9 +106,24 @@ function startSignIn(config, method, opts) {
|
|
|
103
106
|
state
|
|
104
107
|
});
|
|
105
108
|
if (opts?.loginHint) params.set("login_hint", opts.loginHint);
|
|
106
|
-
|
|
109
|
+
try {
|
|
110
|
+
localStorage.setItem(lastMethodKey(config.clientId), method);
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
const url = EMAIL_METHODS.includes(method) ? `${base}/login?${params.toString()}` : `${base}/api/v1/auth/${method}/start?${params.toString()}`;
|
|
107
114
|
window.location.assign(url);
|
|
108
115
|
}
|
|
116
|
+
var lastMethodKey = (clientId) => `authrobo:last-method:${clientId}`;
|
|
117
|
+
function useLastMethod(clientId) {
|
|
118
|
+
const [last, setLast] = (0, import_react.useState)(null);
|
|
119
|
+
(0, import_react.useEffect)(() => {
|
|
120
|
+
try {
|
|
121
|
+
setLast(localStorage.getItem(lastMethodKey(clientId)) ?? null);
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
}, [clientId]);
|
|
125
|
+
return last;
|
|
126
|
+
}
|
|
109
127
|
function useAppConfig(config) {
|
|
110
128
|
const c = useResolvedConfig(config);
|
|
111
129
|
const [state, setState] = (0, import_react.useState)({
|
|
@@ -132,24 +150,52 @@ function useAppConfig(config) {
|
|
|
132
150
|
return state;
|
|
133
151
|
}
|
|
134
152
|
var STYLE_ID = "authrobo-react-styles";
|
|
153
|
+
var TOKENS_LIGHT = `
|
|
154
|
+
--authrobo-btn-bg:#fff;--authrobo-btn-fg:#111827;--authrobo-btn-border:#d1d5db;
|
|
155
|
+
--authrobo-btn-hover-bg:#f8fafc;--authrobo-btn-hover-border:#9ca3af;
|
|
156
|
+
--authrobo-card-bg:#fff;--authrobo-title-fg:#0f172a;--authrobo-muted:#64748b;
|
|
157
|
+
--authrobo-error:#b91c1c;--authrobo-divider-fg:#9ca3af;--authrobo-divider-line:#e5e7eb;
|
|
158
|
+
--authrobo-overlay:rgba(2,6,23,.55);--authrobo-close:#94a3b8;--authrobo-close-hover:#334155;
|
|
159
|
+
--authrobo-focus:#2b8aff;--authrobo-card-shadow:rgba(2,6,23,.4);
|
|
160
|
+
--authrobo-last-bg:#e7efff;--authrobo-last-fg:#2563eb;`;
|
|
161
|
+
var TOKENS_DARK = `
|
|
162
|
+
--authrobo-btn-bg:#0e1626;--authrobo-btn-fg:#e6edf8;--authrobo-btn-border:#28374f;
|
|
163
|
+
--authrobo-btn-hover-bg:#16233a;--authrobo-btn-hover-border:#3a4d6b;
|
|
164
|
+
--authrobo-card-bg:#0b1424;--authrobo-title-fg:#f1f5fb;--authrobo-muted:#93a4bd;
|
|
165
|
+
--authrobo-error:#f87171;--authrobo-divider-fg:#64748b;--authrobo-divider-line:#223049;
|
|
166
|
+
--authrobo-overlay:rgba(2,6,23,.72);--authrobo-close:#64748b;--authrobo-close-hover:#cbd5e1;
|
|
167
|
+
--authrobo-focus:#46c8ff;--authrobo-card-shadow:rgba(0,0,0,.6);
|
|
168
|
+
--authrobo-last-bg:rgba(70,160,255,.18);--authrobo-last-fg:#8fc4ff;`;
|
|
169
|
+
var TOKEN_ROOTS = ".authrobo-btn,.authrobo-stack,.authrobo-card,.authrobo-overlay,.authrobo-divider,.authrobo-title";
|
|
170
|
+
var scoped = (prefix, tokens) => TOKEN_ROOTS.split(",").map((s) => `${prefix}${s.trim()}`).join(",") + `{${tokens}}`;
|
|
135
171
|
var CSS = `
|
|
136
|
-
|
|
137
|
-
|
|
172
|
+
${scoped("", TOKENS_LIGHT)}
|
|
173
|
+
@media (prefers-color-scheme:dark){${scoped("", TOKENS_DARK)}}
|
|
174
|
+
${scoped('[data-theme="light"] ', TOKENS_LIGHT)}
|
|
175
|
+
${scoped('[data-theme="dark"] ', TOKENS_DARK)}
|
|
176
|
+
.authrobo-btn{position:relative;display:flex;width:100%;align-items:center;justify-content:center;gap:10px;
|
|
177
|
+
border:1px solid var(--authrobo-btn-border);background:var(--authrobo-btn-bg);color:var(--authrobo-btn-fg);border-radius:10px;padding:10px 14px;
|
|
138
178
|
font:600 14px/1.2 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer;
|
|
139
179
|
transition:background .15s ease,border-color .15s ease,box-shadow .15s ease;}
|
|
140
|
-
.authrobo-btn:hover{background
|
|
141
|
-
.authrobo-btn:focus-visible{outline:2px solid
|
|
180
|
+
.authrobo-btn:hover{background:var(--authrobo-btn-hover-bg);border-color:var(--authrobo-btn-hover-border);}
|
|
181
|
+
.authrobo-btn:focus-visible{outline:2px solid var(--authrobo-focus);outline-offset:2px;}
|
|
182
|
+
.authrobo-last{position:absolute;right:8px;top:50%;transform:translateY(-50%);pointer-events:none;
|
|
183
|
+
font:600 10px/1 system-ui,sans-serif;letter-spacing:.02em;padding:3px 7px;border-radius:999px;
|
|
184
|
+
background:var(--authrobo-last-bg);color:var(--authrobo-last-fg);}
|
|
142
185
|
.authrobo-stack{display:flex;flex-direction:column;gap:10px;}
|
|
143
|
-
.authrobo-
|
|
144
|
-
.authrobo-
|
|
186
|
+
.authrobo-title{font:700 16px system-ui,sans-serif;color:var(--authrobo-title-fg);}
|
|
187
|
+
.authrobo-muted{color:var(--authrobo-muted);font:14px system-ui,sans-serif;margin:0;}
|
|
188
|
+
.authrobo-error{color:var(--authrobo-error);font:14px system-ui,sans-serif;margin:0;}
|
|
189
|
+
.authrobo-divider{display:flex;align-items:center;gap:10px;color:var(--authrobo-divider-fg);font:500 12px system-ui,sans-serif;}
|
|
190
|
+
.authrobo-divider::before,.authrobo-divider::after{content:"";height:1px;flex:1;background:var(--authrobo-divider-line);}
|
|
145
191
|
.authrobo-overlay{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;
|
|
146
|
-
justify-content:center;padding:16px;background:
|
|
192
|
+
justify-content:center;padding:16px;background:var(--authrobo-overlay);backdrop-filter:blur(2px);
|
|
147
193
|
animation:authrobo-fade .16s ease-out both;}
|
|
148
|
-
.authrobo-card{position:relative;width:100%;max-width:360px;background
|
|
149
|
-
padding:24px;box-shadow:0 20px 60px -12px
|
|
194
|
+
.authrobo-card{position:relative;width:100%;max-width:360px;background:var(--authrobo-card-bg);border-radius:16px;
|
|
195
|
+
padding:24px;box-shadow:0 20px 60px -12px var(--authrobo-card-shadow);animation:authrobo-pop .2s cubic-bezier(.16,1,.3,1) both;}
|
|
150
196
|
.authrobo-close{position:absolute;top:12px;right:12px;border:0;background:transparent;cursor:pointer;
|
|
151
|
-
color
|
|
152
|
-
.authrobo-close:hover{color
|
|
197
|
+
color:var(--authrobo-close);font-size:18px;line-height:1;padding:4px;}
|
|
198
|
+
.authrobo-close:hover{color:var(--authrobo-close-hover);}
|
|
153
199
|
@keyframes authrobo-fade{from{opacity:0}to{opacity:1}}
|
|
154
200
|
@keyframes authrobo-pop{from{opacity:0;transform:translateY(8px) scale(.97)}to{opacity:1;transform:none}}
|
|
155
201
|
@media (prefers-reduced-motion: reduce){.authrobo-overlay,.authrobo-card{animation:none}}
|
|
@@ -169,7 +215,8 @@ function AuthRoboButton({
|
|
|
169
215
|
label,
|
|
170
216
|
className,
|
|
171
217
|
style,
|
|
172
|
-
onClick
|
|
218
|
+
onClick,
|
|
219
|
+
lastUsed
|
|
173
220
|
}) {
|
|
174
221
|
useInjectStyles();
|
|
175
222
|
const c = useResolvedConfig(config);
|
|
@@ -186,7 +233,8 @@ function AuthRoboButton({
|
|
|
186
233
|
},
|
|
187
234
|
children: [
|
|
188
235
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { display: "inline-flex", alignItems: "center" }, children: meta.icon }),
|
|
189
|
-
label ?? `Continue with ${meta.label}
|
|
236
|
+
label ?? `Continue with ${meta.label}`,
|
|
237
|
+
lastUsed && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "authrobo-last", children: "Last used" })
|
|
190
238
|
]
|
|
191
239
|
}
|
|
192
240
|
);
|
|
@@ -202,6 +250,7 @@ function AuthRoboAuth({
|
|
|
202
250
|
useInjectStyles();
|
|
203
251
|
const c = useResolvedConfig(config);
|
|
204
252
|
const { data, loading, error } = useAppConfig(c);
|
|
253
|
+
const lastMethod = useLastMethod(c.clientId);
|
|
205
254
|
(0, import_react.useEffect)(() => {
|
|
206
255
|
if (mode !== "modal" || !open) return;
|
|
207
256
|
const onKey = (e) => e.key === "Escape" && onClose?.();
|
|
@@ -213,19 +262,29 @@ function AuthRoboAuth({
|
|
|
213
262
|
const shown = (only ? only.filter((m) => available.includes(m)) : available).filter(
|
|
214
263
|
(m) => m in PROVIDER_META
|
|
215
264
|
);
|
|
216
|
-
const social = shown.filter((m) => m
|
|
217
|
-
const
|
|
265
|
+
const social = shown.filter((m) => !EMAIL_METHODS.includes(m));
|
|
266
|
+
const email = shown.filter((m) => EMAIL_METHODS.includes(m));
|
|
267
|
+
const badge = (m) => shown.length > 1 && m === lastMethod;
|
|
218
268
|
const panel = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "authrobo-stack", children: [
|
|
219
269
|
(data?.logoUrl || data?.name) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 4 }, children: [
|
|
220
270
|
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", {
|
|
271
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "authrobo-title", children: title ?? `Sign in${data?.name ? ` to ${data.name}` : ""}` })
|
|
222
272
|
] }),
|
|
223
|
-
loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", {
|
|
224
|
-
error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", {
|
|
225
|
-
social.map((m) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AuthRoboButton, { provider: m, config: c }, m)),
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
273
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "authrobo-muted", children: "Loading sign-in options\u2026" }),
|
|
274
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "authrobo-error", children: "Couldn't load sign-in options." }),
|
|
275
|
+
social.map((m) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AuthRoboButton, { provider: m, config: c, lastUsed: badge(m) }, m)),
|
|
276
|
+
email.length > 0 && social.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "authrobo-divider", children: "or" }),
|
|
277
|
+
email.map((m) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
278
|
+
AuthRoboButton,
|
|
279
|
+
{
|
|
280
|
+
provider: m,
|
|
281
|
+
config: c,
|
|
282
|
+
label: m === "magic_link" ? "Email me a sign-in link" : "Continue with email",
|
|
283
|
+
lastUsed: badge(m)
|
|
284
|
+
},
|
|
285
|
+
m
|
|
286
|
+
)),
|
|
287
|
+
!loading && !error && shown.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "authrobo-muted", children: "No sign-in methods are enabled for this app." })
|
|
229
288
|
] });
|
|
230
289
|
if (mode === "inline") return panel;
|
|
231
290
|
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: [
|
|
@@ -240,6 +299,7 @@ function AuthRoboAuth({
|
|
|
240
299
|
AuthRoboProvider,
|
|
241
300
|
PROVIDER_META,
|
|
242
301
|
startSignIn,
|
|
243
|
-
useAppConfig
|
|
302
|
+
useAppConfig,
|
|
303
|
+
useLastMethod
|
|
244
304
|
});
|
|
245
305
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +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'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"]}
|
|
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// Email-based methods open the hosted login page (which collects the address);\n// social providers redirect straight into their OAuth start endpoint.\nconst EMAIL_METHODS: readonly Method[] = [\"password\", \"magic_link\"];\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\" and \"magic_link\"\n * open the hosted email login page (which renders whichever of the two the app\n * has enabled). AuthRobo returns to your `redirectUri` with a `code` that your\n * 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 // Remember the method so we can badge it as \"Last used\" next time. Scoped per\n // client_id so different apps on the same origin don't clobber each other.\n try {\n localStorage.setItem(lastMethodKey(config.clientId), method);\n } catch {\n /* storage unavailable — badge just won't show next time */\n }\n const url = EMAIL_METHODS.includes(method)\n ? `${base}/login?${params.toString()}`\n : `${base}/api/v1/auth/${method}/start?${params.toString()}`;\n window.location.assign(url);\n}\n\nconst lastMethodKey = (clientId: string) => `authrobo:last-method:${clientId}`;\n\n/** The method last used to sign in for this client (from a prior startSignIn). */\nexport function useLastMethod(clientId: string): Method | null {\n const [last, setLast] = useState<Method | null>(null);\n useEffect(() => {\n try {\n setLast((localStorage.getItem(lastMethodKey(clientId)) as Method | null) ?? null);\n } catch {\n /* storage unavailable */\n }\n }, [clientId]);\n return last;\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\";\n\n// Theme-aware: tokens default to a soft light palette, flip to dark on the OS\n// preference, and can be forced either way by a `data-theme` attribute on any\n// ancestor (`<html data-theme=\"dark\">`) — the same hook AuthRobo's own hosted\n// pages use, so the injected widgets match a host app that drives its theme\n// that way. The explicit attribute wins over the OS media query by specificity.\nconst TOKENS_LIGHT = `\n --authrobo-btn-bg:#fff;--authrobo-btn-fg:#111827;--authrobo-btn-border:#d1d5db;\n --authrobo-btn-hover-bg:#f8fafc;--authrobo-btn-hover-border:#9ca3af;\n --authrobo-card-bg:#fff;--authrobo-title-fg:#0f172a;--authrobo-muted:#64748b;\n --authrobo-error:#b91c1c;--authrobo-divider-fg:#9ca3af;--authrobo-divider-line:#e5e7eb;\n --authrobo-overlay:rgba(2,6,23,.55);--authrobo-close:#94a3b8;--authrobo-close-hover:#334155;\n --authrobo-focus:#2b8aff;--authrobo-card-shadow:rgba(2,6,23,.4);\n --authrobo-last-bg:#e7efff;--authrobo-last-fg:#2563eb;`;\nconst TOKENS_DARK = `\n --authrobo-btn-bg:#0e1626;--authrobo-btn-fg:#e6edf8;--authrobo-btn-border:#28374f;\n --authrobo-btn-hover-bg:#16233a;--authrobo-btn-hover-border:#3a4d6b;\n --authrobo-card-bg:#0b1424;--authrobo-title-fg:#f1f5fb;--authrobo-muted:#93a4bd;\n --authrobo-error:#f87171;--authrobo-divider-fg:#64748b;--authrobo-divider-line:#223049;\n --authrobo-overlay:rgba(2,6,23,.72);--authrobo-close:#64748b;--authrobo-close-hover:#cbd5e1;\n --authrobo-focus:#46c8ff;--authrobo-card-shadow:rgba(0,0,0,.6);\n --authrobo-last-bg:rgba(70,160,255,.18);--authrobo-last-fg:#8fc4ff;`;\n// Elements that either read tokens directly or need them for their descendants\n// (a standalone button has no stack ancestor; the overlay can't inherit from\n// its own card child), so every token-owning root declares the full set.\nconst TOKEN_ROOTS =\n \".authrobo-btn,.authrobo-stack,.authrobo-card,.authrobo-overlay,.authrobo-divider,.authrobo-title\";\nconst scoped = (prefix: string, tokens: string) =>\n TOKEN_ROOTS.split(\",\")\n .map((s) => `${prefix}${s.trim()}`)\n .join(\",\") + `{${tokens}}`;\n\nconst CSS = `\n${scoped(\"\", TOKENS_LIGHT)}\n@media (prefers-color-scheme:dark){${scoped(\"\", TOKENS_DARK)}}\n${scoped('[data-theme=\"light\"] ', TOKENS_LIGHT)}\n${scoped('[data-theme=\"dark\"] ', TOKENS_DARK)}\n.authrobo-btn{position:relative;display:flex;width:100%;align-items:center;justify-content:center;gap:10px;\n border:1px solid var(--authrobo-btn-border);background:var(--authrobo-btn-bg);color:var(--authrobo-btn-fg);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:var(--authrobo-btn-hover-bg);border-color:var(--authrobo-btn-hover-border);}\n.authrobo-btn:focus-visible{outline:2px solid var(--authrobo-focus);outline-offset:2px;}\n.authrobo-last{position:absolute;right:8px;top:50%;transform:translateY(-50%);pointer-events:none;\n font:600 10px/1 system-ui,sans-serif;letter-spacing:.02em;padding:3px 7px;border-radius:999px;\n background:var(--authrobo-last-bg);color:var(--authrobo-last-fg);}\n.authrobo-stack{display:flex;flex-direction:column;gap:10px;}\n.authrobo-title{font:700 16px system-ui,sans-serif;color:var(--authrobo-title-fg);}\n.authrobo-muted{color:var(--authrobo-muted);font:14px system-ui,sans-serif;margin:0;}\n.authrobo-error{color:var(--authrobo-error);font:14px system-ui,sans-serif;margin:0;}\n.authrobo-divider{display:flex;align-items:center;gap:10px;color:var(--authrobo-divider-fg);font:500 12px system-ui,sans-serif;}\n.authrobo-divider::before,.authrobo-divider::after{content:\"\";height:1px;flex:1;background:var(--authrobo-divider-line);}\n.authrobo-overlay{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;\n justify-content:center;padding:16px;background:var(--authrobo-overlay);backdrop-filter:blur(2px);\n animation:authrobo-fade .16s ease-out both;}\n.authrobo-card{position:relative;width:100%;max-width:360px;background:var(--authrobo-card-bg);border-radius:16px;\n padding:24px;box-shadow:0 20px 60px -12px var(--authrobo-card-shadow);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:var(--authrobo-close);font-size:18px;line-height:1;padding:4px;}\n.authrobo-close:hover{color:var(--authrobo-close-hover);}\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 lastUsed,\n}: {\n provider: Method;\n config?: AuthRoboConfig;\n label?: string;\n className?: string;\n style?: React.CSSProperties;\n onClick?: () => void;\n /** Show a \"Last used\" badge (the method the visitor signed in with before). */\n lastUsed?: boolean;\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 {lastUsed && <span className=\"authrobo-last\">Last used</span>}\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 const lastMethod = useLastMethod(c.clientId);\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) => !EMAIL_METHODS.includes(m));\n const email = shown.filter((m) => EMAIL_METHODS.includes(m));\n // Only worth pointing out the last-used method when there's a choice to make.\n const badge = (m: Method) => shown.length > 1 && m === lastMethod;\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 className=\"authrobo-title\">\n {title ?? `Sign in${data?.name ? ` to ${data.name}` : \"\"}`}\n </span>\n </div>\n )}\n\n {loading && <p className=\"authrobo-muted\">Loading sign-in options…</p>}\n {error && <p className=\"authrobo-error\">Couldn't load sign-in options.</p>}\n\n {social.map((m) => (\n <AuthRoboButton key={m} provider={m} config={c} lastUsed={badge(m)} />\n ))}\n {email.length > 0 && social.length > 0 && <div className=\"authrobo-divider\">or</div>}\n {email.map((m) => (\n <AuthRoboButton\n key={m}\n provider={m}\n config={c}\n label={m === \"magic_link\" ? \"Email me a sign-in link\" : \"Continue with email\"}\n lastUsed={badge(m)}\n />\n ))}\n\n {!loading && !error && shown.length === 0 && (\n <p className=\"authrobo-muted\">No sign-in methods are enabled for this app.</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\" and \"magic_link\" route to\n// the hosted email login page; the rest are social OAuth providers.\nexport type Method = \"google\" | \"linkedin\" | \"github\" | \"apple\" | \"password\" | \"magic_link\";\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 magic_link: { label: \"magic link\", icon: <MailIcon /> },\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,mBAA+D;;;ACO3D;AAJJ,IAAM,KAAK,EAAE,OAAO,IAAI,QAAQ,IAAI,eAAe,KAAK;AAExD,SAAS,aAAa;AACpB,SACE,6CAAC,SAAI,SAAQ,aAAa,GAAG,IAC3B;AAAA,gDAAC,UAAK,MAAK,WAAU,GAAE,0MAAyM;AAAA,IAChO,4CAAC,UAAK,MAAK,WAAU,GAAE,mHAAkH;AAAA,IACzI,4CAAC,UAAK,MAAK,WAAU,GAAE,oHAAmH;AAAA,IAC1I,4CAAC,UAAK,MAAK,WAAU,GAAE,yGAAwG;AAAA,KACjI;AAEJ;AAEA,SAAS,eAAe;AACtB,SACE,4CAAC,SAAI,SAAQ,aAAa,GAAG,IAAI,MAAK,WACpC,sDAAC,UAAK,GAAE,oXAAmX,GAC7X;AAEJ;AAEA,SAAS,aAAa;AACpB,SACE,4CAAC,SAAI,SAAQ,aAAa,GAAG,IAAI,MAAK,WACpC,sDAAC,UAAK,GAAE,sZAAqZ,GAC/Z;AAEJ;AAEA,SAAS,YAAY;AACnB,SACE,4CAAC,SAAI,SAAQ,aAAa,GAAG,IAAI,MAAK,QACpC,sDAAC,UAAK,GAAE,gcAA+b,GACzc;AAEJ;AAEA,SAAS,WAAW;AAClB,SACE,6CAAC,SAAI,SAAQ,aAAa,GAAG,IAAI,MAAK,QAAO,QAAO,WAAU,aAAa,GAAG,eAAc,SAAQ,gBAAe,SACjH;AAAA,gDAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI;AAAA,IAChD,4CAAC,UAAK,GAAE,gBAAe;AAAA,KACzB;AAEJ;AAEO,IAAM,gBAA0E;AAAA,EACrF,QAAQ,EAAE,OAAO,UAAU,MAAM,4CAAC,cAAW,EAAG;AAAA,EAChD,UAAU,EAAE,OAAO,YAAY,MAAM,4CAAC,gBAAa,EAAG;AAAA,EACtD,QAAQ,EAAE,OAAO,UAAU,MAAM,4CAAC,cAAW,EAAG;AAAA,EAChD,OAAO,EAAE,OAAO,SAAS,MAAM,4CAAC,aAAU,EAAG;AAAA,EAC7C,UAAU,EAAE,OAAO,SAAS,MAAM,4CAAC,YAAS,EAAG;AAAA,EAC/C,YAAY,EAAE,OAAO,cAAc,MAAM,4CAAC,YAAS,EAAG;AACxD;;;ADxBS,IAAAA,sBAAA;AATT,IAAM,oBAAgB,4BAAqC,IAAI;AAExD,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AACF,GAGG;AACD,SAAO,6CAAC,cAAc,UAAd,EAAuB,OAAO,QAAS,UAAS;AAC1D;AAEA,SAAS,kBAAkB,UAA2C;AACpE,QAAM,UAAM,yBAAW,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;AAIA,IAAM,gBAAmC,CAAC,YAAY,YAAY;AAS3D,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;AAG5D,MAAI;AACF,iBAAa,QAAQ,cAAc,OAAO,QAAQ,GAAG,MAAM;AAAA,EAC7D,QAAQ;AAAA,EAER;AACA,QAAM,MAAM,cAAc,SAAS,MAAM,IACrC,GAAG,IAAI,UAAU,OAAO,SAAS,CAAC,KAClC,GAAG,IAAI,gBAAgB,MAAM,UAAU,OAAO,SAAS,CAAC;AAC5D,SAAO,SAAS,OAAO,GAAG;AAC5B;AAEA,IAAM,gBAAgB,CAAC,aAAqB,wBAAwB,QAAQ;AAGrE,SAAS,cAAc,UAAiC;AAC7D,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAwB,IAAI;AACpD,8BAAU,MAAM;AACd,QAAI;AACF,cAAS,aAAa,QAAQ,cAAc,QAAQ,CAAC,KAAuB,IAAI;AAAA,IAClF,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AACb,SAAO;AACT;AAGO,SAAS,aAAa,QAI3B;AACA,QAAM,IAAI,kBAAkB,MAAM;AAClC,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAA6E;AAAA,IACrG,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,EACT,CAAC;AAED,8BAAU,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;AAOjB,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQrB,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWpB,IAAM,cACJ;AACF,IAAM,SAAS,CAAC,QAAgB,WAC9B,YAAY,MAAM,GAAG,EAClB,IAAI,CAAC,MAAM,GAAG,MAAM,GAAG,EAAE,KAAK,CAAC,EAAE,EACjC,KAAK,GAAG,IAAI,IAAI,MAAM;AAE3B,IAAM,MAAM;AAAA,EACV,OAAO,IAAI,YAAY,CAAC;AAAA,qCACW,OAAO,IAAI,WAAW,CAAC;AAAA,EAC1D,OAAO,yBAAyB,YAAY,CAAC;AAAA,EAC7C,OAAO,wBAAwB,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6B7C,SAAS,kBAAkB;AACzB,8BAAU,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;AAAA,EACA;AACF,GASG;AACD,kBAAgB;AAChB,QAAM,IAAI,kBAAkB,MAAM;AAClC,QAAM,OAAO,cAAc,QAAQ;AACnC,SACE;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,qDAAC,UAAK,OAAO,EAAE,SAAS,eAAe,YAAY,SAAS,GAAI,eAAK,MAAK;AAAA,QACzE,SAAS,iBAAiB,KAAK,KAAK;AAAA,QACpC,YAAY,6CAAC,UAAK,WAAU,iBAAgB,uBAAS;AAAA;AAAA;AAAA,EACxD;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;AAC/C,QAAM,aAAa,cAAc,EAAE,QAAQ;AAE3C,8BAAU,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,CAAC,cAAc,SAAS,CAAC,CAAC;AAC7D,QAAM,QAAQ,MAAM,OAAO,CAAC,MAAM,cAAc,SAAS,CAAC,CAAC;AAE3D,QAAM,QAAQ,CAAC,MAAc,MAAM,SAAS,KAAK,MAAM;AAEvD,QAAM,QACJ,8CAAC,SAAI,WAAU,kBACX;AAAA,WAAM,WAAW,MAAM,SACvB,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,IAAI,cAAc,EAAE,GAC3E;AAAA,YAAM,WACL,6CAAC,SAAI,KAAK,KAAK,SAAS,KAAI,IAAG,OAAO,IAAI,QAAQ,IAAI,OAAO,EAAE,cAAc,EAAE,GAAG;AAAA,MAEpF,6CAAC,UAAK,WAAU,kBACb,mBAAS,UAAU,MAAM,OAAO,OAAO,KAAK,IAAI,KAAK,EAAE,IAC1D;AAAA,OACF;AAAA,IAGD,WAAW,6CAAC,OAAE,WAAU,kBAAiB,2CAAwB;AAAA,IACjE,SAAS,6CAAC,OAAE,WAAU,kBAAiB,4CAAmC;AAAA,IAE1E,OAAO,IAAI,CAAC,MACX,6CAAC,kBAAuB,UAAU,GAAG,QAAQ,GAAG,UAAU,MAAM,CAAC,KAA5C,CAA+C,CACrE;AAAA,IACA,MAAM,SAAS,KAAK,OAAO,SAAS,KAAK,6CAAC,SAAI,WAAU,oBAAmB,gBAAE;AAAA,IAC7E,MAAM,IAAI,CAAC,MACV;AAAA,MAAC;AAAA;AAAA,QAEC,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,OAAO,MAAM,eAAe,4BAA4B;AAAA,QACxD,UAAU,MAAM,CAAC;AAAA;AAAA,MAJZ;AAAA,IAKP,CACD;AAAA,IAEA,CAAC,WAAW,CAAC,SAAS,MAAM,WAAW,KACtC,6CAAC,OAAE,WAAU,kBAAiB,0DAA4C;AAAA,KAE9E;AAGF,MAAI,SAAS,SAAU,QAAO;AAE9B,SACE,6CAAC,SAAI,WAAU,oBAAmB,SAAS,MAAM,UAAU,GACzD,wDAAC,SAAI,WAAU,iBAAgB,MAAK,UAAS,cAAW,QAAO,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAC/F;AAAA,iDAAC,YAAO,MAAK,UAAS,WAAU,kBAAiB,cAAW,SAAQ,SAAS,MAAM,UAAU,GAAG,oBAEhG;AAAA,IACC;AAAA,KACH,GACF;AAEJ;","names":["import_jsx_runtime"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
type Method = "google" | "linkedin" | "github" | "apple" | "password";
|
|
3
|
+
type Method = "google" | "linkedin" | "github" | "apple" | "password" | "magic_link";
|
|
4
4
|
declare const PROVIDER_META: Record<Method, {
|
|
5
5
|
label: string;
|
|
6
6
|
icon: React.ReactNode;
|
|
@@ -27,27 +27,32 @@ declare function AuthRoboProvider({ config, children, }: {
|
|
|
27
27
|
}): React.JSX.Element;
|
|
28
28
|
/**
|
|
29
29
|
* Begin sign-in: navigate the browser to AuthRobo's hosted flow for `method`.
|
|
30
|
-
* Social methods hit /api/v1/auth/<provider>/start; "password"
|
|
31
|
-
* email login page
|
|
32
|
-
*
|
|
30
|
+
* Social methods hit /api/v1/auth/<provider>/start; "password" and "magic_link"
|
|
31
|
+
* open the hosted email login page (which renders whichever of the two the app
|
|
32
|
+
* has enabled). AuthRobo returns to your `redirectUri` with a `code` that your
|
|
33
|
+
* backend exchanges (with your client_secret) for tokens.
|
|
33
34
|
*/
|
|
34
35
|
declare function startSignIn(config: AuthRoboConfig, method: Method, opts?: {
|
|
35
36
|
state?: string;
|
|
36
37
|
loginHint?: string;
|
|
37
38
|
}): void;
|
|
39
|
+
/** The method last used to sign in for this client (from a prior startSignIn). */
|
|
40
|
+
declare function useLastMethod(clientId: string): Method | null;
|
|
38
41
|
/** Fetch an app's public config (branding + enabled methods) for `clientId`. */
|
|
39
42
|
declare function useAppConfig(config?: AuthRoboConfig): {
|
|
40
43
|
data: AppConfig | null;
|
|
41
44
|
loading: boolean;
|
|
42
45
|
error: string | null;
|
|
43
46
|
};
|
|
44
|
-
declare function AuthRoboButton({ provider, config, label, className, style, onClick, }: {
|
|
47
|
+
declare function AuthRoboButton({ provider, config, label, className, style, onClick, lastUsed, }: {
|
|
45
48
|
provider: Method;
|
|
46
49
|
config?: AuthRoboConfig;
|
|
47
50
|
label?: string;
|
|
48
51
|
className?: string;
|
|
49
52
|
style?: React.CSSProperties;
|
|
50
53
|
onClick?: () => void;
|
|
54
|
+
/** Show a "Last used" badge (the method the visitor signed in with before). */
|
|
55
|
+
lastUsed?: boolean;
|
|
51
56
|
}): React.JSX.Element;
|
|
52
57
|
declare function AuthRoboAuth({ config, mode, open, onClose, title, methods: only, }: {
|
|
53
58
|
config?: AuthRoboConfig;
|
|
@@ -62,4 +67,4 @@ declare function AuthRoboAuth({ config, mode, open, onClose, title, methods: onl
|
|
|
62
67
|
methods?: Method[];
|
|
63
68
|
}): React.JSX.Element | null;
|
|
64
69
|
|
|
65
|
-
export { type AppConfig, AuthRoboAuth, AuthRoboButton, type AuthRoboConfig, AuthRoboProvider, type Method, PROVIDER_META, startSignIn, useAppConfig };
|
|
70
|
+
export { type AppConfig, AuthRoboAuth, AuthRoboButton, type AuthRoboConfig, AuthRoboProvider, type Method, PROVIDER_META, startSignIn, useAppConfig, useLastMethod };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
type Method = "google" | "linkedin" | "github" | "apple" | "password";
|
|
3
|
+
type Method = "google" | "linkedin" | "github" | "apple" | "password" | "magic_link";
|
|
4
4
|
declare const PROVIDER_META: Record<Method, {
|
|
5
5
|
label: string;
|
|
6
6
|
icon: React.ReactNode;
|
|
@@ -27,27 +27,32 @@ declare function AuthRoboProvider({ config, children, }: {
|
|
|
27
27
|
}): React.JSX.Element;
|
|
28
28
|
/**
|
|
29
29
|
* Begin sign-in: navigate the browser to AuthRobo's hosted flow for `method`.
|
|
30
|
-
* Social methods hit /api/v1/auth/<provider>/start; "password"
|
|
31
|
-
* email login page
|
|
32
|
-
*
|
|
30
|
+
* Social methods hit /api/v1/auth/<provider>/start; "password" and "magic_link"
|
|
31
|
+
* open the hosted email login page (which renders whichever of the two the app
|
|
32
|
+
* has enabled). AuthRobo returns to your `redirectUri` with a `code` that your
|
|
33
|
+
* backend exchanges (with your client_secret) for tokens.
|
|
33
34
|
*/
|
|
34
35
|
declare function startSignIn(config: AuthRoboConfig, method: Method, opts?: {
|
|
35
36
|
state?: string;
|
|
36
37
|
loginHint?: string;
|
|
37
38
|
}): void;
|
|
39
|
+
/** The method last used to sign in for this client (from a prior startSignIn). */
|
|
40
|
+
declare function useLastMethod(clientId: string): Method | null;
|
|
38
41
|
/** Fetch an app's public config (branding + enabled methods) for `clientId`. */
|
|
39
42
|
declare function useAppConfig(config?: AuthRoboConfig): {
|
|
40
43
|
data: AppConfig | null;
|
|
41
44
|
loading: boolean;
|
|
42
45
|
error: string | null;
|
|
43
46
|
};
|
|
44
|
-
declare function AuthRoboButton({ provider, config, label, className, style, onClick, }: {
|
|
47
|
+
declare function AuthRoboButton({ provider, config, label, className, style, onClick, lastUsed, }: {
|
|
45
48
|
provider: Method;
|
|
46
49
|
config?: AuthRoboConfig;
|
|
47
50
|
label?: string;
|
|
48
51
|
className?: string;
|
|
49
52
|
style?: React.CSSProperties;
|
|
50
53
|
onClick?: () => void;
|
|
54
|
+
/** Show a "Last used" badge (the method the visitor signed in with before). */
|
|
55
|
+
lastUsed?: boolean;
|
|
51
56
|
}): React.JSX.Element;
|
|
52
57
|
declare function AuthRoboAuth({ config, mode, open, onClose, title, methods: only, }: {
|
|
53
58
|
config?: AuthRoboConfig;
|
|
@@ -62,4 +67,4 @@ declare function AuthRoboAuth({ config, mode, open, onClose, title, methods: onl
|
|
|
62
67
|
methods?: Method[];
|
|
63
68
|
}): React.JSX.Element | null;
|
|
64
69
|
|
|
65
|
-
export { type AppConfig, AuthRoboAuth, AuthRoboButton, type AuthRoboConfig, AuthRoboProvider, type Method, PROVIDER_META, startSignIn, useAppConfig };
|
|
70
|
+
export { type AppConfig, AuthRoboAuth, AuthRoboButton, type AuthRoboConfig, AuthRoboProvider, type Method, PROVIDER_META, startSignIn, useAppConfig, useLastMethod };
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,8 @@ var PROVIDER_META = {
|
|
|
35
35
|
linkedin: { label: "LinkedIn", icon: /* @__PURE__ */ jsx(LinkedInIcon, {}) },
|
|
36
36
|
github: { label: "GitHub", icon: /* @__PURE__ */ jsx(GitHubIcon, {}) },
|
|
37
37
|
apple: { label: "Apple", icon: /* @__PURE__ */ jsx(AppleIcon, {}) },
|
|
38
|
-
password: { label: "email", icon: /* @__PURE__ */ jsx(MailIcon, {}) }
|
|
38
|
+
password: { label: "email", icon: /* @__PURE__ */ jsx(MailIcon, {}) },
|
|
39
|
+
magic_link: { label: "magic link", icon: /* @__PURE__ */ jsx(MailIcon, {}) }
|
|
39
40
|
};
|
|
40
41
|
|
|
41
42
|
// src/index.tsx
|
|
@@ -62,6 +63,7 @@ function randomState() {
|
|
|
62
63
|
(globalThis.crypto ?? globalThis.crypto).getRandomValues(b);
|
|
63
64
|
return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
64
65
|
}
|
|
66
|
+
var EMAIL_METHODS = ["password", "magic_link"];
|
|
65
67
|
function startSignIn(config, method, opts) {
|
|
66
68
|
const base = config.issuerUrl.replace(/\/$/, "");
|
|
67
69
|
const state = opts?.state ?? randomState();
|
|
@@ -75,9 +77,24 @@ function startSignIn(config, method, opts) {
|
|
|
75
77
|
state
|
|
76
78
|
});
|
|
77
79
|
if (opts?.loginHint) params.set("login_hint", opts.loginHint);
|
|
78
|
-
|
|
80
|
+
try {
|
|
81
|
+
localStorage.setItem(lastMethodKey(config.clientId), method);
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
const url = EMAIL_METHODS.includes(method) ? `${base}/login?${params.toString()}` : `${base}/api/v1/auth/${method}/start?${params.toString()}`;
|
|
79
85
|
window.location.assign(url);
|
|
80
86
|
}
|
|
87
|
+
var lastMethodKey = (clientId) => `authrobo:last-method:${clientId}`;
|
|
88
|
+
function useLastMethod(clientId) {
|
|
89
|
+
const [last, setLast] = useState(null);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
try {
|
|
92
|
+
setLast(localStorage.getItem(lastMethodKey(clientId)) ?? null);
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
}, [clientId]);
|
|
96
|
+
return last;
|
|
97
|
+
}
|
|
81
98
|
function useAppConfig(config) {
|
|
82
99
|
const c = useResolvedConfig(config);
|
|
83
100
|
const [state, setState] = useState({
|
|
@@ -104,24 +121,52 @@ function useAppConfig(config) {
|
|
|
104
121
|
return state;
|
|
105
122
|
}
|
|
106
123
|
var STYLE_ID = "authrobo-react-styles";
|
|
124
|
+
var TOKENS_LIGHT = `
|
|
125
|
+
--authrobo-btn-bg:#fff;--authrobo-btn-fg:#111827;--authrobo-btn-border:#d1d5db;
|
|
126
|
+
--authrobo-btn-hover-bg:#f8fafc;--authrobo-btn-hover-border:#9ca3af;
|
|
127
|
+
--authrobo-card-bg:#fff;--authrobo-title-fg:#0f172a;--authrobo-muted:#64748b;
|
|
128
|
+
--authrobo-error:#b91c1c;--authrobo-divider-fg:#9ca3af;--authrobo-divider-line:#e5e7eb;
|
|
129
|
+
--authrobo-overlay:rgba(2,6,23,.55);--authrobo-close:#94a3b8;--authrobo-close-hover:#334155;
|
|
130
|
+
--authrobo-focus:#2b8aff;--authrobo-card-shadow:rgba(2,6,23,.4);
|
|
131
|
+
--authrobo-last-bg:#e7efff;--authrobo-last-fg:#2563eb;`;
|
|
132
|
+
var TOKENS_DARK = `
|
|
133
|
+
--authrobo-btn-bg:#0e1626;--authrobo-btn-fg:#e6edf8;--authrobo-btn-border:#28374f;
|
|
134
|
+
--authrobo-btn-hover-bg:#16233a;--authrobo-btn-hover-border:#3a4d6b;
|
|
135
|
+
--authrobo-card-bg:#0b1424;--authrobo-title-fg:#f1f5fb;--authrobo-muted:#93a4bd;
|
|
136
|
+
--authrobo-error:#f87171;--authrobo-divider-fg:#64748b;--authrobo-divider-line:#223049;
|
|
137
|
+
--authrobo-overlay:rgba(2,6,23,.72);--authrobo-close:#64748b;--authrobo-close-hover:#cbd5e1;
|
|
138
|
+
--authrobo-focus:#46c8ff;--authrobo-card-shadow:rgba(0,0,0,.6);
|
|
139
|
+
--authrobo-last-bg:rgba(70,160,255,.18);--authrobo-last-fg:#8fc4ff;`;
|
|
140
|
+
var TOKEN_ROOTS = ".authrobo-btn,.authrobo-stack,.authrobo-card,.authrobo-overlay,.authrobo-divider,.authrobo-title";
|
|
141
|
+
var scoped = (prefix, tokens) => TOKEN_ROOTS.split(",").map((s) => `${prefix}${s.trim()}`).join(",") + `{${tokens}}`;
|
|
107
142
|
var CSS = `
|
|
108
|
-
|
|
109
|
-
|
|
143
|
+
${scoped("", TOKENS_LIGHT)}
|
|
144
|
+
@media (prefers-color-scheme:dark){${scoped("", TOKENS_DARK)}}
|
|
145
|
+
${scoped('[data-theme="light"] ', TOKENS_LIGHT)}
|
|
146
|
+
${scoped('[data-theme="dark"] ', TOKENS_DARK)}
|
|
147
|
+
.authrobo-btn{position:relative;display:flex;width:100%;align-items:center;justify-content:center;gap:10px;
|
|
148
|
+
border:1px solid var(--authrobo-btn-border);background:var(--authrobo-btn-bg);color:var(--authrobo-btn-fg);border-radius:10px;padding:10px 14px;
|
|
110
149
|
font:600 14px/1.2 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer;
|
|
111
150
|
transition:background .15s ease,border-color .15s ease,box-shadow .15s ease;}
|
|
112
|
-
.authrobo-btn:hover{background
|
|
113
|
-
.authrobo-btn:focus-visible{outline:2px solid
|
|
151
|
+
.authrobo-btn:hover{background:var(--authrobo-btn-hover-bg);border-color:var(--authrobo-btn-hover-border);}
|
|
152
|
+
.authrobo-btn:focus-visible{outline:2px solid var(--authrobo-focus);outline-offset:2px;}
|
|
153
|
+
.authrobo-last{position:absolute;right:8px;top:50%;transform:translateY(-50%);pointer-events:none;
|
|
154
|
+
font:600 10px/1 system-ui,sans-serif;letter-spacing:.02em;padding:3px 7px;border-radius:999px;
|
|
155
|
+
background:var(--authrobo-last-bg);color:var(--authrobo-last-fg);}
|
|
114
156
|
.authrobo-stack{display:flex;flex-direction:column;gap:10px;}
|
|
115
|
-
.authrobo-
|
|
116
|
-
.authrobo-
|
|
157
|
+
.authrobo-title{font:700 16px system-ui,sans-serif;color:var(--authrobo-title-fg);}
|
|
158
|
+
.authrobo-muted{color:var(--authrobo-muted);font:14px system-ui,sans-serif;margin:0;}
|
|
159
|
+
.authrobo-error{color:var(--authrobo-error);font:14px system-ui,sans-serif;margin:0;}
|
|
160
|
+
.authrobo-divider{display:flex;align-items:center;gap:10px;color:var(--authrobo-divider-fg);font:500 12px system-ui,sans-serif;}
|
|
161
|
+
.authrobo-divider::before,.authrobo-divider::after{content:"";height:1px;flex:1;background:var(--authrobo-divider-line);}
|
|
117
162
|
.authrobo-overlay{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;
|
|
118
|
-
justify-content:center;padding:16px;background:
|
|
163
|
+
justify-content:center;padding:16px;background:var(--authrobo-overlay);backdrop-filter:blur(2px);
|
|
119
164
|
animation:authrobo-fade .16s ease-out both;}
|
|
120
|
-
.authrobo-card{position:relative;width:100%;max-width:360px;background
|
|
121
|
-
padding:24px;box-shadow:0 20px 60px -12px
|
|
165
|
+
.authrobo-card{position:relative;width:100%;max-width:360px;background:var(--authrobo-card-bg);border-radius:16px;
|
|
166
|
+
padding:24px;box-shadow:0 20px 60px -12px var(--authrobo-card-shadow);animation:authrobo-pop .2s cubic-bezier(.16,1,.3,1) both;}
|
|
122
167
|
.authrobo-close{position:absolute;top:12px;right:12px;border:0;background:transparent;cursor:pointer;
|
|
123
|
-
color
|
|
124
|
-
.authrobo-close:hover{color
|
|
168
|
+
color:var(--authrobo-close);font-size:18px;line-height:1;padding:4px;}
|
|
169
|
+
.authrobo-close:hover{color:var(--authrobo-close-hover);}
|
|
125
170
|
@keyframes authrobo-fade{from{opacity:0}to{opacity:1}}
|
|
126
171
|
@keyframes authrobo-pop{from{opacity:0;transform:translateY(8px) scale(.97)}to{opacity:1;transform:none}}
|
|
127
172
|
@media (prefers-reduced-motion: reduce){.authrobo-overlay,.authrobo-card{animation:none}}
|
|
@@ -141,7 +186,8 @@ function AuthRoboButton({
|
|
|
141
186
|
label,
|
|
142
187
|
className,
|
|
143
188
|
style,
|
|
144
|
-
onClick
|
|
189
|
+
onClick,
|
|
190
|
+
lastUsed
|
|
145
191
|
}) {
|
|
146
192
|
useInjectStyles();
|
|
147
193
|
const c = useResolvedConfig(config);
|
|
@@ -158,7 +204,8 @@ function AuthRoboButton({
|
|
|
158
204
|
},
|
|
159
205
|
children: [
|
|
160
206
|
/* @__PURE__ */ jsx2("span", { style: { display: "inline-flex", alignItems: "center" }, children: meta.icon }),
|
|
161
|
-
label ?? `Continue with ${meta.label}
|
|
207
|
+
label ?? `Continue with ${meta.label}`,
|
|
208
|
+
lastUsed && /* @__PURE__ */ jsx2("span", { className: "authrobo-last", children: "Last used" })
|
|
162
209
|
]
|
|
163
210
|
}
|
|
164
211
|
);
|
|
@@ -174,6 +221,7 @@ function AuthRoboAuth({
|
|
|
174
221
|
useInjectStyles();
|
|
175
222
|
const c = useResolvedConfig(config);
|
|
176
223
|
const { data, loading, error } = useAppConfig(c);
|
|
224
|
+
const lastMethod = useLastMethod(c.clientId);
|
|
177
225
|
useEffect(() => {
|
|
178
226
|
if (mode !== "modal" || !open) return;
|
|
179
227
|
const onKey = (e) => e.key === "Escape" && onClose?.();
|
|
@@ -185,19 +233,29 @@ function AuthRoboAuth({
|
|
|
185
233
|
const shown = (only ? only.filter((m) => available.includes(m)) : available).filter(
|
|
186
234
|
(m) => m in PROVIDER_META
|
|
187
235
|
);
|
|
188
|
-
const social = shown.filter((m) => m
|
|
189
|
-
const
|
|
236
|
+
const social = shown.filter((m) => !EMAIL_METHODS.includes(m));
|
|
237
|
+
const email = shown.filter((m) => EMAIL_METHODS.includes(m));
|
|
238
|
+
const badge = (m) => shown.length > 1 && m === lastMethod;
|
|
190
239
|
const panel = /* @__PURE__ */ jsxs2("div", { className: "authrobo-stack", children: [
|
|
191
240
|
(data?.logoUrl || data?.name) && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 4 }, children: [
|
|
192
241
|
data?.logoUrl && /* @__PURE__ */ jsx2("img", { src: data.logoUrl, alt: "", width: 28, height: 28, style: { borderRadius: 6 } }),
|
|
193
|
-
/* @__PURE__ */ jsx2("span", {
|
|
242
|
+
/* @__PURE__ */ jsx2("span", { className: "authrobo-title", children: title ?? `Sign in${data?.name ? ` to ${data.name}` : ""}` })
|
|
194
243
|
] }),
|
|
195
|
-
loading && /* @__PURE__ */ jsx2("p", {
|
|
196
|
-
error && /* @__PURE__ */ jsx2("p", {
|
|
197
|
-
social.map((m) => /* @__PURE__ */ jsx2(AuthRoboButton, { provider: m, config: c }, m)),
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
244
|
+
loading && /* @__PURE__ */ jsx2("p", { className: "authrobo-muted", children: "Loading sign-in options\u2026" }),
|
|
245
|
+
error && /* @__PURE__ */ jsx2("p", { className: "authrobo-error", children: "Couldn't load sign-in options." }),
|
|
246
|
+
social.map((m) => /* @__PURE__ */ jsx2(AuthRoboButton, { provider: m, config: c, lastUsed: badge(m) }, m)),
|
|
247
|
+
email.length > 0 && social.length > 0 && /* @__PURE__ */ jsx2("div", { className: "authrobo-divider", children: "or" }),
|
|
248
|
+
email.map((m) => /* @__PURE__ */ jsx2(
|
|
249
|
+
AuthRoboButton,
|
|
250
|
+
{
|
|
251
|
+
provider: m,
|
|
252
|
+
config: c,
|
|
253
|
+
label: m === "magic_link" ? "Email me a sign-in link" : "Continue with email",
|
|
254
|
+
lastUsed: badge(m)
|
|
255
|
+
},
|
|
256
|
+
m
|
|
257
|
+
)),
|
|
258
|
+
!loading && !error && shown.length === 0 && /* @__PURE__ */ jsx2("p", { className: "authrobo-muted", children: "No sign-in methods are enabled for this app." })
|
|
201
259
|
] });
|
|
202
260
|
if (mode === "inline") return panel;
|
|
203
261
|
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: [
|
|
@@ -211,6 +269,7 @@ export {
|
|
|
211
269
|
AuthRoboProvider,
|
|
212
270
|
PROVIDER_META,
|
|
213
271
|
startSignIn,
|
|
214
|
-
useAppConfig
|
|
272
|
+
useAppConfig,
|
|
273
|
+
useLastMethod
|
|
215
274
|
};
|
|
216
275
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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'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":["jsx","jsxs"]}
|
|
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// Email-based methods open the hosted login page (which collects the address);\n// social providers redirect straight into their OAuth start endpoint.\nconst EMAIL_METHODS: readonly Method[] = [\"password\", \"magic_link\"];\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\" and \"magic_link\"\n * open the hosted email login page (which renders whichever of the two the app\n * has enabled). AuthRobo returns to your `redirectUri` with a `code` that your\n * 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 // Remember the method so we can badge it as \"Last used\" next time. Scoped per\n // client_id so different apps on the same origin don't clobber each other.\n try {\n localStorage.setItem(lastMethodKey(config.clientId), method);\n } catch {\n /* storage unavailable — badge just won't show next time */\n }\n const url = EMAIL_METHODS.includes(method)\n ? `${base}/login?${params.toString()}`\n : `${base}/api/v1/auth/${method}/start?${params.toString()}`;\n window.location.assign(url);\n}\n\nconst lastMethodKey = (clientId: string) => `authrobo:last-method:${clientId}`;\n\n/** The method last used to sign in for this client (from a prior startSignIn). */\nexport function useLastMethod(clientId: string): Method | null {\n const [last, setLast] = useState<Method | null>(null);\n useEffect(() => {\n try {\n setLast((localStorage.getItem(lastMethodKey(clientId)) as Method | null) ?? null);\n } catch {\n /* storage unavailable */\n }\n }, [clientId]);\n return last;\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\";\n\n// Theme-aware: tokens default to a soft light palette, flip to dark on the OS\n// preference, and can be forced either way by a `data-theme` attribute on any\n// ancestor (`<html data-theme=\"dark\">`) — the same hook AuthRobo's own hosted\n// pages use, so the injected widgets match a host app that drives its theme\n// that way. The explicit attribute wins over the OS media query by specificity.\nconst TOKENS_LIGHT = `\n --authrobo-btn-bg:#fff;--authrobo-btn-fg:#111827;--authrobo-btn-border:#d1d5db;\n --authrobo-btn-hover-bg:#f8fafc;--authrobo-btn-hover-border:#9ca3af;\n --authrobo-card-bg:#fff;--authrobo-title-fg:#0f172a;--authrobo-muted:#64748b;\n --authrobo-error:#b91c1c;--authrobo-divider-fg:#9ca3af;--authrobo-divider-line:#e5e7eb;\n --authrobo-overlay:rgba(2,6,23,.55);--authrobo-close:#94a3b8;--authrobo-close-hover:#334155;\n --authrobo-focus:#2b8aff;--authrobo-card-shadow:rgba(2,6,23,.4);\n --authrobo-last-bg:#e7efff;--authrobo-last-fg:#2563eb;`;\nconst TOKENS_DARK = `\n --authrobo-btn-bg:#0e1626;--authrobo-btn-fg:#e6edf8;--authrobo-btn-border:#28374f;\n --authrobo-btn-hover-bg:#16233a;--authrobo-btn-hover-border:#3a4d6b;\n --authrobo-card-bg:#0b1424;--authrobo-title-fg:#f1f5fb;--authrobo-muted:#93a4bd;\n --authrobo-error:#f87171;--authrobo-divider-fg:#64748b;--authrobo-divider-line:#223049;\n --authrobo-overlay:rgba(2,6,23,.72);--authrobo-close:#64748b;--authrobo-close-hover:#cbd5e1;\n --authrobo-focus:#46c8ff;--authrobo-card-shadow:rgba(0,0,0,.6);\n --authrobo-last-bg:rgba(70,160,255,.18);--authrobo-last-fg:#8fc4ff;`;\n// Elements that either read tokens directly or need them for their descendants\n// (a standalone button has no stack ancestor; the overlay can't inherit from\n// its own card child), so every token-owning root declares the full set.\nconst TOKEN_ROOTS =\n \".authrobo-btn,.authrobo-stack,.authrobo-card,.authrobo-overlay,.authrobo-divider,.authrobo-title\";\nconst scoped = (prefix: string, tokens: string) =>\n TOKEN_ROOTS.split(\",\")\n .map((s) => `${prefix}${s.trim()}`)\n .join(\",\") + `{${tokens}}`;\n\nconst CSS = `\n${scoped(\"\", TOKENS_LIGHT)}\n@media (prefers-color-scheme:dark){${scoped(\"\", TOKENS_DARK)}}\n${scoped('[data-theme=\"light\"] ', TOKENS_LIGHT)}\n${scoped('[data-theme=\"dark\"] ', TOKENS_DARK)}\n.authrobo-btn{position:relative;display:flex;width:100%;align-items:center;justify-content:center;gap:10px;\n border:1px solid var(--authrobo-btn-border);background:var(--authrobo-btn-bg);color:var(--authrobo-btn-fg);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:var(--authrobo-btn-hover-bg);border-color:var(--authrobo-btn-hover-border);}\n.authrobo-btn:focus-visible{outline:2px solid var(--authrobo-focus);outline-offset:2px;}\n.authrobo-last{position:absolute;right:8px;top:50%;transform:translateY(-50%);pointer-events:none;\n font:600 10px/1 system-ui,sans-serif;letter-spacing:.02em;padding:3px 7px;border-radius:999px;\n background:var(--authrobo-last-bg);color:var(--authrobo-last-fg);}\n.authrobo-stack{display:flex;flex-direction:column;gap:10px;}\n.authrobo-title{font:700 16px system-ui,sans-serif;color:var(--authrobo-title-fg);}\n.authrobo-muted{color:var(--authrobo-muted);font:14px system-ui,sans-serif;margin:0;}\n.authrobo-error{color:var(--authrobo-error);font:14px system-ui,sans-serif;margin:0;}\n.authrobo-divider{display:flex;align-items:center;gap:10px;color:var(--authrobo-divider-fg);font:500 12px system-ui,sans-serif;}\n.authrobo-divider::before,.authrobo-divider::after{content:\"\";height:1px;flex:1;background:var(--authrobo-divider-line);}\n.authrobo-overlay{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;\n justify-content:center;padding:16px;background:var(--authrobo-overlay);backdrop-filter:blur(2px);\n animation:authrobo-fade .16s ease-out both;}\n.authrobo-card{position:relative;width:100%;max-width:360px;background:var(--authrobo-card-bg);border-radius:16px;\n padding:24px;box-shadow:0 20px 60px -12px var(--authrobo-card-shadow);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:var(--authrobo-close);font-size:18px;line-height:1;padding:4px;}\n.authrobo-close:hover{color:var(--authrobo-close-hover);}\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 lastUsed,\n}: {\n provider: Method;\n config?: AuthRoboConfig;\n label?: string;\n className?: string;\n style?: React.CSSProperties;\n onClick?: () => void;\n /** Show a \"Last used\" badge (the method the visitor signed in with before). */\n lastUsed?: boolean;\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 {lastUsed && <span className=\"authrobo-last\">Last used</span>}\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 const lastMethod = useLastMethod(c.clientId);\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) => !EMAIL_METHODS.includes(m));\n const email = shown.filter((m) => EMAIL_METHODS.includes(m));\n // Only worth pointing out the last-used method when there's a choice to make.\n const badge = (m: Method) => shown.length > 1 && m === lastMethod;\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 className=\"authrobo-title\">\n {title ?? `Sign in${data?.name ? ` to ${data.name}` : \"\"}`}\n </span>\n </div>\n )}\n\n {loading && <p className=\"authrobo-muted\">Loading sign-in options…</p>}\n {error && <p className=\"authrobo-error\">Couldn't load sign-in options.</p>}\n\n {social.map((m) => (\n <AuthRoboButton key={m} provider={m} config={c} lastUsed={badge(m)} />\n ))}\n {email.length > 0 && social.length > 0 && <div className=\"authrobo-divider\">or</div>}\n {email.map((m) => (\n <AuthRoboButton\n key={m}\n provider={m}\n config={c}\n label={m === \"magic_link\" ? \"Email me a sign-in link\" : \"Continue with email\"}\n lastUsed={badge(m)}\n />\n ))}\n\n {!loading && !error && shown.length === 0 && (\n <p className=\"authrobo-muted\">No sign-in methods are enabled for this app.</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\" and \"magic_link\" route to\n// the hosted email login page; the rest are social OAuth providers.\nexport type Method = \"google\" | \"linkedin\" | \"github\" | \"apple\" | \"password\" | \"magic_link\";\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 magic_link: { label: \"magic link\", icon: <MailIcon /> },\n};\n"],"mappings":"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","names":["jsx","jsxs"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@authrobo/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "React sign-in buttons and a configurable auth modal for AuthRobo — Google, LinkedIn, GitHub, Apple, and email.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"types": "./dist/index.d.ts",
|
|
14
14
|
"import": "./dist/index.js",
|
|
15
15
|
"require": "./dist/index.cjs"
|
|
16
|
-
}
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
17
18
|
},
|
|
18
19
|
"files": ["dist", "README.md"],
|
|
19
20
|
"scripts": {
|