@reauth-dev/sdk 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/LICENSE +21 -0
- package/README.md +252 -0
- package/dist/chunk-JX2J36FS.mjs +269 -0
- package/dist/index.d.mts +127 -0
- package/dist/index.d.ts +127 -0
- package/dist/index.js +308 -0
- package/dist/index.mjs +17 -0
- package/dist/react/index.d.mts +123 -0
- package/dist/react/index.d.ts +123 -0
- package/dist/react/index.js +448 -0
- package/dist/react/index.mjs +154 -0
- package/dist/server.d.mts +188 -0
- package/dist/server.d.ts +188 -0
- package/dist/server.js +391 -0
- package/dist/server.mjs +356 -0
- package/dist/types-D8oOYbeC.d.mts +169 -0
- package/dist/types-D8oOYbeC.d.ts +169 -0
- package/dist/webhooks.d.mts +23 -0
- package/dist/webhooks.d.ts +23 -0
- package/dist/webhooks.js +123 -0
- package/dist/webhooks.mjs +94 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Emil Suleymanov <emil@esnx.xyz>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# @reauth-dev/sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for [reauth.dev](https://reauth.dev) - passwordless authentication for your apps.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @reauth-dev/sdk
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @reauth-dev/sdk
|
|
11
|
+
# or
|
|
12
|
+
yarn add @reauth-dev/sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
### React (Next.js)
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// app/layout.tsx
|
|
21
|
+
import { AuthProvider } from '@reauth-dev/sdk/react';
|
|
22
|
+
|
|
23
|
+
export default function RootLayout({ children }) {
|
|
24
|
+
return (
|
|
25
|
+
<AuthProvider config={{ domain: 'yourdomain.com' }}>
|
|
26
|
+
{children}
|
|
27
|
+
</AuthProvider>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// app/dashboard/page.tsx
|
|
32
|
+
import { ProtectedRoute, useAuthContext } from '@reauth-dev/sdk/react';
|
|
33
|
+
|
|
34
|
+
export default function Dashboard() {
|
|
35
|
+
return (
|
|
36
|
+
<ProtectedRoute>
|
|
37
|
+
<DashboardContent />
|
|
38
|
+
</ProtectedRoute>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function DashboardContent() {
|
|
43
|
+
const { user, logout } = useAuthContext();
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div>
|
|
47
|
+
<h1>Welcome, {user?.email}</h1>
|
|
48
|
+
<button onClick={logout}>Sign out</button>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Vanilla JavaScript
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { createReauthClient } from '@reauth-dev/sdk';
|
|
58
|
+
|
|
59
|
+
const reauth = createReauthClient({ domain: 'yourdomain.com' });
|
|
60
|
+
|
|
61
|
+
// Check if authenticated
|
|
62
|
+
const session = await reauth.getSession();
|
|
63
|
+
if (!session.valid) {
|
|
64
|
+
reauth.login(); // Redirect to login
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Log out
|
|
68
|
+
await reauth.logout();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Server-Side (API Routes)
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// Next.js API Route
|
|
75
|
+
import { createServerClient } from '@reauth-dev/sdk/server';
|
|
76
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
77
|
+
|
|
78
|
+
const reauth = createServerClient({ domain: 'yourdomain.com' });
|
|
79
|
+
|
|
80
|
+
export async function GET(request: NextRequest) {
|
|
81
|
+
const user = await reauth.getUser(request.headers.get('cookie') || '');
|
|
82
|
+
|
|
83
|
+
if (!user) {
|
|
84
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return NextResponse.json({ user });
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## How It Works
|
|
92
|
+
|
|
93
|
+
reauth.dev handles the entire login flow. Your app just needs to:
|
|
94
|
+
|
|
95
|
+
1. **Redirect to login** - Send users to `https://reauth.yourdomain.com`
|
|
96
|
+
2. **Check session** - Verify authentication via the session endpoint
|
|
97
|
+
3. **Handle logout** - Clear session cookies
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
User clicks "Sign in" → Redirect to reauth.yourdomain.com
|
|
101
|
+
→ reauth handles email + magic link → User authenticates
|
|
102
|
+
→ Redirect back to your app with cookies set
|
|
103
|
+
→ Your app checks /auth/session → User info returned
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## API Reference
|
|
107
|
+
|
|
108
|
+
### Browser Client
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { createReauthClient } from '@reauth-dev/sdk';
|
|
112
|
+
|
|
113
|
+
const reauth = createReauthClient({ domain: 'yourdomain.com' });
|
|
114
|
+
|
|
115
|
+
// Redirect to login page
|
|
116
|
+
reauth.login();
|
|
117
|
+
|
|
118
|
+
// Check authentication status
|
|
119
|
+
const session = await reauth.getSession();
|
|
120
|
+
// Returns: { valid, end_user_id, email, roles, waitlist_position, error, error_code }
|
|
121
|
+
|
|
122
|
+
// Refresh access token (when session.valid === false)
|
|
123
|
+
const success = await reauth.refresh();
|
|
124
|
+
|
|
125
|
+
// Log out
|
|
126
|
+
await reauth.logout();
|
|
127
|
+
|
|
128
|
+
// Delete account (self-service)
|
|
129
|
+
const deleted = await reauth.deleteAccount();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Server Client
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { createServerClient } from '@reauth-dev/sdk/server';
|
|
136
|
+
|
|
137
|
+
const reauth = createServerClient({ domain: 'yourdomain.com' });
|
|
138
|
+
|
|
139
|
+
// Get raw session data
|
|
140
|
+
const session = await reauth.getSession(cookieHeader);
|
|
141
|
+
|
|
142
|
+
// Get user object (or null if not authenticated)
|
|
143
|
+
const user = await reauth.getUser(cookieHeader);
|
|
144
|
+
// Returns: { id, email, roles } | null
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### React Components
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { AuthProvider, useAuthContext, useAuth, ProtectedRoute } from '@reauth-dev/sdk/react';
|
|
151
|
+
|
|
152
|
+
// AuthProvider - Wrap your app
|
|
153
|
+
<AuthProvider config={{ domain: 'yourdomain.com' }}>
|
|
154
|
+
{children}
|
|
155
|
+
</AuthProvider>
|
|
156
|
+
|
|
157
|
+
// useAuthContext - Access auth state
|
|
158
|
+
const { user, loading, error, isOnWaitlist, waitlistPosition, login, logout, refetch } = useAuthContext();
|
|
159
|
+
|
|
160
|
+
// useAuth - Standalone hook (without provider)
|
|
161
|
+
const auth = useAuth({ domain: 'yourdomain.com' });
|
|
162
|
+
|
|
163
|
+
// ProtectedRoute - Protect pages
|
|
164
|
+
<ProtectedRoute fallback={<Loading />} onWaitlist={() => router.push('/waitlist')}>
|
|
165
|
+
<ProtectedContent />
|
|
166
|
+
</ProtectedRoute>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Types
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
type ReauthSession = {
|
|
173
|
+
valid: boolean;
|
|
174
|
+
end_user_id: string | null;
|
|
175
|
+
email: string | null;
|
|
176
|
+
roles: string[] | null;
|
|
177
|
+
waitlist_position: number | null;
|
|
178
|
+
error: string | null;
|
|
179
|
+
error_code: 'ACCOUNT_SUSPENDED' | null;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
type User = {
|
|
183
|
+
id: string;
|
|
184
|
+
email: string;
|
|
185
|
+
roles: string[];
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
type ReauthConfig = {
|
|
189
|
+
domain: string;
|
|
190
|
+
};
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Handling Edge Cases
|
|
194
|
+
|
|
195
|
+
### Token Refresh
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
const session = await reauth.getSession();
|
|
199
|
+
|
|
200
|
+
if (!session.valid && !session.error_code) {
|
|
201
|
+
// Access token expired, try refresh
|
|
202
|
+
const refreshed = await reauth.refresh();
|
|
203
|
+
if (refreshed) {
|
|
204
|
+
const newSession = await reauth.getSession();
|
|
205
|
+
// Continue with newSession
|
|
206
|
+
} else {
|
|
207
|
+
// Refresh token also expired, redirect to login
|
|
208
|
+
reauth.login();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Waitlist
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
if (session.valid && session.waitlist_position) {
|
|
217
|
+
// User authenticated but on waitlist
|
|
218
|
+
// Show waitlist page with position
|
|
219
|
+
console.log(`Position: #${session.waitlist_position}`);
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Account Suspended
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
if (session.error_code === 'ACCOUNT_SUSPENDED') {
|
|
227
|
+
// Show suspended message
|
|
228
|
+
// User cannot access app until admin unfreezes
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Prerequisites
|
|
233
|
+
|
|
234
|
+
1. **Verify your domain** on [reauth.dev](https://reauth.dev)
|
|
235
|
+
2. **Set redirect URL** (where users go after login)
|
|
236
|
+
3. **Enable magic link auth** in domain settings
|
|
237
|
+
|
|
238
|
+
## Cookies
|
|
239
|
+
|
|
240
|
+
reauth.dev sets these cookies on `.yourdomain.com`:
|
|
241
|
+
|
|
242
|
+
| Cookie | Purpose | Expiry |
|
|
243
|
+
|--------|---------|--------|
|
|
244
|
+
| `end_user_access_token` | Auth token (HttpOnly) | 24 hours |
|
|
245
|
+
| `end_user_refresh_token` | Token renewal (HttpOnly) | 30 days |
|
|
246
|
+
| `end_user_email` | Display email | 30 days |
|
|
247
|
+
|
|
248
|
+
All cookies require HTTPS.
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
function createReauthClient(config) {
|
|
3
|
+
const { domain } = config;
|
|
4
|
+
const baseUrl = `https://reauth.${domain}/api/public`;
|
|
5
|
+
return {
|
|
6
|
+
/**
|
|
7
|
+
* Redirect the user to the reauth.dev login page.
|
|
8
|
+
* After successful login, they'll be redirected back to your configured redirect URL.
|
|
9
|
+
*/
|
|
10
|
+
login() {
|
|
11
|
+
if (typeof window === "undefined") {
|
|
12
|
+
throw new Error("login() can only be called in browser");
|
|
13
|
+
}
|
|
14
|
+
window.location.href = `https://reauth.${domain}/`;
|
|
15
|
+
},
|
|
16
|
+
/**
|
|
17
|
+
* Check if the user is authenticated.
|
|
18
|
+
* Returns session info including user ID, email, and roles.
|
|
19
|
+
*/
|
|
20
|
+
async getSession() {
|
|
21
|
+
const res = await fetch(`${baseUrl}/auth/session`, {
|
|
22
|
+
credentials: "include"
|
|
23
|
+
});
|
|
24
|
+
return res.json();
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* Refresh the access token using the refresh token.
|
|
28
|
+
* Call this when getSession() returns valid: false but no error_code.
|
|
29
|
+
* @returns true if refresh succeeded, false otherwise
|
|
30
|
+
*/
|
|
31
|
+
async refresh() {
|
|
32
|
+
const res = await fetch(`${baseUrl}/auth/refresh`, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
credentials: "include"
|
|
35
|
+
});
|
|
36
|
+
return res.ok;
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* Get an access token for Bearer authentication.
|
|
40
|
+
* Use this when calling your own API that uses local token verification.
|
|
41
|
+
*
|
|
42
|
+
* @returns TokenResponse with access token, or null if not authenticated
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const tokenResponse = await reauth.getToken();
|
|
47
|
+
* if (tokenResponse) {
|
|
48
|
+
* fetch('/api/data', {
|
|
49
|
+
* headers: { Authorization: `Bearer ${tokenResponse.accessToken}` }
|
|
50
|
+
* });
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
async getToken() {
|
|
55
|
+
try {
|
|
56
|
+
const res = await fetch(`${baseUrl}/auth/token`, {
|
|
57
|
+
method: "GET",
|
|
58
|
+
credentials: "include"
|
|
59
|
+
});
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
if (res.status === 401) return null;
|
|
62
|
+
throw new Error(`Failed to get token: ${res.status}`);
|
|
63
|
+
}
|
|
64
|
+
const data = await res.json();
|
|
65
|
+
return {
|
|
66
|
+
accessToken: data.access_token,
|
|
67
|
+
expiresIn: data.expires_in,
|
|
68
|
+
tokenType: data.token_type
|
|
69
|
+
};
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
/**
|
|
75
|
+
* Log out the user by clearing all session cookies.
|
|
76
|
+
*/
|
|
77
|
+
async logout() {
|
|
78
|
+
await fetch(`${baseUrl}/auth/logout`, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
credentials: "include"
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
/**
|
|
84
|
+
* Delete the user's own account (self-service).
|
|
85
|
+
* @returns true if deletion succeeded, false otherwise
|
|
86
|
+
*/
|
|
87
|
+
async deleteAccount() {
|
|
88
|
+
const res = await fetch(`${baseUrl}/auth/account`, {
|
|
89
|
+
method: "DELETE",
|
|
90
|
+
credentials: "include"
|
|
91
|
+
});
|
|
92
|
+
return res.ok;
|
|
93
|
+
},
|
|
94
|
+
// ========================================================================
|
|
95
|
+
// Billing Methods
|
|
96
|
+
// ========================================================================
|
|
97
|
+
/**
|
|
98
|
+
* Get available subscription plans for the domain.
|
|
99
|
+
* Only returns public plans sorted by display order.
|
|
100
|
+
*/
|
|
101
|
+
async getPlans() {
|
|
102
|
+
const res = await fetch(`${baseUrl}/billing/plans`, {
|
|
103
|
+
credentials: "include"
|
|
104
|
+
});
|
|
105
|
+
if (!res.ok) return [];
|
|
106
|
+
const data = await res.json();
|
|
107
|
+
return data.map(
|
|
108
|
+
(p) => ({
|
|
109
|
+
id: p.id,
|
|
110
|
+
code: p.code,
|
|
111
|
+
name: p.name,
|
|
112
|
+
description: p.description,
|
|
113
|
+
priceCents: p.price_cents,
|
|
114
|
+
currency: p.currency,
|
|
115
|
+
interval: p.interval,
|
|
116
|
+
intervalCount: p.interval_count,
|
|
117
|
+
trialDays: p.trial_days,
|
|
118
|
+
features: p.features,
|
|
119
|
+
displayOrder: p.display_order
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
},
|
|
123
|
+
/**
|
|
124
|
+
* Get the current user's subscription status.
|
|
125
|
+
*/
|
|
126
|
+
async getSubscription() {
|
|
127
|
+
const res = await fetch(`${baseUrl}/billing/subscription`, {
|
|
128
|
+
credentials: "include"
|
|
129
|
+
});
|
|
130
|
+
const data = await res.json();
|
|
131
|
+
return {
|
|
132
|
+
id: data.id,
|
|
133
|
+
planCode: data.plan_code,
|
|
134
|
+
planName: data.plan_name,
|
|
135
|
+
status: data.status,
|
|
136
|
+
currentPeriodEnd: data.current_period_end,
|
|
137
|
+
trialEnd: data.trial_end,
|
|
138
|
+
cancelAtPeriodEnd: data.cancel_at_period_end
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
/**
|
|
142
|
+
* Create a Stripe checkout session to subscribe to a plan.
|
|
143
|
+
* @param planCode The plan code to subscribe to
|
|
144
|
+
* @param successUrl URL to redirect to after successful payment
|
|
145
|
+
* @param cancelUrl URL to redirect to if checkout is canceled
|
|
146
|
+
* @returns Checkout session with URL to redirect the user to
|
|
147
|
+
*/
|
|
148
|
+
async createCheckout(planCode, successUrl, cancelUrl) {
|
|
149
|
+
const res = await fetch(`${baseUrl}/billing/checkout`, {
|
|
150
|
+
method: "POST",
|
|
151
|
+
headers: { "Content-Type": "application/json" },
|
|
152
|
+
credentials: "include",
|
|
153
|
+
body: JSON.stringify({
|
|
154
|
+
plan_code: planCode,
|
|
155
|
+
success_url: successUrl,
|
|
156
|
+
cancel_url: cancelUrl
|
|
157
|
+
})
|
|
158
|
+
});
|
|
159
|
+
if (!res.ok) {
|
|
160
|
+
const err = await res.json().catch(() => ({}));
|
|
161
|
+
throw new Error(err.message || "Failed to create checkout session");
|
|
162
|
+
}
|
|
163
|
+
const data = await res.json();
|
|
164
|
+
return { checkoutUrl: data.checkout_url };
|
|
165
|
+
},
|
|
166
|
+
/**
|
|
167
|
+
* Redirect user to subscribe to a plan.
|
|
168
|
+
* Creates a checkout session and redirects to Stripe.
|
|
169
|
+
* @param planCode The plan code to subscribe to
|
|
170
|
+
*/
|
|
171
|
+
async subscribe(planCode) {
|
|
172
|
+
if (typeof window === "undefined") {
|
|
173
|
+
throw new Error("subscribe() can only be called in browser");
|
|
174
|
+
}
|
|
175
|
+
const currentUrl = window.location.href;
|
|
176
|
+
const { checkoutUrl } = await this.createCheckout(
|
|
177
|
+
planCode,
|
|
178
|
+
currentUrl,
|
|
179
|
+
currentUrl
|
|
180
|
+
);
|
|
181
|
+
window.location.href = checkoutUrl;
|
|
182
|
+
},
|
|
183
|
+
/**
|
|
184
|
+
* Open the Stripe customer portal for managing subscription.
|
|
185
|
+
* @param returnUrl URL to return to after leaving the portal
|
|
186
|
+
*/
|
|
187
|
+
async openBillingPortal(returnUrl) {
|
|
188
|
+
if (typeof window === "undefined") {
|
|
189
|
+
throw new Error("openBillingPortal() can only be called in browser");
|
|
190
|
+
}
|
|
191
|
+
const res = await fetch(`${baseUrl}/billing/portal`, {
|
|
192
|
+
method: "POST",
|
|
193
|
+
headers: { "Content-Type": "application/json" },
|
|
194
|
+
credentials: "include",
|
|
195
|
+
body: JSON.stringify({
|
|
196
|
+
return_url: returnUrl || window.location.href
|
|
197
|
+
})
|
|
198
|
+
});
|
|
199
|
+
if (!res.ok) {
|
|
200
|
+
throw new Error("Failed to open billing portal");
|
|
201
|
+
}
|
|
202
|
+
const data = await res.json();
|
|
203
|
+
window.location.href = data.portal_url;
|
|
204
|
+
},
|
|
205
|
+
/**
|
|
206
|
+
* Cancel the user's subscription at period end.
|
|
207
|
+
* @returns true if cancellation succeeded
|
|
208
|
+
*/
|
|
209
|
+
async cancelSubscription() {
|
|
210
|
+
const res = await fetch(`${baseUrl}/billing/cancel`, {
|
|
211
|
+
method: "POST",
|
|
212
|
+
credentials: "include"
|
|
213
|
+
});
|
|
214
|
+
return res.ok;
|
|
215
|
+
},
|
|
216
|
+
// ========================================================================
|
|
217
|
+
// Balance Methods
|
|
218
|
+
// ========================================================================
|
|
219
|
+
/**
|
|
220
|
+
* Get the current user's balance.
|
|
221
|
+
* @returns Object with the current balance
|
|
222
|
+
*/
|
|
223
|
+
async getBalance() {
|
|
224
|
+
const res = await fetch(`${baseUrl}/balance`, {
|
|
225
|
+
credentials: "include"
|
|
226
|
+
});
|
|
227
|
+
if (!res.ok) {
|
|
228
|
+
if (res.status === 401) throw new Error("Not authenticated");
|
|
229
|
+
throw new Error(`Failed to get balance: ${res.status}`);
|
|
230
|
+
}
|
|
231
|
+
return res.json();
|
|
232
|
+
},
|
|
233
|
+
/**
|
|
234
|
+
* Get the current user's balance transaction history.
|
|
235
|
+
* @param opts - Optional pagination (limit, offset)
|
|
236
|
+
* @returns Object with array of transactions (newest first)
|
|
237
|
+
*/
|
|
238
|
+
async getTransactions(opts) {
|
|
239
|
+
const params = new URLSearchParams();
|
|
240
|
+
if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
|
|
241
|
+
if (opts?.offset !== void 0) params.set("offset", String(opts.offset));
|
|
242
|
+
const qs = params.toString();
|
|
243
|
+
const res = await fetch(
|
|
244
|
+
`${baseUrl}/balance/transactions${qs ? `?${qs}` : ""}`,
|
|
245
|
+
{ credentials: "include" }
|
|
246
|
+
);
|
|
247
|
+
if (!res.ok) {
|
|
248
|
+
if (res.status === 401) throw new Error("Not authenticated");
|
|
249
|
+
throw new Error(`Failed to get transactions: ${res.status}`);
|
|
250
|
+
}
|
|
251
|
+
const data = await res.json();
|
|
252
|
+
return {
|
|
253
|
+
transactions: data.transactions.map(
|
|
254
|
+
(t) => ({
|
|
255
|
+
id: t.id,
|
|
256
|
+
amountDelta: t.amount_delta,
|
|
257
|
+
reason: t.reason,
|
|
258
|
+
balanceAfter: t.balance_after,
|
|
259
|
+
createdAt: t.created_at
|
|
260
|
+
})
|
|
261
|
+
)
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export {
|
|
268
|
+
createReauthClient
|
|
269
|
+
};
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { R as ReauthConfig, a as ReauthSession, T as TokenResponse, S as SubscriptionPlan, U as UserSubscription, C as CheckoutSession, b as TransactionsPaginationOptions, B as BalanceTransaction } from './types-D8oOYbeC.mjs';
|
|
2
|
+
export { A as AuthResult, h as ChargeOptions, i as DepositOptions, D as DomainEndUserClaims, e as ReauthServerConfig, f as RequestLike, g as SubscriptionInfo, c as User, d as UserDetails } from './types-D8oOYbeC.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create a reauth client for browser-side authentication.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const reauth = createReauthClient({ domain: 'yourdomain.com' });
|
|
10
|
+
*
|
|
11
|
+
* // Check if user is authenticated
|
|
12
|
+
* const session = await reauth.getSession();
|
|
13
|
+
* if (!session.valid) {
|
|
14
|
+
* reauth.login(); // Redirect to login page
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* // Log out
|
|
18
|
+
* await reauth.logout();
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
declare function createReauthClient(config: ReauthConfig): {
|
|
22
|
+
/**
|
|
23
|
+
* Redirect the user to the reauth.dev login page.
|
|
24
|
+
* After successful login, they'll be redirected back to your configured redirect URL.
|
|
25
|
+
*/
|
|
26
|
+
login(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Check if the user is authenticated.
|
|
29
|
+
* Returns session info including user ID, email, and roles.
|
|
30
|
+
*/
|
|
31
|
+
getSession(): Promise<ReauthSession>;
|
|
32
|
+
/**
|
|
33
|
+
* Refresh the access token using the refresh token.
|
|
34
|
+
* Call this when getSession() returns valid: false but no error_code.
|
|
35
|
+
* @returns true if refresh succeeded, false otherwise
|
|
36
|
+
*/
|
|
37
|
+
refresh(): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Get an access token for Bearer authentication.
|
|
40
|
+
* Use this when calling your own API that uses local token verification.
|
|
41
|
+
*
|
|
42
|
+
* @returns TokenResponse with access token, or null if not authenticated
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const tokenResponse = await reauth.getToken();
|
|
47
|
+
* if (tokenResponse) {
|
|
48
|
+
* fetch('/api/data', {
|
|
49
|
+
* headers: { Authorization: `Bearer ${tokenResponse.accessToken}` }
|
|
50
|
+
* });
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
getToken(): Promise<TokenResponse | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Log out the user by clearing all session cookies.
|
|
57
|
+
*/
|
|
58
|
+
logout(): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Delete the user's own account (self-service).
|
|
61
|
+
* @returns true if deletion succeeded, false otherwise
|
|
62
|
+
*/
|
|
63
|
+
deleteAccount(): Promise<boolean>;
|
|
64
|
+
/**
|
|
65
|
+
* Get available subscription plans for the domain.
|
|
66
|
+
* Only returns public plans sorted by display order.
|
|
67
|
+
*/
|
|
68
|
+
getPlans(): Promise<SubscriptionPlan[]>;
|
|
69
|
+
/**
|
|
70
|
+
* Get the current user's subscription status.
|
|
71
|
+
*/
|
|
72
|
+
getSubscription(): Promise<UserSubscription>;
|
|
73
|
+
/**
|
|
74
|
+
* Create a Stripe checkout session to subscribe to a plan.
|
|
75
|
+
* @param planCode The plan code to subscribe to
|
|
76
|
+
* @param successUrl URL to redirect to after successful payment
|
|
77
|
+
* @param cancelUrl URL to redirect to if checkout is canceled
|
|
78
|
+
* @returns Checkout session with URL to redirect the user to
|
|
79
|
+
*/
|
|
80
|
+
createCheckout(planCode: string, successUrl: string, cancelUrl: string): Promise<CheckoutSession>;
|
|
81
|
+
/**
|
|
82
|
+
* Redirect user to subscribe to a plan.
|
|
83
|
+
* Creates a checkout session and redirects to Stripe.
|
|
84
|
+
* @param planCode The plan code to subscribe to
|
|
85
|
+
*/
|
|
86
|
+
subscribe(planCode: string): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Open the Stripe customer portal for managing subscription.
|
|
89
|
+
* @param returnUrl URL to return to after leaving the portal
|
|
90
|
+
*/
|
|
91
|
+
openBillingPortal(returnUrl?: string): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Cancel the user's subscription at period end.
|
|
94
|
+
* @returns true if cancellation succeeded
|
|
95
|
+
*/
|
|
96
|
+
cancelSubscription(): Promise<boolean>;
|
|
97
|
+
/**
|
|
98
|
+
* Get the current user's balance.
|
|
99
|
+
* @returns Object with the current balance
|
|
100
|
+
*/
|
|
101
|
+
getBalance(): Promise<{
|
|
102
|
+
balance: number;
|
|
103
|
+
}>;
|
|
104
|
+
/**
|
|
105
|
+
* Get the current user's balance transaction history.
|
|
106
|
+
* @param opts - Optional pagination (limit, offset)
|
|
107
|
+
* @returns Object with array of transactions (newest first)
|
|
108
|
+
*/
|
|
109
|
+
getTransactions(opts?: TransactionsPaginationOptions): Promise<{
|
|
110
|
+
transactions: BalanceTransaction[];
|
|
111
|
+
}>;
|
|
112
|
+
};
|
|
113
|
+
type ReauthClient = ReturnType<typeof createReauthClient>;
|
|
114
|
+
|
|
115
|
+
declare enum ReauthErrorCode {
|
|
116
|
+
/**
|
|
117
|
+
* OAuth retry window expired. The OAuth flow must be restarted.
|
|
118
|
+
*/
|
|
119
|
+
OAUTH_RETRY_EXPIRED = "OAUTH_RETRY_EXPIRED"
|
|
120
|
+
}
|
|
121
|
+
type ReauthError = {
|
|
122
|
+
code: ReauthErrorCode | string;
|
|
123
|
+
message?: string;
|
|
124
|
+
};
|
|
125
|
+
declare function requiresOAuthRestart(error: ReauthError | null | undefined): boolean;
|
|
126
|
+
|
|
127
|
+
export { BalanceTransaction, type ReauthClient, ReauthConfig, type ReauthError, ReauthErrorCode, ReauthSession, TokenResponse, TransactionsPaginationOptions, createReauthClient, requiresOAuthRestart };
|