@flowsta/login-button 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,422 @@
1
+ # @flowsta/login-button
2
+
3
+ **Login with Flowsta** button components for React, Vue, Qwik, and vanilla JavaScript.
4
+
5
+ Easily integrate OAuth 2.0 authentication with Flowsta into your web application using pre-designed, production-ready button components.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@flowsta/login-button.svg)](https://www.npmjs.com/package/@flowsta/login-button)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ ## Features
11
+
12
+ - ✅ **6 Button Variants** - Dark, light, and neutral themes with pill or rectangle shapes
13
+ - ✅ **Framework Support** - React, Vue 3, Qwik, and vanilla JavaScript
14
+ - ✅ **PKCE Built-in** - Secure OAuth 2.0 with Proof Key for Code Exchange
15
+ - ✅ **TypeScript** - Full type definitions included
16
+ - ✅ **Zero Dependencies** - No external runtime dependencies
17
+ - ✅ **Production Ready** - Professional button designs with proper accessibility
18
+ - ✅ **Resumable** - Qwik support for instant-on apps
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install @flowsta/login-button
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ### React
29
+
30
+ ```tsx
31
+ import { FlowstaLoginButton } from '@flowsta/login-button/react';
32
+
33
+ function App() {
34
+ return (
35
+ <FlowstaLoginButton
36
+ clientId="your-client-id"
37
+ redirectUri="https://yourapp.com/callback"
38
+ scopes={['profile', 'email']}
39
+ variant="dark-pill"
40
+ onSuccess={(data) => {
41
+ console.log('Authorization code:', data.code);
42
+ // Exchange code for token on your backend
43
+ }}
44
+ onError={(error) => {
45
+ console.error('Login error:', error);
46
+ }}
47
+ />
48
+ );
49
+ }
50
+ ```
51
+
52
+ ### Vue 3
53
+
54
+ ```vue
55
+ <template>
56
+ <FlowstaLoginButton
57
+ clientId="your-client-id"
58
+ redirectUri="https://yourapp.com/callback"
59
+ :scopes="['profile', 'email']"
60
+ variant="dark-pill"
61
+ @success="handleSuccess"
62
+ @error="handleError"
63
+ />
64
+ </template>
65
+
66
+ <script setup>
67
+ import { FlowstaLoginButton } from '@flowsta/login-button/vue';
68
+
69
+ const handleSuccess = (data) => {
70
+ console.log('Authorization code:', data.code);
71
+ // Exchange code for token on your backend
72
+ };
73
+
74
+ const handleError = (error) => {
75
+ console.error('Login error:', error);
76
+ };
77
+ </script>
78
+ ```
79
+
80
+ ### Qwik
81
+
82
+ ```tsx
83
+ import { component$ } from '@builder.io/qwik';
84
+ import { FlowstaLoginButton } from '@flowsta/login-button/qwik';
85
+
86
+ export default component$(() => {
87
+ return (
88
+ <FlowstaLoginButton
89
+ clientId="your-client-id"
90
+ redirectUri="https://yourapp.com/callback"
91
+ scopes={['profile', 'email']}
92
+ variant="dark-pill"
93
+ onSuccess$={(data) => {
94
+ console.log('Authorization code:', data.code);
95
+ // Exchange code for token on your backend
96
+ }}
97
+ onError$={(error) => {
98
+ console.error('Login error:', error);
99
+ }}
100
+ />
101
+ );
102
+ });
103
+ ```
104
+
105
+ ### Vanilla JavaScript
106
+
107
+ ```javascript
108
+ import { initFlowstaLoginButton } from '@flowsta/login-button/vanilla';
109
+
110
+ initFlowstaLoginButton('#login-container', {
111
+ clientId: 'your-client-id',
112
+ redirectUri: 'https://yourapp.com/callback',
113
+ scopes: ['profile', 'email'],
114
+ variant: 'dark-pill',
115
+ onSuccess: (data) => {
116
+ console.log('Authorization code:', data.code);
117
+ // Exchange code for token on your backend
118
+ },
119
+ onError: (error) => {
120
+ console.error('Login error:', error);
121
+ }
122
+ });
123
+ ```
124
+
125
+ ## Button Variants
126
+
127
+ Choose from 6 professionally designed button variants:
128
+
129
+ | Variant | Description | Best For |
130
+ |---------|-------------|----------|
131
+ | `dark-pill` | Dark background, rounded corners | Light backgrounds, modern UI |
132
+ | `dark-rectangle` | Dark background, square corners | Light backgrounds, professional UI |
133
+ | `light-pill` | Light background, rounded corners | Dark backgrounds, modern UI |
134
+ | `light-rectangle` | Light background, square corners | Dark backgrounds, professional UI |
135
+ | `neutral-pill` | Gray background, rounded corners | Any background, subtle integration |
136
+ | `neutral-rectangle` | Gray background, square corners | Any background, professional look |
137
+
138
+ ## OAuth Flow
139
+
140
+ The button handles the OAuth 2.0 Authorization Code Flow with PKCE:
141
+
142
+ 1. **User clicks button** → Component generates PKCE pair and redirects to Flowsta
143
+ 2. **User authenticates** → Flowsta login page (login.flowsta.com)
144
+ 3. **User grants permissions** → Consent screen shows requested scopes
145
+ 4. **Redirect back** → Your app receives authorization code
146
+ 5. **Exchange code** → Your backend exchanges code for access token
147
+
148
+ ### Handling the Callback
149
+
150
+ On your redirect URI page, handle the OAuth callback:
151
+
152
+ ```typescript
153
+ import { handleCallback, retrieveCodeVerifier, validateState } from '@flowsta/login-button';
154
+
155
+ // Parse callback parameters
156
+ const { code, state, error, errorDescription } = handleCallback();
157
+
158
+ if (error) {
159
+ console.error('OAuth error:', error, errorDescription);
160
+ } else if (code && state) {
161
+ // Validate state to prevent CSRF
162
+ if (validateState(state)) {
163
+ // Retrieve code verifier for token exchange
164
+ const codeVerifier = retrieveCodeVerifier(state);
165
+
166
+ // Exchange code for token on your backend
167
+ const response = await fetch('/api/auth/token', {
168
+ method: 'POST',
169
+ headers: { 'Content-Type': 'application/json' },
170
+ body: JSON.stringify({ code, codeVerifier })
171
+ });
172
+
173
+ const { access_token } = await response.json();
174
+ // Store token and create user session
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### Backend Token Exchange
180
+
181
+ Exchange the authorization code for an access token:
182
+
183
+ ```javascript
184
+ // Example: Node.js backend
185
+ app.post('/api/auth/token', async (req, res) => {
186
+ const { code, codeVerifier } = req.body;
187
+
188
+ const response = await fetch('https://auth-api.flowsta.com/oauth/token', {
189
+ method: 'POST',
190
+ headers: { 'Content-Type': 'application/json' },
191
+ body: JSON.stringify({
192
+ grant_type: 'authorization_code',
193
+ code,
194
+ client_id: process.env.FLOWSTA_CLIENT_ID,
195
+ client_secret: process.env.FLOWSTA_CLIENT_SECRET,
196
+ redirect_uri: process.env.REDIRECT_URI,
197
+ code_verifier: codeVerifier
198
+ })
199
+ });
200
+
201
+ const tokens = await response.json();
202
+ res.json(tokens);
203
+ });
204
+ ```
205
+
206
+ ## API Reference
207
+
208
+ ### Props / Options
209
+
210
+ All components accept the following configuration:
211
+
212
+ | Property | Type | Required | Default | Description |
213
+ |----------|------|----------|---------|-------------|
214
+ | `clientId` | `string` | ✅ | - | Your Flowsta application client ID |
215
+ | `redirectUri` | `string` | ✅ | - | URI to redirect back to after authentication |
216
+ | `scopes` | `string[]` | ❌ | `['profile']` | OAuth scopes: `'profile'`, `'email'` |
217
+ | `variant` | `string` | ❌ | `'dark-pill'` | Button variant (see variants above) |
218
+ | `loginUrl` | `string` | ❌ | `'https://login.flowsta.com'` | Flowsta login URL |
219
+ | `className` | `string` | ❌ | `''` | Custom CSS class name |
220
+ | `disabled` | `boolean` | ❌ | `false` | Whether button is disabled |
221
+
222
+ ### Events / Callbacks
223
+
224
+ #### React
225
+
226
+ ```tsx
227
+ interface FlowstaLoginButtonProps {
228
+ onSuccess?: (data: { code: string; state: string }) => void;
229
+ onError?: (error: { error: string; errorDescription?: string }) => void;
230
+ onClick?: () => void;
231
+ }
232
+ ```
233
+
234
+ #### Vue
235
+
236
+ ```vue
237
+ <FlowstaLoginButton
238
+ @success="(data) => { /* code, state */ }"
239
+ @error="(error) => { /* error, errorDescription */ }"
240
+ @click="() => { /* before redirect */ }"
241
+ />
242
+ ```
243
+
244
+ #### Qwik
245
+
246
+ ```tsx
247
+ <FlowstaLoginButton
248
+ onSuccess$={(data) => { /* code, state */ }}
249
+ onError$={(error) => { /* error, errorDescription */ }}
250
+ onClick$={() => { /* before redirect */ }}
251
+ />
252
+ ```
253
+
254
+ #### Vanilla
255
+
256
+ ```javascript
257
+ {
258
+ onSuccess: (data) => { /* code, state */ },
259
+ onError: (error) => { /* error, errorDescription */ },
260
+ onClick: () => { /* before redirect */ }
261
+ }
262
+ ```
263
+
264
+ ## OAuth Scopes
265
+
266
+ ### `profile` (default)
267
+
268
+ Returns user's basic profile information:
269
+
270
+ ```json
271
+ {
272
+ "userId": "user_abc123",
273
+ "displayName": "John Doe",
274
+ "did": "did:key:z6Mk...",
275
+ "agentPubKey": "uhCAk..."
276
+ }
277
+ ```
278
+
279
+ ### `email`
280
+
281
+ Returns user's email address (requires explicit permission):
282
+
283
+ ```json
284
+ {
285
+ "userId": "user_abc123",
286
+ "displayName": "John Doe",
287
+ "did": "did:key:z6Mk...",
288
+ "agentPubKey": "uhCAk...",
289
+ "email": "john@example.com",
290
+ "emailVerified": true
291
+ }
292
+ ```
293
+
294
+ **Note**: Email is stored encrypted in Flowsta. Users must explicitly grant permission for your app to access it.
295
+
296
+ ## Utilities
297
+
298
+ The package also exports OAuth utilities for custom implementations:
299
+
300
+ ```typescript
301
+ import {
302
+ generatePKCEPair,
303
+ generateCodeChallenge,
304
+ generateState,
305
+ buildAuthorizationUrl,
306
+ handleCallback,
307
+ validateState
308
+ } from '@flowsta/login-button';
309
+
310
+ // Generate PKCE pair
311
+ const { verifier, challenge } = await generatePKCEPair();
312
+
313
+ // Build custom authorization URL
314
+ const { url, state, codeVerifier } = await buildAuthorizationUrl({
315
+ clientId: 'your-client-id',
316
+ redirectUri: 'https://yourapp.com/callback',
317
+ scopes: ['profile', 'email']
318
+ });
319
+
320
+ // Handle callback
321
+ const { code, state, error } = handleCallback();
322
+
323
+ // Validate state
324
+ const isValid = validateState(state);
325
+ ```
326
+
327
+ ## Examples
328
+
329
+ ### Custom Button Content
330
+
331
+ #### React
332
+
333
+ ```tsx
334
+ <FlowstaLoginButton
335
+ clientId="your-client-id"
336
+ redirectUri="https://yourapp.com/callback"
337
+ >
338
+ <span>Custom Login Text</span>
339
+ </FlowstaLoginButton>
340
+ ```
341
+
342
+ #### Vue
343
+
344
+ ```vue
345
+ <FlowstaLoginButton
346
+ clientId="your-client-id"
347
+ redirectUri="https://yourapp.com/callback"
348
+ >
349
+ <span>Custom Login Text</span>
350
+ </FlowstaLoginButton>
351
+ ```
352
+
353
+ ### Vanilla with Custom Container
354
+
355
+ ```javascript
356
+ import { createFlowstaLoginButton } from '@flowsta/login-button/vanilla';
357
+
358
+ const button = createFlowstaLoginButton({
359
+ clientId: 'your-client-id',
360
+ redirectUri: 'https://yourapp.com/callback',
361
+ variant: 'neutral-pill'
362
+ });
363
+
364
+ document.getElementById('my-container').appendChild(button);
365
+ ```
366
+
367
+ ## TypeScript
368
+
369
+ Full TypeScript support with exported types:
370
+
371
+ ```typescript
372
+ import type {
373
+ ButtonVariant,
374
+ FlowstaScope,
375
+ FlowstaLoginConfig,
376
+ FlowstaLoginSuccess,
377
+ FlowstaLoginError,
378
+ FlowstaLoginButtonProps,
379
+ FlowstaLoginButtonQwikProps,
380
+ VueFlowstaLoginButtonProps
381
+ } from '@flowsta/login-button';
382
+ ```
383
+
384
+ ## Browser Support
385
+
386
+ - Chrome/Edge (latest)
387
+ - Firefox (latest)
388
+ - Safari (latest)
389
+ - Modern browsers with ES2020+ and Web Crypto API support
390
+
391
+ ## Security
392
+
393
+ - **PKCE**: All OAuth flows use PKCE (Proof Key for Code Exchange) for enhanced security
394
+ - **State Parameter**: CSRF protection via state parameter validation
395
+ - **Secure Storage**: Code verifiers stored in sessionStorage with state correlation
396
+ - **No Client Secret**: Client secret never exposed to browser (backend only)
397
+
398
+ ## Contributing
399
+
400
+ Contributions are welcome! Please see [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.
401
+
402
+ ## License
403
+
404
+ MIT © Flowsta
405
+
406
+ ## Support
407
+
408
+ - 📖 [Documentation](https://docs.flowsta.com/guides/login-with-flowsta)
409
+ - 💬 [Community](https://community.flowsta.com)
410
+ - 🐛 [Issue Tracker](https://github.com/WeAreFlowsta/flowsta-sdk/issues)
411
+ - 📧 [Email Support](mailto:support@flowsta.com)
412
+
413
+ ## Related Packages
414
+
415
+ - [`@flowsta/auth-sdk`](https://www.npmjs.com/package/@flowsta/auth-sdk) - Core authentication SDK
416
+ - [`@flowsta/auth-react`](https://www.npmjs.com/package/@flowsta/auth-react) - React hooks and context
417
+ - [`@flowsta/auth-client`](https://www.npmjs.com/package/@flowsta/auth-client) - HTTP client for Flowsta API
418
+
419
+ ---
420
+
421
+ **Made with ❤️ by the Flowsta team**
422
+