flowlink-auth 2.7.3 → 2.7.4

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/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "flowlink-auth",
3
- "version": "2.7.3",
3
+ "version": "2.7.4",
4
4
  "description": "Custom auth library",
5
5
  "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
6
+ "types": "src/index.d.ts",
7
7
  "files": [
8
- "dist"
8
+ "dist",
9
+ "src"
9
10
  ],
10
11
  "scripts": {
11
12
  "build": "esbuild src/*.js src/*.jsx --outdir=dist --bundle --format=esm --loader:.js=jsx --loader:.jsx=jsx",
@@ -0,0 +1,22 @@
1
+ // src/ErrorBox.jsx
2
+ import React from 'react'
3
+
4
+ export default function ErrorBox({ message }) {
5
+ const style = {
6
+ border: '1px solid #e53935',
7
+ background: '#fff6f6',
8
+ color: '#b71c1c',
9
+ padding: 12,
10
+ borderRadius: 8,
11
+ fontFamily: 'system-ui, Roboto, Arial',
12
+ maxWidth: 640,
13
+ margin: '8px auto'
14
+ }
15
+ return (
16
+ <div role="alert" style={style}>
17
+ <strong>flowlink Auth Error —</strong>
18
+ <div style={{ marginTop: 6 }}>{message}</div>
19
+ </div>
20
+ )
21
+ }
22
+
package/src/SignIn.jsx ADDED
@@ -0,0 +1,212 @@
1
+ // src/signin.jsx
2
+ 'use client'
3
+ import React, { useState } from 'react'
4
+ import { useAuth } from './provider.js'
5
+
6
+ export default function SignIn({ onSuccess } = {}) {
7
+ const {
8
+ publishableKey,
9
+ baseUrl,
10
+ redirect,
11
+ redirectTo,
12
+ user,
13
+ loadingUser,
14
+ completeLogin,
15
+ fetchMe,
16
+ setUser
17
+ } = useAuth()
18
+
19
+ const [email, setEmail] = useState('')
20
+ const [password, setPassword] = useState('')
21
+ const [loading, setLoading] = useState(false)
22
+ const [error, setError] = useState(null)
23
+ const [message, setMessage] = useState(null)
24
+
25
+ if (loadingUser) return null
26
+
27
+ if (user && redirect) {
28
+ if (typeof redirectTo === 'function') redirectTo(redirect)
29
+ else if (typeof window !== 'undefined') window.location.assign(redirect)
30
+ return null
31
+ }
32
+
33
+ async function submit(e) {
34
+ e.preventDefault()
35
+ setError(null)
36
+ setMessage(null)
37
+
38
+ if (!email || !password) {
39
+ setError('Email and password are required')
40
+ return
41
+ }
42
+
43
+ setLoading(true)
44
+
45
+ try {
46
+ const endpoint = `${(baseUrl || '').replace(/\/+$/, '')}/api/sdk/login`
47
+
48
+ const res = await fetch(endpoint, {
49
+ method: 'POST',
50
+ credentials: 'include',
51
+ headers: {
52
+ 'Content-Type': 'application/json',
53
+ 'x-publishable-key': publishableKey || ''
54
+ },
55
+ body: JSON.stringify({ email, password })
56
+ })
57
+
58
+ const ct = res.headers.get('content-type') || ''
59
+ let data = {}
60
+ if (ct.includes('application/json')) data = await res.json()
61
+ else {
62
+ const text = await res.text()
63
+ throw new Error(`Unexpected response (status ${res.status}): ${text.slice(0, 200)}`)
64
+ }
65
+
66
+ if (!res.ok) throw new Error(data.error || data.message || `Login failed (status ${res.status})`)
67
+
68
+ const serverUser = data.user ?? null
69
+ if (serverUser && typeof setUser === 'function') {
70
+ try { setUser(serverUser) } catch (_) {}
71
+ }
72
+
73
+ if (typeof completeLogin === 'function') {
74
+ try {
75
+ await completeLogin()
76
+ } catch (e) {
77
+ if (typeof fetchMe === 'function') await fetchMe()
78
+ }
79
+ } else if (typeof fetchMe === 'function') {
80
+ await fetchMe()
81
+ }
82
+
83
+ if (onSuccess) {
84
+ try { onSuccess(data) } catch (_) {}
85
+ }
86
+
87
+ setMessage('Signed in. Redirecting...')
88
+ if (redirect) {
89
+ setTimeout(() => {
90
+ if (typeof redirectTo === 'function') redirectTo(redirect)
91
+ else if (typeof window !== 'undefined') window.location.assign(redirect)
92
+ }, 250)
93
+ }
94
+ } catch (err) {
95
+ setError(err.message || 'Network error')
96
+ } finally {
97
+ setLoading(false)
98
+ }
99
+ }
100
+
101
+ // --- OAuth start flow (Google / GitHub) ---
102
+ async function startOAuthFlow(provider) {
103
+ setError(null)
104
+ setLoading(true)
105
+
106
+ try {
107
+ const rid =
108
+ (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')
109
+ ? crypto.randomUUID()
110
+ : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`
111
+
112
+ // Use main app signin page as callback so user lands on /signin after flow
113
+ const callbackUrl = encodeURIComponent(`${window.location.origin}/signin`)
114
+
115
+ const sdkBase = (typeof process !== 'undefined' && process.env && process.env.NEXT_PUBLIC_FLOWLINK_BASE_URL)
116
+ || baseUrl || 'http://localhost:3001'
117
+ const startUrl = `${sdkBase.replace(/\/+$/, '')}/sdk/auth/start?rid=${rid}&source=${encodeURIComponent(provider)}&callbackUrl=${callbackUrl}`
118
+
119
+ if (!publishableKey) {
120
+ throw new Error('Missing publishable key (client side). Set NEXT_PUBLIC_FLOWLINK_PUBLISHABLE_KEY or provide publishableKey in provider.')
121
+ }
122
+
123
+ const res = await fetch(startUrl, {
124
+ method: 'GET',
125
+ headers: { 'x-publishable-key': publishableKey },
126
+ mode: 'cors'
127
+ })
128
+
129
+ const data = await res.json().catch(() => null)
130
+ if (!res.ok) throw new Error(data?.error || `OAuth start failed (${res.status})`)
131
+ if (!data?.oauthUrl) throw new Error('SDK start did not return oauthUrl')
132
+
133
+ window.location.href = data.oauthUrl
134
+ } catch (err) {
135
+ console.error('OAuth start error:', err)
136
+ setError(err?.message || 'OAuth start failed')
137
+ setLoading(false)
138
+ }
139
+ }
140
+
141
+ const handleGoogle = (e) => {
142
+ if (e && typeof e.preventDefault === 'function') e.preventDefault()
143
+ startOAuthFlow('google')
144
+ }
145
+
146
+ const handleGithub = (e) => {
147
+ if (e && typeof e.preventDefault === 'function') e.preventDefault()
148
+ startOAuthFlow('github')
149
+ }
150
+
151
+ return (
152
+ <div style={overlay}>
153
+ <div style={modal}>
154
+ <h2 style={title}>Sign in</h2>
155
+ <p style={subtitle}>Welcome back — enter your credentials.</p>
156
+
157
+ <form onSubmit={submit} style={{ width: '100%' }}>
158
+ <label style={label}>Email</label>
159
+ <input
160
+ style={input}
161
+ value={email}
162
+ onChange={e => setEmail(e.target.value)}
163
+ type="email"
164
+ required
165
+ />
166
+
167
+ <label style={label}>Password</label>
168
+ <input
169
+ style={input}
170
+ type="password"
171
+ value={password}
172
+ onChange={e => setPassword(e.target.value)}
173
+ required
174
+ />
175
+
176
+ <div style={{ marginTop: 12 }}>
177
+ <button style={button} type="submit" disabled={loading}>
178
+ {loading ? 'Signing in…' : 'Sign in'}
179
+ </button>
180
+ </div>
181
+
182
+ <div style={{ display: 'flex', gap: 8, marginTop: 16 }}>
183
+ <button type="button" onClick={handleGoogle} style={oauthButtonGoogle} disabled={loading}>
184
+ Continue with Google
185
+ </button>
186
+
187
+ <button type="button" onClick={handleGithub} style={oauthButtonGithub} disabled={loading}>
188
+ Continue with GitHub
189
+ </button>
190
+ </div>
191
+
192
+ {error && <div style={errorBox}>{error}</div>}
193
+ {message && <div style={successBox}>{message}</div>}
194
+ </form>
195
+ </div>
196
+ </div>
197
+ )
198
+ }
199
+
200
+ /* styles */
201
+ const overlay = { position: 'fixed', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.45)', zIndex: 9999, padding: 20 }
202
+ const modal = { width: '100%', maxWidth: 420, background: '#0f1724', color: '#fff', borderRadius: 12, padding: 22, boxShadow: '0 10px 30px rgba(2,6,23,0.6)', border: '1px solid rgba(255,255,255,0.04)' }
203
+ const title = { margin: 0, fontSize: 20, fontWeight: 600 }
204
+ const subtitle = { marginTop: 6, marginBottom: 14, color: '#cbd5e1', fontSize: 13 }
205
+ const label = { display: 'block', color: '#cbd5e1', fontSize: 13, marginTop: 8 }
206
+ const input = { width: '100%', padding: '10px 12px', marginTop: 6, borderRadius: 8, border: '1px solid rgba(255,255,255,0.06)', background: '#0b1220', color: '#fff', boxSizing: 'border-box' }
207
+ const button = { width: '100%', padding: '10px 12px', borderRadius: 8, background: 'linear-gradient(90deg,#06b6d4,#2563eb)', color: '#0b1220', border: 'none', fontWeight: 700, cursor: 'pointer' }
208
+ const oauthButtonGoogle = { flex: 1, padding: '10px 12px', borderRadius: 8, background: '#db4437', color: '#fff', border: 'none', cursor: 'pointer' }
209
+ const oauthButtonGithub = { flex: 1, padding: '10px 12px', borderRadius: 8, background: '#24292f', color: '#fff', border: 'none', cursor: 'pointer' }
210
+ const errorBox = { marginTop: 10, color: '#ffb4b4', fontSize: 13 }
211
+ const successBox = { marginTop: 10, color: '#bef264', fontSize: 13 }
212
+
package/src/SignUp.jsx ADDED
@@ -0,0 +1,260 @@
1
+ // src/signup.jsx
2
+ 'use client'
3
+ import React, { useState } from 'react'
4
+ import { useAuth } from './provider.js'
5
+
6
+ export default function SignUp() {
7
+ const {
8
+ publishableKey,
9
+ baseUrl,
10
+ redirect,
11
+ redirectTo,
12
+ user,
13
+ loadingUser,
14
+ fetchMe,
15
+ setUser
16
+ } = useAuth()
17
+
18
+ const [name, setName] = useState('')
19
+ const [email, setEmail] = useState('')
20
+ const [password, setPassword] = useState('')
21
+ const [loading, setLoading] = useState(false)
22
+ const [error, setError] = useState(null)
23
+ const [message, setMessage] = useState(null)
24
+
25
+ if (loadingUser) return null
26
+
27
+ if (user && redirect) {
28
+ if (typeof redirectTo === 'function') redirectTo(redirect)
29
+ else if (typeof window !== 'undefined') window.location.assign(redirect)
30
+ return null
31
+ }
32
+
33
+ async function submit(e) {
34
+ e.preventDefault()
35
+ setError(null)
36
+ setMessage(null)
37
+ setLoading(true)
38
+
39
+ const url = `${(baseUrl || '').replace(/\/+$/, '')}/api/sdk/signup`
40
+
41
+ try {
42
+ const res = await fetch(url, {
43
+ method: 'POST',
44
+ credentials: 'include',
45
+ headers: {
46
+ 'Content-Type': 'application/json',
47
+ 'x-publishable-key': publishableKey || ''
48
+ },
49
+ body: JSON.stringify({ name, email, password })
50
+ })
51
+
52
+ const data = await res.json().catch(() => ({}))
53
+ if (!res.ok) throw new Error(data.error || 'Signup failed')
54
+
55
+ if (data.user && typeof setUser === 'function') setUser(data.user)
56
+ if (typeof fetchMe === 'function') await fetchMe()
57
+
58
+ setMessage('Account created. Redirecting…')
59
+
60
+ if (redirect) {
61
+ setTimeout(() => {
62
+ if (typeof redirectTo === 'function') redirectTo(redirect)
63
+ else if (typeof window !== 'undefined') window.location.assign(redirect)
64
+ }, 300)
65
+ }
66
+ } catch (err) {
67
+ setError(err?.message ?? 'Network error')
68
+ console.error('Signup error:', err)
69
+ } finally {
70
+ setLoading(false)
71
+ }
72
+ }
73
+
74
+ async function startOAuthFlow(provider) {
75
+ // prevent double clicks
76
+ setError(null)
77
+ setLoading(true)
78
+
79
+ try {
80
+ const rid =
81
+ (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')
82
+ ? crypto.randomUUID()
83
+ : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`
84
+
85
+ // callback must match what your SDK start expects and what Google console allows
86
+ const callbackUrl = encodeURIComponent(`${window.location.origin}/signup`)
87
+
88
+ // build start URL (server returns { oauthUrl })
89
+ const sdkBase = (typeof process !== 'undefined' && process.env && process.env.NEXT_PUBLIC_FLOWLINK_BASE_URL)
90
+ || baseUrl || window.location.origin.replace(/\/+$/, '')
91
+ const startUrl = `${sdkBase}/sdk/auth/start?rid=${rid}&source=${encodeURIComponent(provider)}&callbackUrl=${callbackUrl}`
92
+
93
+ // ensure publishableKey exists
94
+ if (!publishableKey) {
95
+ throw new Error('Missing publishable key (client side). Set NEXT_PUBLIC_FLOWLINK_PUBLISHABLE_KEY or provide publishableKey in provider.')
96
+ }
97
+
98
+ const res = await fetch(startUrl, {
99
+ method: 'GET',
100
+ headers: {
101
+ 'x-publishable-key': publishableKey
102
+ }
103
+ })
104
+
105
+ const data = await res.json().catch(() => null)
106
+ if (!res.ok) {
107
+ throw new Error(data?.error || `OAuth start failed (${res.status})`)
108
+ }
109
+ if (!data?.oauthUrl) {
110
+ throw new Error('SDK start did not return oauthUrl')
111
+ }
112
+
113
+ // navigate to provider (Google/GitHub)
114
+ window.location.href = data.oauthUrl
115
+ } catch (err) {
116
+ console.error('OAuth start error:', err)
117
+ setError(err?.message || 'OAuth start failed')
118
+ setLoading(false)
119
+ }
120
+ }
121
+
122
+ const handleGoogle = (e) => {
123
+ if (e && typeof e.preventDefault === 'function') e.preventDefault()
124
+ startOAuthFlow('google')
125
+ }
126
+
127
+ const handleGithub = (e) => {
128
+ if (e && typeof e.preventDefault === 'function') e.preventDefault()
129
+ startOAuthFlow('github')
130
+ }
131
+ return (
132
+ <div style={overlay}>
133
+ <div style={modal}>
134
+ <h2 style={title}>Create account</h2>
135
+
136
+ <form onSubmit={submit}>
137
+ <label style={label}>Full name</label>
138
+ <input
139
+ name="name"
140
+ style={input}
141
+ value={name}
142
+ onChange={e => setName(e.target.value)}
143
+ autoComplete="name"
144
+ />
145
+
146
+ <label style={label}>Email</label>
147
+ <input
148
+ name="email"
149
+ type="email"
150
+ style={input}
151
+ value={email}
152
+ onChange={e => setEmail(e.target.value)}
153
+ required
154
+ autoComplete="email"
155
+ />
156
+
157
+ <label style={label}>Password</label>
158
+ <input
159
+ name="password"
160
+ type="password"
161
+ style={input}
162
+ value={password}
163
+ onChange={e => setPassword(e.target.value)}
164
+ required
165
+ autoComplete="new-password"
166
+ />
167
+
168
+ <div style={{ marginTop: 12 }}>
169
+ <button style={button} type="submit" disabled={loading}>
170
+ {loading ? 'Creating...' : 'Create account'}
171
+ </button>
172
+ </div>
173
+
174
+ <div style={{ display: 'flex', gap: 8, marginTop: 16 }}>
175
+ <button
176
+ type="button"
177
+ onClick={handleGoogle}
178
+ style={oauthButtonGoogle}
179
+ >
180
+ Continue with Google
181
+ </button>
182
+
183
+ <button
184
+ type="button"
185
+ onClick={handleGithub}
186
+ style={oauthButtonGithub}
187
+ >
188
+ Continue with GitHub
189
+ </button>
190
+ </div>
191
+
192
+ {error && <div style={errorBox}>{error}</div>}
193
+ {message && <div style={successBox}>{message}</div>}
194
+ </form>
195
+ </div>
196
+ </div>
197
+ )
198
+ }
199
+
200
+ const overlay = {
201
+ position: 'fixed',
202
+ inset: 0,
203
+ background: 'rgba(0,0,0,0.5)',
204
+ display: 'flex',
205
+ justifyContent: 'center',
206
+ alignItems: 'center',
207
+ padding: 20,
208
+ zIndex: 9999
209
+ }
210
+ const modal = {
211
+ width: '100%',
212
+ maxWidth: 420,
213
+ background: '#0f1724',
214
+ borderRadius: 12,
215
+ padding: 24,
216
+ color: '#fff'
217
+ }
218
+ const title = { margin: 0, fontSize: 20, fontWeight: 600 }
219
+ const label = { marginTop: 10, display: 'block', fontSize: 13 }
220
+ const input = {
221
+ width: '100%',
222
+ padding: '10px 12px',
223
+ borderRadius: 8,
224
+ background: '#0b1220',
225
+ color: '#fff',
226
+ border: '1px solid #1e293b',
227
+ marginTop: 6
228
+ }
229
+ const button = {
230
+ marginTop: 15,
231
+ width: '100%',
232
+ padding: '10px 12px',
233
+ borderRadius: 8,
234
+ background: '#2563eb',
235
+ border: 'none',
236
+ color: '#fff',
237
+ fontWeight: 600,
238
+ cursor: 'pointer'
239
+ }
240
+ const oauthButtonGoogle = {
241
+ flex: 1,
242
+ padding: '10px 12px',
243
+ borderRadius: 8,
244
+ background: '#db4437',
245
+ color: '#fff',
246
+ border: 'none',
247
+ cursor: 'pointer'
248
+ }
249
+ const oauthButtonGithub = {
250
+ flex: 1,
251
+ padding: '10px 12px',
252
+ borderRadius: 8,
253
+ background: '#24292f',
254
+ color: '#fff',
255
+ border: 'none',
256
+ cursor: 'pointer'
257
+ }
258
+ const errorBox = { marginTop: 10, color: '#ffb4b4', fontSize: 13 }
259
+ const successBox = { marginTop: 10, color: '#bef264', fontSize: 13 }
260
+
package/src/api.js ADDED
@@ -0,0 +1,60 @@
1
+ // src/api.js
2
+ import { getConfigSafe, getCSRFToken } from './init.js'
3
+
4
+ /**
5
+ * Secure API call wrapper with CSRF protection and validation
6
+ */
7
+ export const makeApi = () => {
8
+ const cfg = getConfigSafe()
9
+ return {
10
+ call: async (path, opts = {}) => {
11
+ if (!cfg) {
12
+ return { status: 400, body: { error: 'flowlink-auth: SDK not initialized (publishable key missing)' } }
13
+ }
14
+
15
+ // Security: Validate path
16
+ if (!path || typeof path !== 'string') {
17
+ return { status: 400, body: { error: 'Invalid API path' } }
18
+ }
19
+
20
+ // Security: Prevent open redirect
21
+ if (path.startsWith('//') || path.startsWith('http')) {
22
+ return { status: 400, body: { error: 'Invalid API path' } }
23
+ }
24
+
25
+ const headers = {
26
+ 'Content-Type': 'application/json',
27
+ 'x-publishable-key': cfg.publishableKey,
28
+ ...(opts.headers || {})
29
+ }
30
+
31
+ // Security: Add CSRF token if available
32
+ const csrfToken = getCSRFToken()
33
+ if (csrfToken && opts.method && opts.method.toUpperCase() !== 'GET') {
34
+ headers['x-csrf-token'] = csrfToken
35
+ }
36
+
37
+ try {
38
+ // Security: Validate URL before fetch
39
+ const fullUrl = new URL(cfg.baseUrl + path).href
40
+
41
+ const res = await fetch(fullUrl, {
42
+ ...opts,
43
+ headers,
44
+ // Security: Include credentials for cookie-based auth
45
+ credentials: 'include',
46
+ // Security: Enforce no referrer for sensitive requests
47
+ referrerPolicy: 'strict-origin-when-cross-origin'
48
+ })
49
+
50
+ const body = await res.json().catch(() => null)
51
+ return { status: res.status, body }
52
+ } catch (err) {
53
+ // Security: Don't leak error details
54
+ console.error('API call failed:', err.message)
55
+ return { status: 500, body: { error: 'Request failed. Please try again.' } }
56
+ }
57
+ }
58
+ }
59
+ }
60
+
@@ -0,0 +1,69 @@
1
+ // flowlink-auth/src/createAuthMiddleware.js
2
+ import { NextResponse } from 'next/server'
3
+ import { jwtVerify } from 'jose'
4
+
5
+ /**
6
+ * Factory that returns a Next.js middleware function.
7
+ * - secret: JWT HMAC secret (string)
8
+ * - redirectTo: where to send unauthenticated users (default '/signin')
9
+ *
10
+ * Note: the app's middleware.ts should export the returned function as default
11
+ * and set `export const config = { matcher: [...] }` for paths to protect.
12
+ */
13
+ export function createAuthMiddleware({ secret, redirectTo = '/signin', publicPaths = [] } = {}) {
14
+ if (!secret) {
15
+ throw new Error('createAuthMiddleware: secret is required')
16
+ }
17
+
18
+ if (secret.length < 32) {
19
+ console.warn('createAuthMiddleware: secret should be at least 32 characters long for security')
20
+ }
21
+
22
+ return async function middleware(req) {
23
+ const url = req.nextUrl.clone()
24
+ const pathname = req.nextUrl.pathname
25
+
26
+ // Check if path is public (no auth required)
27
+ if (publicPaths && publicPaths.some(path => pathname.startsWith(path))) {
28
+ return NextResponse.next()
29
+ }
30
+
31
+ // 1) Read token from cookie (secure, httpOnly)
32
+ const token = req.cookies.get?.('flowlink_token')?.value ?? null
33
+
34
+ if (!token) {
35
+ // No token -> redirect to sign-in
36
+ url.pathname = redirectTo
37
+ return NextResponse.redirect(url)
38
+ }
39
+
40
+ // 2) Validate token signature & expiry
41
+ try {
42
+ // Security: Use jose for secure JWT verification
43
+ const key = new TextEncoder().encode(secret)
44
+ const verified = await jwtVerify(token, key, {
45
+ algorithms: ['HS256'] // Restrict to HS256
46
+ })
47
+
48
+ // 3) Security: Add user info to request headers for downstream services
49
+ const requestHeaders = new Headers(req.headers)
50
+ requestHeaders.set('x-user-id', verified.payload.userId || '')
51
+ requestHeaders.set('x-tenant-id', verified.payload.tenantId || '')
52
+
53
+ // token OK -> continue with verified user info
54
+ return NextResponse.next({
55
+ request: {
56
+ headers: requestHeaders
57
+ }
58
+ })
59
+ } catch (err) {
60
+ // Security: Log failed verification but don't expose details to user
61
+ console.error('Token verification failed:', err.message)
62
+
63
+ // invalid/expired token -> redirect to sign-in
64
+ url.pathname = redirectTo
65
+ return NextResponse.redirect(url)
66
+ }
67
+ }
68
+ }
69
+
package/src/index.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+
3
+ export interface FlowlinkAuthProviderProps {
4
+ baseUrl: string;
5
+ publishableKey?: string;
6
+ children: React.ReactNode;
7
+ redirect?: string;
8
+ }
9
+
10
+ export declare function FlowlinkAuthProvider(props: FlowlinkAuthProviderProps): JSX.Element;
11
+ export declare function initFlowlinkAuth(opts: { publishableKey: string; baseUrl: string; opts?: any }): void;
12
+ export declare function SignIn(props?: any): JSX.Element;
13
+ export declare function SignUp(props?: any): JSX.Element;
14
+ export declare function useAuth(): any;
15
+
package/src/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { default as FlowlinkAuthProvider } from './provider'
2
+ export { default as SignIn } from './SignIn'
3
+ export { default as SignUp } from './SignUp'
4
+ export { useAuth } from './provider'
5
+