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