@authrobo/react 0.2.0 → 0.5.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 CHANGED
@@ -1,13 +1,25 @@
1
1
  # @authrobo/react
2
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).
3
+ Drop-in React sign-in for [AuthRobo](https://authrobo.com). Renders exactly the
4
+ methods you've enabled in your dashboard social providers (Google, LinkedIn,
5
+ GitHub, Apple) plus an email switcher (**Magic Link · Register · Login**) — or
6
+ compose your own UI from a single configurable button.
6
7
 
7
8
  ```bash
8
9
  npm i @authrobo/react
9
10
  ```
10
11
 
12
+ ## Contents
13
+
14
+ - [Quick start](#quick-start) — the whole box: `<AuthRoboAuth>`
15
+ - [Just one button](#just-one-button) — `<AuthRoboButton>`, single or mapped
16
+ - [Two ways to sign in](#two-ways-to-sign-in-direct-vs-proxied) — direct vs. `startPath`
17
+ - [The proxy server contract](#the-proxy-server-contract) — routes your app implements
18
+ - [Theming](#theming)
19
+ - [API](#api)
20
+
21
+ ---
22
+
11
23
  ## Quick start
12
24
 
13
25
  Wrap your app once with your AuthRobo config:
@@ -18,7 +30,7 @@ import { AuthRoboProvider } from "@authrobo/react";
18
30
  <AuthRoboProvider
19
31
  config={{
20
32
  issuerUrl: "https://authrobo.com",
21
- clientId: process.env.NEXT_PUBLIC_AUTHROBO_CLIENT_ID!, // arc_...
33
+ clientId: process.env.NEXT_PUBLIC_AUTHROBO_CLIENT_ID!, // arc_... (public)
22
34
  redirectUri: "https://yourapp.com/auth/callback",
23
35
  }}
24
36
  >
@@ -26,18 +38,8 @@ import { AuthRoboProvider } from "@authrobo/react";
26
38
  </AuthRoboProvider>;
27
39
  ```
28
40
 
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
+ Then drop in the full widget. It calls AuthRobo's public `/api/v1/config` and
42
+ renders only what you've enabled:
41
43
 
42
44
  ```tsx
43
45
  import { AuthRoboAuth } from "@authrobo/react";
@@ -51,26 +53,151 @@ const [open, setOpen] = useState(false);
51
53
  <AuthRoboAuth mode="modal" open={open} onClose={() => setOpen(false)} />
52
54
  ```
53
55
 
54
- ## How the flow works
56
+ **What the email switcher collects** (each tab shows exactly its flow's fields):
57
+
58
+ | Tab | Fields | Result |
59
+ | --- | --- | --- |
60
+ | **Magic Link** | email | a one-time sign-in link is emailed |
61
+ | **Register** | name · email · password | creates the account |
62
+ | **Login** | email · password | signs in |
63
+
64
+ Magic Link is first; a tab only appears if its method is enabled (`magic_link`
65
+ for Magic Link, `password` for Register + Login).
66
+
67
+ ## Just one button
68
+
69
+ Don't want the whole box? Use `<AuthRoboButton>` — it takes a `provider` and
70
+ renders that provider's branded button. Perfect for a single "Continue with
71
+ Google", or map an array to lay them out however your design wants:
72
+
73
+ ```tsx
74
+ import { AuthRoboButton } from "@authrobo/react";
75
+
76
+ // one button
77
+ <AuthRoboButton provider="google" />
78
+
79
+ // your own set, your own layout
80
+ <div className="grid gap-2">
81
+ {["google", "linkedin", "github"].map((p) => (
82
+ <AuthRoboButton key={p} provider={p} />
83
+ ))}
84
+ </div>
85
+
86
+ // custom label / classes
87
+ <AuthRoboButton provider="github" label="Sign in with GitHub" className="my-btn" />
88
+ ```
89
+
90
+ `provider` is any enabled method (`"google" | "linkedin" | "github" | "apple" |
91
+ "facebook" | "password" | "magic_link"`). Clicking begins that flow via
92
+ `startSignIn` — pass `startPath` here too if you proxy (see below).
93
+
94
+ ### Neutral or branded buttons
95
+
96
+ By default social buttons share one consistent, themeable chrome (`buttonStyle="neutral"`).
97
+ Switch to `buttonStyle="branded"` to give each button its provider's own colours —
98
+ LinkedIn blue, GitHub black, Google white with a Google-blue label, Facebook blue,
99
+ Apple black (monochrome glyphs flip to white for legibility):
100
+
101
+ ```tsx
102
+ <AuthRoboAuth buttonStyle="branded" />
103
+ <AuthRoboButton provider="linkedin" buttonStyle="branded" />
104
+ ```
55
105
 
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.
106
+ ## Loading &amp; the config cache
107
+
108
+ `<AuthRoboProvider>` **prefetches** your app's enabled methods the moment it
109
+ mountsi.e. on app start — and caches them per `(issuer, clientId)`. So by the
110
+ time a login modal or page opens, the methods are already resolved: no
111
+ request-on-open, no flash. (Not using the provider? Call `prefetchAppConfig(config)`
112
+ yourself on app start.) While anything is loading, a theme-aware `<Spinner>` is
113
+ shown (it inherits `currentColor`, so it matches whatever you've themed).
114
+
115
+ ## Two ways to sign in (direct vs. proxied)
116
+
117
+ The same components support two flows; pick with the `startPath` prop.
118
+
119
+ **Direct (default).** No backend beyond one callback. Social buttons redirect to
120
+ the provider; the Login/Register forms do a native cross-origin form POST to
121
+ AuthRobo's `authorize` endpoint; Magic Link posts to `magic/start`. AuthRobo
122
+ returns to your `redirectUri` with a one-time `code`; your server exchanges it
123
+ (with your `client_secret`) for tokens. Credentials go straight to AuthRobo —
124
+ never to your own server.
125
+
126
+ ```tsx
127
+ <AuthRoboAuth /> // uses config.redirectUri
128
+ ```
129
+
130
+ **Proxied (`startPath`).** Best when you keep the session in **httpOnly
131
+ cookies**. Every action is proxied through your own server so the CSRF state
132
+ cookie and the token exchange stay first-party. Password login/register become a
133
+ same-origin JSON POST that sets your cookies with **no page redirect**, so
134
+ `onSuccess` fires and you can close your modal in place:
135
+
136
+ ```tsx
137
+ <AuthRoboAuth
138
+ startPath="/api/auth"
139
+ onSuccess={() => { refreshSession(); close(); }}
140
+ />
141
+ ```
142
+
143
+ > **Note:** in direct mode the password field lives in your app's DOM (submitted
144
+ > to AuthRobo). If you require the credential entry to be isolated on AuthRobo's
145
+ > origin, use `<AuthRoboButton provider="password" />`, which redirects to the
146
+ > hosted page instead.
147
+
148
+ ## The proxy server contract
149
+
150
+ When you pass `startPath`, the components call these routes on **your** server
151
+ (shown for `startPath="/api/auth"`). Each one forwards to AuthRobo and manages
152
+ your session cookies:
153
+
154
+ | Trigger | Request | Your route should |
155
+ | --- | --- | --- |
156
+ | Social button | `GET /api/auth/{provider}/start` | set a CSRF state cookie, redirect to AuthRobo's provider start with your callback as `redirect_uri` |
157
+ | Login | `POST /api/auth/login` `{ email, password }` | password-grant to AuthRobo, set httpOnly cookies, `2xx` |
158
+ | Register | `POST /api/auth/register` `{ name?, email, password }` | create + set cookies, `2xx` |
159
+ | Magic Link | `POST /api/auth/magic` `{ email }` | call AuthRobo `magic/start`, `2xx` |
160
+ | Callback | `GET /api/auth/{method}/callback?code=…` | exchange the code (+ `client_secret`) for tokens, set cookies |
161
+
162
+ On a non-`2xx`, return `{ "error": "message" }` — the widget shows it inline.
163
+ (See AuthRobo's docs for a copy-pasteable Next.js implementation of these routes.)
164
+
165
+ ## Theming
166
+
167
+ Styling is self-contained and theme-aware (light/dark via `prefers-color-scheme`
168
+ or a `data-theme` attribute). Override any CSS variable on an ancestor to brand
169
+ it — e.g. `--authrobo-accent` / `--authrobo-accent-fg` recolour the active tab
170
+ and the primary button:
171
+
172
+ ```css
173
+ .my-auth .authrobo-stack,
174
+ .my-auth .authrobo-btn {
175
+ --authrobo-accent: #f5b81d;
176
+ --authrobo-accent-fg: #1c1400;
177
+ --authrobo-input-bg: #070f1d;
178
+ --authrobo-btn-bg: #070f1d;
179
+ --authrobo-tabs-bg: #070f1d;
180
+ }
181
+ ```
182
+
183
+ Other tokens: `--authrobo-btn-*`, `--authrobo-input-fg`, `--authrobo-title-fg`,
184
+ `--authrobo-muted`, `--authrobo-divider-*`, `--authrobo-focus`, `--authrobo-error`.
61
185
 
62
186
  ## API
63
187
 
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.
188
+ - `AuthRoboProvider({ config, children })` — supplies config via context **and
189
+ prefetches** the app config on mount.
190
+ - `AuthRoboButton({ provider, config?, label?, className?, style?, onClick?, startPath?, lastUsed?, buttonStyle? })`
191
+ one button. `provider` is any enabled `Method`; `buttonStyle` is `"neutral" | "branded"`.
192
+ - `AuthRoboAuth({ config?, mode?, open?, onClose?, onSuccess?, title?, showHeader?, methods?, startPath?, buttonStyle? })` —
193
+ the full widget: social buttons + the email switcher. `methods` restricts/reorders;
194
+ `showHeader={false}` hides the logo/title row when you supply your own heading.
195
+ - `useAppConfig(config?)` → `{ data, loading, error }` — branding + enabled methods (cached).
196
+ - `prefetchAppConfig(config)` → `Promise<void>` — warm the cache on app start.
197
+ - `useLastMethod(clientId)` → `Method | null` — the method last used on this client.
198
+ - `startSignIn(config, method, opts?)` — imperatively begin a flow. `opts`:
199
+ `{ state?, loginHint?, screen?: "login" | "register" | "magic", startPath? }`.
200
+ - `Spinner({ style?, className? })` — the theme-aware loading spinner.
201
+ - `PROVIDER_META` / `BRAND_STYLE` — `{ label, icon }` and the branded palette per method.
72
202
 
73
203
  `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.